138494Sobrien/*
2174294Sobrien * Copyright (c) 1997-2006 Erez Zadok
338494Sobrien * Copyright (c) 1990 Jan-Simon Pendry
438494Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
538494Sobrien * Copyright (c) 1990 The Regents of the University of California.
638494Sobrien * All rights reserved.
738494Sobrien *
838494Sobrien * This code is derived from software contributed to Berkeley by
938494Sobrien * Jan-Simon Pendry at Imperial College, London.
1038494Sobrien *
1138494Sobrien * Redistribution and use in source and binary forms, with or without
1238494Sobrien * modification, are permitted provided that the following conditions
1338494Sobrien * are met:
1438494Sobrien * 1. Redistributions of source code must retain the above copyright
1538494Sobrien *    notice, this list of conditions and the following disclaimer.
1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1738494Sobrien *    notice, this list of conditions and the following disclaimer in the
1838494Sobrien *    documentation and/or other materials provided with the distribution.
1938494Sobrien * 3. All advertising materials mentioning features or use of this software
2042629Sobrien *    must display the following acknowledgment:
2138494Sobrien *      This product includes software developed by the University of
2238494Sobrien *      California, Berkeley and its contributors.
2338494Sobrien * 4. Neither the name of the University nor the names of its contributors
2438494Sobrien *    may be used to endorse or promote products derived from this software
2538494Sobrien *    without specific prior written permission.
2638494Sobrien *
2738494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2838494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2938494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3038494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3138494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3238494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3338494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3438494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3538494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3638494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3738494Sobrien * SUCH DAMAGE.
3838494Sobrien *
3938494Sobrien *
40174294Sobrien * File: am-utils/amd/amfs_nfsx.c
4138494Sobrien *
4238494Sobrien */
4338494Sobrien
4438494Sobrien/*
4538494Sobrien * NFS hierarchical mounts
4638494Sobrien *
4738494Sobrien * TODO: Re-implement.
4838494Sobrien */
4938494Sobrien
5038494Sobrien#ifdef HAVE_CONFIG_H
5138494Sobrien# include <config.h>
5238494Sobrien#endif /* HAVE_CONFIG_H */
5338494Sobrien#include <am_defs.h>
5438494Sobrien#include <amd.h>
5538494Sobrien
5638494Sobrien/*
5738494Sobrien * The rfs field contains a list of mounts to be done from
5838494Sobrien * the remote host.
5938494Sobrien */
6038494Sobrientypedef struct amfs_nfsx_mnt {
6138494Sobrien  mntfs *n_mnt;
6238494Sobrien  int n_error;
6338494Sobrien} amfs_nfsx_mnt;
6438494Sobrien
6538494Sobrienstruct amfs_nfsx {
6638494Sobrien  int nx_c;			/* Number of elements in nx_v */
6738494Sobrien  amfs_nfsx_mnt *nx_v;		/* Underlying mounts */
6838494Sobrien  amfs_nfsx_mnt *nx_try;
69174294Sobrien  am_node *nx_mp;
7038494Sobrien};
7138494Sobrien
7238494Sobrien/* forward definitions */
7338494Sobrienstatic char *amfs_nfsx_match(am_opts *fo);
74174294Sobrienstatic int amfs_nfsx_mount(am_node *am, mntfs *mf);
75174294Sobrienstatic int amfs_nfsx_umount(am_node *am, mntfs *mf);
7638494Sobrienstatic int amfs_nfsx_init(mntfs *mf);
7738494Sobrien
7838494Sobrien/*
7938494Sobrien * Ops structure
8038494Sobrien */
8138494Sobrienam_ops amfs_nfsx_ops =
8238494Sobrien{
8338494Sobrien  "nfsx",
8438494Sobrien  amfs_nfsx_match,
8538494Sobrien  amfs_nfsx_init,
86174294Sobrien  amfs_nfsx_mount,
87174294Sobrien  amfs_nfsx_umount,
88174294Sobrien  amfs_error_lookup_child,
89174294Sobrien  amfs_error_mount_child,
9038494Sobrien  amfs_error_readdir,
9138494Sobrien  0,				/* amfs_nfsx_readlink */
9238494Sobrien  0,				/* amfs_nfsx_mounted */
9338494Sobrien  0,				/* amfs_nfsx_umounted */
9438494Sobrien  find_nfs_srvr,		/* XXX */
95174294Sobrien  0,				/* amfs_nfsx_get_wchan */
96174294Sobrien  /* FS_UBACKGROUND| */ FS_AMQINFO,	/* nfs_fs_flags */
97174294Sobrien#ifdef HAVE_FS_AUTOFS
98174294Sobrien  AUTOFS_NFSX_FS_FLAGS,
99174294Sobrien#endif /* HAVE_FS_AUTOFS */
10038494Sobrien};
10138494Sobrien
10238494Sobrien
10338494Sobrienstatic char *
10438494Sobrienamfs_nfsx_match(am_opts *fo)
10538494Sobrien{
10638494Sobrien  char *xmtab;
10738494Sobrien  char *ptr;
10838494Sobrien  int len;
10938494Sobrien
11038494Sobrien  if (!fo->opt_rfs) {
11138494Sobrien    plog(XLOG_USER, "amfs_nfsx: no remote filesystem specified");
11238494Sobrien    return FALSE;
11338494Sobrien  }
11438494Sobrien
11538494Sobrien  if (!fo->opt_rhost) {
11638494Sobrien    plog(XLOG_USER, "amfs_nfsx: no remote host specified");
11738494Sobrien    return FALSE;
11838494Sobrien  }
11938494Sobrien
12038494Sobrien  /* set default sublink */
12138494Sobrien  if (fo->opt_sublink == 0) {
12238494Sobrien    ptr = strchr(fo->opt_rfs, ',');
123174294Sobrien    if (ptr && ptr > (fo->opt_rfs + 1))
12438494Sobrien      fo->opt_sublink = strnsave(fo->opt_rfs + 1, ptr - fo->opt_rfs - 1);
12538494Sobrien  }
12638494Sobrien
12738494Sobrien  /*
12838494Sobrien   * Remove trailing ",..." from ${fs}
12938494Sobrien   * After deslashifying, overwrite the end of ${fs} with "/"
13038494Sobrien   * to make sure it is unique.
13138494Sobrien   */
13238494Sobrien  if ((ptr = strchr(fo->opt_fs, ',')))
13338494Sobrien    *ptr = '\0';
13438494Sobrien  deslashify(fo->opt_fs);
13538494Sobrien
13638494Sobrien  /*
13738494Sobrien   * Bump string length to allow trailing /
13838494Sobrien   */
13938494Sobrien  len = strlen(fo->opt_fs);
14038494Sobrien  fo->opt_fs = xrealloc(fo->opt_fs, len + 1 + 1);
14138494Sobrien  ptr = fo->opt_fs + len;
14238494Sobrien
14338494Sobrien  /*
14438494Sobrien   * Make unique...
14538494Sobrien   */
14638494Sobrien  *ptr++ = '/';
14738494Sobrien  *ptr = '\0';
14838494Sobrien
14938494Sobrien  /*
15038494Sobrien   * Determine magic cookie to put in mtab
15138494Sobrien   */
15238494Sobrien  xmtab = str3cat((char *) 0, fo->opt_rhost, ":", fo->opt_rfs);
15382794Sobrien  dlog("NFSX: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
15438494Sobrien       fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
15538494Sobrien
15638494Sobrien  return xmtab;
15738494Sobrien}
15838494Sobrien
15938494Sobrien
16038494Sobrienstatic void
161174294Sobrienamfs_nfsx_prfree(opaque_t vp)
16238494Sobrien{
16338494Sobrien  struct amfs_nfsx *nx = (struct amfs_nfsx *) vp;
16438494Sobrien  int i;
16538494Sobrien
16638494Sobrien  for (i = 0; i < nx->nx_c; i++) {
16738494Sobrien    mntfs *m = nx->nx_v[i].n_mnt;
16838494Sobrien    if (m)
16938494Sobrien      free_mntfs(m);
17038494Sobrien  }
17138494Sobrien
17238494Sobrien  XFREE(nx->nx_v);
17338494Sobrien  XFREE(nx);
17438494Sobrien}
17538494Sobrien
17638494Sobrien
17738494Sobrienstatic int
17838494Sobrienamfs_nfsx_init(mntfs *mf)
17938494Sobrien{
18038494Sobrien  /*
18138494Sobrien   * mf_info has the form:
18238494Sobrien   *   host:/prefix/path,sub,sub,sub
18338494Sobrien   */
18438494Sobrien  int i;
18538494Sobrien  int glob_error;
18638494Sobrien  struct amfs_nfsx *nx;
18738494Sobrien  int asked_for_wakeup = 0;
18838494Sobrien
18938494Sobrien  nx = (struct amfs_nfsx *) mf->mf_private;
19038494Sobrien
19138494Sobrien  if (nx == 0) {
19238494Sobrien    char **ivec;
19338494Sobrien    char *info = 0;
19438494Sobrien    char *host;
19538494Sobrien    char *pref;
19638494Sobrien    int error = 0;
19738494Sobrien
19838494Sobrien    info = strdup(mf->mf_info);
19938494Sobrien    host = strchr(info, ':');
20038494Sobrien    if (!host) {
20138494Sobrien      error = EINVAL;
20238494Sobrien      goto errexit;
20338494Sobrien    }
204174294Sobrien    pref = host + 1;
20538494Sobrien    host = info;
20638494Sobrien
20738494Sobrien    /*
20838494Sobrien     * Split the prefix off from the suffices
20938494Sobrien     */
21038494Sobrien    ivec = strsplit(pref, ',', '\'');
21138494Sobrien
21238494Sobrien    /*
21338494Sobrien     * Count array size
21438494Sobrien     */
215174294Sobrien    for (i = 0; ivec[i]; i++)
216174294Sobrien      /* nothing */;
21738494Sobrien
21838494Sobrien    nx = ALLOC(struct amfs_nfsx);
219174294Sobrien    mf->mf_private = (opaque_t) nx;
22038494Sobrien    mf->mf_prfree = amfs_nfsx_prfree;
22138494Sobrien
22238494Sobrien    nx->nx_c = i - 1;		/* i-1 because we don't want the prefix */
22338494Sobrien    nx->nx_v = (amfs_nfsx_mnt *) xmalloc(nx->nx_c * sizeof(amfs_nfsx_mnt));
224174294Sobrien    nx->nx_mp = 0;
22538494Sobrien    {
22638494Sobrien      char *mp = 0;
22738494Sobrien      char *xinfo = 0;
22838494Sobrien      char *fs = mf->mf_fo->opt_fs;
22938494Sobrien      char *rfs = 0;
23038494Sobrien      for (i = 0; i < nx->nx_c; i++) {
23138494Sobrien	char *path = ivec[i + 1];
23238494Sobrien	rfs = str3cat(rfs, pref, "/", path);
23338494Sobrien	/*
23438494Sobrien	 * Determine the mount point.
23538494Sobrien	 * If this is the root, then don't remove
23638494Sobrien	 * the trailing slash to avoid mntfs name clashes.
23738494Sobrien	 */
23838494Sobrien	mp = str3cat(mp, fs, "/", rfs);
23938494Sobrien	normalize_slash(mp);
24038494Sobrien	deslashify(mp);
24138494Sobrien	/*
24238494Sobrien	 * Determine the mount info
24338494Sobrien	 */
24438494Sobrien	xinfo = str3cat(xinfo, host, *path == '/' ? "" : "/", path);
24538494Sobrien	normalize_slash(xinfo);
24638494Sobrien	if (pref[1] != '\0')
24738494Sobrien	  deslashify(xinfo);
24838494Sobrien	dlog("amfs_nfsx: init mount for %s on %s", xinfo, mp);
24938494Sobrien	nx->nx_v[i].n_error = -1;
25038494Sobrien	nx->nx_v[i].n_mnt = find_mntfs(&nfs_ops, mf->mf_fo, mp, xinfo, "", mf->mf_mopts, mf->mf_remopts);
251174294Sobrien	/* propagate the on_autofs flag */
252174294Sobrien	nx->nx_v[i].n_mnt->mf_flags |= mf->mf_flags & MFF_ON_AUTOFS;
25338494Sobrien      }
25438494Sobrien      if (rfs)
25538494Sobrien	XFREE(rfs);
25638494Sobrien      if (mp)
25738494Sobrien	XFREE(mp);
25838494Sobrien      if (xinfo)
25938494Sobrien	XFREE(xinfo);
26038494Sobrien    }
26138494Sobrien
26238494Sobrien    XFREE(ivec);
26338494Sobrien  errexit:
26438494Sobrien    if (info)
26538494Sobrien      XFREE(info);
26638494Sobrien    if (error)
26738494Sobrien      return error;
26838494Sobrien  }
26938494Sobrien
27038494Sobrien  /*
27138494Sobrien   * Iterate through the mntfs's and call
27238494Sobrien   * the underlying init routine on each
27338494Sobrien   */
27438494Sobrien  glob_error = 0;
27538494Sobrien
27638494Sobrien  for (i = 0; i < nx->nx_c; i++) {
27738494Sobrien    amfs_nfsx_mnt *n = &nx->nx_v[i];
27838494Sobrien    mntfs *m = n->n_mnt;
279174294Sobrien    int error = 0;
280174294Sobrien    if (m->mf_ops->fs_init && !(mf->mf_flags & MFF_RESTART))
281174294Sobrien      error = m->mf_ops->fs_init(m);
28238494Sobrien    /*
28338494Sobrien     * if you just "return error" here, you will have made a failure
28438494Sobrien     * in any submounts to fail the whole group.  There was old unused code
28538494Sobrien     * here before.
28638494Sobrien     */
28738494Sobrien    if (error > 0)
28838494Sobrien      n->n_error = error;
28938494Sobrien
29038494Sobrien    else if (error < 0) {
29138494Sobrien      glob_error = -1;
29238494Sobrien      if (!asked_for_wakeup) {
29338494Sobrien	asked_for_wakeup = 1;
294174294Sobrien	sched_task(wakeup_task, (opaque_t) mf, get_mntfs_wchan(m));
29538494Sobrien      }
29638494Sobrien    }
29738494Sobrien  }
29838494Sobrien
29938494Sobrien  return glob_error;
30038494Sobrien}
30138494Sobrien
30238494Sobrien
30338494Sobrienstatic void
304174294Sobrienamfs_nfsx_cont(int rc, int term, opaque_t arg)
30538494Sobrien{
306174294Sobrien  mntfs *mf = (mntfs *) arg;
30738494Sobrien  struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
308174294Sobrien  am_node *mp = nx->nx_mp;
30938494Sobrien  amfs_nfsx_mnt *n = nx->nx_try;
31038494Sobrien
31138494Sobrien  n->n_mnt->mf_flags &= ~(MFF_ERROR | MFF_MOUNTING);
31238494Sobrien  mf->mf_flags &= ~MFF_ERROR;
31338494Sobrien
31438494Sobrien  /*
31538494Sobrien   * Wakeup anything waiting for this mount
31638494Sobrien   */
317174294Sobrien  wakeup(get_mntfs_wchan(n->n_mnt));
31838494Sobrien
31938494Sobrien  if (rc || term) {
32038494Sobrien    if (term) {
32138494Sobrien      /*
32238494Sobrien       * Not sure what to do for an error code.
32338494Sobrien       */
32438494Sobrien      plog(XLOG_ERROR, "mount for %s got signal %d", n->n_mnt->mf_mount, term);
32538494Sobrien      n->n_error = EIO;
32638494Sobrien    } else {
32738494Sobrien      /*
32838494Sobrien       * Check for exit status
32938494Sobrien       */
33038494Sobrien      errno = rc;		/* XXX */
33138494Sobrien      plog(XLOG_ERROR, "%s: mount (amfs_nfsx_cont): %m", n->n_mnt->mf_mount);
33238494Sobrien      n->n_error = rc;
33338494Sobrien    }
33438494Sobrien    free_mntfs(n->n_mnt);
33538494Sobrien    n->n_mnt = new_mntfs();
33638494Sobrien    n->n_mnt->mf_error = n->n_error;
33738494Sobrien    n->n_mnt->mf_flags |= MFF_ERROR;
33838494Sobrien  } else {
33938494Sobrien    /*
34038494Sobrien     * The mount worked.
34138494Sobrien     */
342174294Sobrien    mf_mounted(n->n_mnt, FALSE); /* FALSE => don't free the n_mnt->am_opts */
34338494Sobrien    n->n_error = 0;
34438494Sobrien  }
34538494Sobrien
34638494Sobrien  /*
34738494Sobrien   * Do the remaining bits
34838494Sobrien   */
349174294Sobrien  if (amfs_nfsx_mount(mp, mf) >= 0)
350174294Sobrien    wakeup(get_mntfs_wchan(mf));
35138494Sobrien}
35238494Sobrien
35338494Sobrien
35438494Sobrienstatic int
355174294Sobrientry_amfs_nfsx_mount(opaque_t mv)
35638494Sobrien{
35738494Sobrien  mntfs *mf = (mntfs *) mv;
358174294Sobrien  struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
359174294Sobrien  am_node *mp = nx->nx_mp;
36038494Sobrien  int error;
36138494Sobrien
362174294Sobrien  error = mf->mf_ops->mount_fs(mp, mf);
36338494Sobrien
36438494Sobrien  return error;
36538494Sobrien}
36638494Sobrien
36738494Sobrien
36838494Sobrienstatic int
369174294Sobrienamfs_nfsx_remount(am_node *am, mntfs *mf, int fg)
37038494Sobrien{
37138494Sobrien  struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
37238494Sobrien  amfs_nfsx_mnt *n;
37338494Sobrien  int glob_error = -1;
37438494Sobrien
375174294Sobrien  /* Save the am_node pointer for later use */
376174294Sobrien  nx->nx_mp = am;
37738494Sobrien
37838494Sobrien  /*
37938494Sobrien   * Iterate through the mntfs's and mount each filesystem
38038494Sobrien   * which is not yet mounted.
38138494Sobrien   */
38238494Sobrien  for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
38338494Sobrien    mntfs *m = n->n_mnt;
384174294Sobrien
385174294Sobrien    if (m->mf_flags & MFF_MOUNTING)
386174294Sobrien      break;
387174294Sobrien
388174294Sobrien    if (m->mf_flags & MFF_MOUNTED) {
389174294Sobrien      mf_mounted(m, FALSE);	/* FALSE => don't free the m->am_opts */
390174294Sobrien      n->n_error = glob_error = 0;
391174294Sobrien      continue;
392174294Sobrien    }
393174294Sobrien
39438494Sobrien    if (n->n_error < 0) {
395174294Sobrien      /* Create the mountpoint, if and as required */
396174294Sobrien      if (!(m->mf_flags & MFF_MKMNT) && m->mf_fsflags & FS_MKMNT) {
397174294Sobrien	if (!mkdirs(m->mf_mount, 0555))
398174294Sobrien	  m->mf_flags |= MFF_MKMNT;
39938494Sobrien      }
40038494Sobrien
401174294Sobrien      dlog("calling underlying mount on %s", m->mf_mount);
402174294Sobrien      if (!fg && foreground && (m->mf_fsflags & FS_MBACKGROUND)) {
403174294Sobrien	m->mf_flags |= MFF_MOUNTING;
404174294Sobrien	dlog("backgrounding mount of \"%s\"", m->mf_info);
405174294Sobrien	nx->nx_try = n;
406174294Sobrien	run_task(try_amfs_nfsx_mount, (opaque_t) m, amfs_nfsx_cont, (opaque_t) mf);
407174294Sobrien	n->n_error = -1;
408174294Sobrien	return -1;
409174294Sobrien      } else {
410174294Sobrien	dlog("foreground mount of \"%s\" ...", mf->mf_info);
411174294Sobrien	n->n_error = m->mf_ops->mount_fs(am, m);
41238494Sobrien      }
41338494Sobrien
414174294Sobrien      if (n->n_error > 0)
415174294Sobrien	dlog("underlying fmount of %s failed: %s", m->mf_mount, strerror(n->n_error));
416174294Sobrien
41738494Sobrien      if (n->n_error == 0) {
41838494Sobrien	glob_error = 0;
41938494Sobrien      } else if (glob_error < 0) {
42038494Sobrien	glob_error = n->n_error;
42138494Sobrien      }
42238494Sobrien    }
42338494Sobrien  }
42438494Sobrien
42538494Sobrien  return glob_error < 0 ? 0 : glob_error;
42638494Sobrien}
42738494Sobrien
42838494Sobrien
42938494Sobrienstatic int
430174294Sobrienamfs_nfsx_mount(am_node *am, mntfs *mf)
43138494Sobrien{
432174294Sobrien  return amfs_nfsx_remount(am, mf, FALSE);
43338494Sobrien}
43438494Sobrien
43538494Sobrien
43638494Sobrien/*
43738494Sobrien * Unmount an NFS hierarchy.
43838494Sobrien * Note that this is called in the foreground
43938494Sobrien * and so may hang under extremely rare conditions.
44038494Sobrien */
44138494Sobrienstatic int
442174294Sobrienamfs_nfsx_umount(am_node *am, mntfs *mf)
44338494Sobrien{
44438494Sobrien  struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
44538494Sobrien  amfs_nfsx_mnt *n;
44638494Sobrien  int glob_error = 0;
44738494Sobrien
44838494Sobrien  /*
44938494Sobrien   * Iterate in reverse through the mntfs's and unmount each filesystem
45038494Sobrien   * which is mounted.
45138494Sobrien   */
45238494Sobrien  for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) {
45338494Sobrien    mntfs *m = n->n_mnt;
45438494Sobrien    /*
45538494Sobrien     * If this node has not been messed with
45638494Sobrien     * and there has been no error so far
45738494Sobrien     * then try and unmount.
45842629Sobrien     * If an error had occurred then zero
45938494Sobrien     * the error code so that the remount
46038494Sobrien     * only tries to unmount those nodes
46138494Sobrien     * which had been successfully unmounted.
46238494Sobrien     */
46338494Sobrien    if (n->n_error == 0) {
46438494Sobrien      dlog("calling underlying fumount on %s", m->mf_mount);
465174294Sobrien      n->n_error = m->mf_ops->umount_fs(am, m);
46638494Sobrien      if (n->n_error) {
46738494Sobrien	glob_error = n->n_error;
46838494Sobrien	n->n_error = 0;
46938494Sobrien      } else {
47038494Sobrien	/*
47138494Sobrien	 * Make sure remount gets this node
47238494Sobrien	 */
47338494Sobrien	n->n_error = -1;
47438494Sobrien      }
47538494Sobrien    }
47638494Sobrien  }
47738494Sobrien
47838494Sobrien  /*
47938494Sobrien   * If any unmounts failed then remount the
48038494Sobrien   * whole lot...
48138494Sobrien   */
48238494Sobrien  if (glob_error) {
483174294Sobrien    glob_error = amfs_nfsx_remount(am, mf, TRUE);
48438494Sobrien    if (glob_error) {
48538494Sobrien      errno = glob_error;	/* XXX */
48638494Sobrien      plog(XLOG_USER, "amfs_nfsx: remount of %s failed: %m", mf->mf_mount);
48738494Sobrien    }
48838494Sobrien    glob_error = EBUSY;
48938494Sobrien  } else {
49038494Sobrien    /*
49138494Sobrien     * Remove all the mount points
49238494Sobrien     */
49338494Sobrien    for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
49438494Sobrien      mntfs *m = n->n_mnt;
49538494Sobrien      dlog("calling underlying umounted on %s", m->mf_mount);
496174294Sobrien      if (m->mf_ops->umounted)
497174294Sobrien	m->mf_ops->umounted(m);
49838494Sobrien
49938494Sobrien      if (n->n_error < 0) {
500174294Sobrien	if (m->mf_fsflags & FS_MKMNT) {
50138494Sobrien	  (void) rmdirs(m->mf_mount);
50238494Sobrien	  m->mf_flags &= ~MFF_MKMNT;
50338494Sobrien	}
50438494Sobrien      }
50538494Sobrien      free_mntfs(m);
50638494Sobrien      n->n_mnt = 0;
50738494Sobrien      n->n_error = -1;
50838494Sobrien    }
50938494Sobrien  }
51038494Sobrien
51138494Sobrien  return glob_error;
51238494Sobrien}
513