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