/* $NetBSD: amfs_nfsx.c,v 1.1.1.3 2015/01/17 16:34:15 christos Exp $ */ /* * Copyright (c) 1997-2014 Erez Zadok * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry at Imperial College, London. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * * File: am-utils/amd/amfs_nfsx.c * */ /* * NFS hierarchical mounts * * TODO: Re-implement. */ #ifdef HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #include #include /* * The rfs field contains a list of mounts to be done from * the remote host. */ typedef struct amfs_nfsx_mnt { mntfs *n_mnt; int n_error; } amfs_nfsx_mnt; struct amfs_nfsx { int nx_c; /* Number of elements in nx_v */ amfs_nfsx_mnt *nx_v; /* Underlying mounts */ amfs_nfsx_mnt *nx_try; am_node *nx_mp; }; /* forward definitions */ static char *amfs_nfsx_match(am_opts *fo); static int amfs_nfsx_mount(am_node *am, mntfs *mf); static int amfs_nfsx_umount(am_node *am, mntfs *mf); static int amfs_nfsx_init(mntfs *mf); /* * Ops structure */ am_ops amfs_nfsx_ops = { "nfsx", amfs_nfsx_match, amfs_nfsx_init, amfs_nfsx_mount, amfs_nfsx_umount, amfs_error_lookup_child, amfs_error_mount_child, amfs_error_readdir, 0, /* amfs_nfsx_readlink */ 0, /* amfs_nfsx_mounted */ 0, /* amfs_nfsx_umounted */ find_nfs_srvr, /* XXX */ 0, /* amfs_nfsx_get_wchan */ /* FS_UBACKGROUND| */ FS_AMQINFO, /* nfs_fs_flags */ #ifdef HAVE_FS_AUTOFS AUTOFS_NFSX_FS_FLAGS, #endif /* HAVE_FS_AUTOFS */ }; static char * amfs_nfsx_match(am_opts *fo) { char *xmtab; char *ptr; int len; if (!fo->opt_rfs) { plog(XLOG_USER, "amfs_nfsx: no remote filesystem specified"); return FALSE; } if (!fo->opt_rhost) { plog(XLOG_USER, "amfs_nfsx: no remote host specified"); return FALSE; } /* set default sublink */ if (fo->opt_sublink == NULL || fo->opt_sublink[0] == '\0') { ptr = strchr(fo->opt_rfs, ','); if (ptr && ptr > (fo->opt_rfs + 1)) fo->opt_sublink = strnsave(fo->opt_rfs + 1, ptr - fo->opt_rfs - 1); } /* * Remove trailing ",..." from ${fs} * After deslashifying, overwrite the end of ${fs} with "/" * to make sure it is unique. */ if ((ptr = strchr(fo->opt_fs, ','))) *ptr = '\0'; deslashify(fo->opt_fs); /* * Bump string length to allow trailing / */ len = strlen(fo->opt_fs); fo->opt_fs = xrealloc(fo->opt_fs, len + 1 + 1); ptr = fo->opt_fs + len; /* * Make unique... */ *ptr++ = '/'; *ptr = '\0'; /* * Determine magic cookie to put in mtab */ xmtab = str3cat((char *) NULL, fo->opt_rhost, ":", fo->opt_rfs); dlog("NFSX: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"", fo->opt_rhost, fo->opt_rfs, fo->opt_fs); return xmtab; } static void amfs_nfsx_prfree(opaque_t vp) { struct amfs_nfsx *nx = (struct amfs_nfsx *) vp; int i; for (i = 0; i < nx->nx_c; i++) { mntfs *m = nx->nx_v[i].n_mnt; if (m) free_mntfs(m); } XFREE(nx->nx_v); XFREE(nx); } static int amfs_nfsx_init(mntfs *mf) { /* * mf_info has the form: * host:/prefix/path,sub,sub,sub */ int i; int glob_error; struct amfs_nfsx *nx; int asked_for_wakeup = 0; nx = (struct amfs_nfsx *) mf->mf_private; if (nx == 0) { char **ivec; char *info = NULL; char *host; char *pref; int error = 0; info = xstrdup(mf->mf_info); if (info == NULL) return errno; host = strchr(info, ':'); if (!host) { error = EINVAL; goto errexit; } pref = host + 1; host = info; /* * Split the prefix off from the suffices */ ivec = strsplit(pref, ',', '\''); /* * Count array size */ for (i = 0; ivec[i]; i++) /* nothing */; nx = ALLOC(struct amfs_nfsx); mf->mf_private = (opaque_t) nx; mf->mf_prfree = amfs_nfsx_prfree; nx->nx_c = i - 1; /* i-1 because we don't want the prefix */ nx->nx_v = (amfs_nfsx_mnt *) xmalloc(nx->nx_c * sizeof(amfs_nfsx_mnt)); nx->nx_mp = NULL; { char *mp = NULL; char *xinfo = NULL; char *fs = mf->mf_fo->opt_fs; char *rfs = NULL; for (i = 0; i < nx->nx_c; i++) { char *path = ivec[i + 1]; rfs = str3cat(rfs, pref, "/", path); /* * Determine the mount point. * If this is the root, then don't remove * the trailing slash to avoid mntfs name clashes. */ mp = str3cat(mp, fs, "/", rfs); normalize_slash(mp); deslashify(mp); /* * Determine the mount info */ xinfo = str3cat(xinfo, host, *path == '/' ? "" : "/", path); normalize_slash(xinfo); if (pref[1] != '\0') deslashify(xinfo); dlog("amfs_nfsx: init mount for %s on %s", xinfo, mp); nx->nx_v[i].n_error = -1; nx->nx_v[i].n_mnt = find_mntfs(&nfs_ops, mf->mf_fo, mp, xinfo, "", mf->mf_mopts, mf->mf_remopts); /* propagate the on_autofs flag */ nx->nx_v[i].n_mnt->mf_flags |= mf->mf_flags & MFF_ON_AUTOFS; } XFREE(rfs); XFREE(mp); XFREE(xinfo); } XFREE(ivec); errexit: XFREE(info); if (error) return error; } /* * Iterate through the mntfs's and call * the underlying init routine on each */ glob_error = 0; for (i = 0; i < nx->nx_c; i++) { amfs_nfsx_mnt *n = &nx->nx_v[i]; mntfs *m = n->n_mnt; int error = 0; if (m->mf_ops->fs_init && !(mf->mf_flags & MFF_RESTART)) error = m->mf_ops->fs_init(m); /* * if you just "return error" here, you will have made a failure * in any submounts to fail the whole group. There was old unused code * here before. */ if (error > 0) n->n_error = error; else if (error < 0) { glob_error = -1; if (!asked_for_wakeup) { asked_for_wakeup = 1; sched_task(wakeup_task, (opaque_t) mf, get_mntfs_wchan(m)); } } } return glob_error; } static void amfs_nfsx_cont(int rc, int term, opaque_t arg) { mntfs *mf = (mntfs *) arg; struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; am_node *mp = nx->nx_mp; amfs_nfsx_mnt *n = nx->nx_try; n->n_mnt->mf_flags &= ~(MFF_ERROR | MFF_MOUNTING); mf->mf_flags &= ~MFF_ERROR; /* * Wakeup anything waiting for this mount */ wakeup(get_mntfs_wchan(n->n_mnt)); if (rc || term) { if (term) { /* * Not sure what to do for an error code. */ plog(XLOG_ERROR, "mount for %s got signal %d", n->n_mnt->mf_mount, term); n->n_error = EIO; } else { /* * Check for exit status */ errno = rc; /* XXX */ plog(XLOG_ERROR, "%s: mount (amfs_nfsx_cont): %m", n->n_mnt->mf_mount); n->n_error = rc; } free_mntfs(n->n_mnt); n->n_mnt = new_mntfs(); n->n_mnt->mf_error = n->n_error; n->n_mnt->mf_flags |= MFF_ERROR; } else { /* * The mount worked. */ mf_mounted(n->n_mnt, FALSE); /* FALSE => don't free the n_mnt->am_opts */ n->n_error = 0; } /* * Do the remaining bits */ if (amfs_nfsx_mount(mp, mf) >= 0) wakeup(get_mntfs_wchan(mf)); } static int try_amfs_nfsx_mount(opaque_t mv) { mntfs *mf = (mntfs *) mv; struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; am_node *mp = nx->nx_mp; int error; error = mf->mf_ops->mount_fs(mp, mf); return error; } static int amfs_nfsx_remount(am_node *am, mntfs *mf, int fg) { struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; amfs_nfsx_mnt *n; int glob_error = -1; /* Save the am_node pointer for later use */ nx->nx_mp = am; /* * Iterate through the mntfs's and mount each filesystem * which is not yet mounted. */ for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) { mntfs *m = n->n_mnt; if (m->mf_flags & MFF_MOUNTING) break; if (m->mf_flags & MFF_MOUNTED) { mf_mounted(m, FALSE); /* FALSE => don't free the m->am_opts */ n->n_error = glob_error = 0; continue; } if (n->n_error < 0) { /* Create the mountpoint, if and as required */ if (!(m->mf_flags & MFF_MKMNT) && m->mf_fsflags & FS_MKMNT) { if (!mkdirs(m->mf_mount, 0555)) m->mf_flags |= MFF_MKMNT; } dlog("calling underlying mount on %s", m->mf_mount); if (!fg && foreground && (m->mf_fsflags & FS_MBACKGROUND)) { m->mf_flags |= MFF_MOUNTING; dlog("backgrounding mount of \"%s\"", m->mf_info); nx->nx_try = n; run_task(try_amfs_nfsx_mount, (opaque_t) m, amfs_nfsx_cont, (opaque_t) mf); n->n_error = -1; return -1; } else { dlog("foreground mount of \"%s\" ...", mf->mf_info); n->n_error = m->mf_ops->mount_fs(am, m); } if (n->n_error > 0) dlog("underlying fmount of %s failed: %s", m->mf_mount, strerror(n->n_error)); if (n->n_error == 0) { glob_error = 0; } else if (glob_error < 0) { glob_error = n->n_error; } } } return glob_error < 0 ? 0 : glob_error; } static int amfs_nfsx_mount(am_node *am, mntfs *mf) { return amfs_nfsx_remount(am, mf, FALSE); } /* * Unmount an NFS hierarchy. * Note that this is called in the foreground * and so may hang under extremely rare conditions. */ static int amfs_nfsx_umount(am_node *am, mntfs *mf) { struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private; amfs_nfsx_mnt *n; int glob_error = 0; /* * Iterate in reverse through the mntfs's and unmount each filesystem * which is mounted. */ for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) { mntfs *m = n->n_mnt; /* * If this node has not been messed with * and there has been no error so far * then try and unmount. * If an error had occurred then zero * the error code so that the remount * only tries to unmount those nodes * which had been successfully unmounted. */ if (n->n_error == 0) { dlog("calling underlying fumount on %s", m->mf_mount); n->n_error = m->mf_ops->umount_fs(am, m); if (n->n_error) { glob_error = n->n_error; n->n_error = 0; } else { /* * Make sure remount gets this node */ n->n_error = -1; } } } /* * If any unmounts failed then remount the * whole lot... */ if (glob_error) { glob_error = amfs_nfsx_remount(am, mf, TRUE); if (glob_error) { errno = glob_error; /* XXX */ plog(XLOG_USER, "amfs_nfsx: remount of %s failed: %m", mf->mf_mount); } glob_error = EBUSY; } else { /* * Remove all the mount points */ for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) { mntfs *m = n->n_mnt; dlog("calling underlying umounted on %s", m->mf_mount); if (m->mf_ops->umounted) m->mf_ops->umounted(m); if (n->n_error < 0) { if (m->mf_fsflags & FS_MKMNT) { (void) rmdirs(m->mf_mount); m->mf_flags &= ~MFF_MKMNT; } } free_mntfs(m); n->n_mnt = NULL; n->n_error = -1; } } return glob_error; }