Home | History | Annotate | Line # | Download | only in amd
      1 /*	$NetBSD: amfs_nfsx.c,v 1.1.1.3 2015/01/17 16:34:15 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1997-2014 Erez Zadok
      5  * Copyright (c) 1990 Jan-Simon Pendry
      6  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
      7  * Copyright (c) 1990 The Regents of the University of California.
      8  * All rights reserved.
      9  *
     10  * This code is derived from software contributed to Berkeley by
     11  * Jan-Simon Pendry at Imperial College, London.
     12  *
     13  * Redistribution and use in source and binary forms, with or without
     14  * modification, are permitted provided that the following conditions
     15  * are met:
     16  * 1. Redistributions of source code must retain the above copyright
     17  *    notice, this list of conditions and the following disclaimer.
     18  * 2. Redistributions in binary form must reproduce the above copyright
     19  *    notice, this list of conditions and the following disclaimer in the
     20  *    documentation and/or other materials provided with the distribution.
     21  * 3. Neither the name of the University nor the names of its contributors
     22  *    may be used to endorse or promote products derived from this software
     23  *    without specific prior written permission.
     24  *
     25  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     35  * SUCH DAMAGE.
     36  *
     37  *
     38  * File: am-utils/amd/amfs_nfsx.c
     39  *
     40  */
     41 
     42 /*
     43  * NFS hierarchical mounts
     44  *
     45  * TODO: Re-implement.
     46  */
     47 
     48 #ifdef HAVE_CONFIG_H
     49 # include <config.h>
     50 #endif /* HAVE_CONFIG_H */
     51 #include <am_defs.h>
     52 #include <amd.h>
     53 
     54 /*
     55  * The rfs field contains a list of mounts to be done from
     56  * the remote host.
     57  */
     58 typedef struct amfs_nfsx_mnt {
     59   mntfs *n_mnt;
     60   int n_error;
     61 } amfs_nfsx_mnt;
     62 
     63 struct amfs_nfsx {
     64   int nx_c;			/* Number of elements in nx_v */
     65   amfs_nfsx_mnt *nx_v;		/* Underlying mounts */
     66   amfs_nfsx_mnt *nx_try;
     67   am_node *nx_mp;
     68 };
     69 
     70 /* forward definitions */
     71 static char *amfs_nfsx_match(am_opts *fo);
     72 static int amfs_nfsx_mount(am_node *am, mntfs *mf);
     73 static int amfs_nfsx_umount(am_node *am, mntfs *mf);
     74 static int amfs_nfsx_init(mntfs *mf);
     75 
     76 /*
     77  * Ops structure
     78  */
     79 am_ops amfs_nfsx_ops =
     80 {
     81   "nfsx",
     82   amfs_nfsx_match,
     83   amfs_nfsx_init,
     84   amfs_nfsx_mount,
     85   amfs_nfsx_umount,
     86   amfs_error_lookup_child,
     87   amfs_error_mount_child,
     88   amfs_error_readdir,
     89   0,				/* amfs_nfsx_readlink */
     90   0,				/* amfs_nfsx_mounted */
     91   0,				/* amfs_nfsx_umounted */
     92   find_nfs_srvr,		/* XXX */
     93   0,				/* amfs_nfsx_get_wchan */
     94   /* FS_UBACKGROUND| */ FS_AMQINFO,	/* nfs_fs_flags */
     95 #ifdef HAVE_FS_AUTOFS
     96   AUTOFS_NFSX_FS_FLAGS,
     97 #endif /* HAVE_FS_AUTOFS */
     98 };
     99 
    100 
    101 static char *
    102 amfs_nfsx_match(am_opts *fo)
    103 {
    104   char *xmtab;
    105   char *ptr;
    106   int len;
    107 
    108   if (!fo->opt_rfs) {
    109     plog(XLOG_USER, "amfs_nfsx: no remote filesystem specified");
    110     return FALSE;
    111   }
    112 
    113   if (!fo->opt_rhost) {
    114     plog(XLOG_USER, "amfs_nfsx: no remote host specified");
    115     return FALSE;
    116   }
    117 
    118   /* set default sublink */
    119   if (fo->opt_sublink == NULL || fo->opt_sublink[0] == '\0') {
    120     ptr = strchr(fo->opt_rfs, ',');
    121     if (ptr && ptr > (fo->opt_rfs + 1))
    122       fo->opt_sublink = strnsave(fo->opt_rfs + 1, ptr - fo->opt_rfs - 1);
    123   }
    124 
    125   /*
    126    * Remove trailing ",..." from ${fs}
    127    * After deslashifying, overwrite the end of ${fs} with "/"
    128    * to make sure it is unique.
    129    */
    130   if ((ptr = strchr(fo->opt_fs, ',')))
    131     *ptr = '\0';
    132   deslashify(fo->opt_fs);
    133 
    134   /*
    135    * Bump string length to allow trailing /
    136    */
    137   len = strlen(fo->opt_fs);
    138   fo->opt_fs = xrealloc(fo->opt_fs, len + 1 + 1);
    139   ptr = fo->opt_fs + len;
    140 
    141   /*
    142    * Make unique...
    143    */
    144   *ptr++ = '/';
    145   *ptr = '\0';
    146 
    147   /*
    148    * Determine magic cookie to put in mtab
    149    */
    150   xmtab = str3cat((char *) NULL, fo->opt_rhost, ":", fo->opt_rfs);
    151   dlog("NFSX: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
    152        fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
    153 
    154   return xmtab;
    155 }
    156 
    157 
    158 static void
    159 amfs_nfsx_prfree(opaque_t vp)
    160 {
    161   struct amfs_nfsx *nx = (struct amfs_nfsx *) vp;
    162   int i;
    163 
    164   for (i = 0; i < nx->nx_c; i++) {
    165     mntfs *m = nx->nx_v[i].n_mnt;
    166     if (m)
    167       free_mntfs(m);
    168   }
    169 
    170   XFREE(nx->nx_v);
    171   XFREE(nx);
    172 }
    173 
    174 
    175 static int
    176 amfs_nfsx_init(mntfs *mf)
    177 {
    178   /*
    179    * mf_info has the form:
    180    *   host:/prefix/path,sub,sub,sub
    181    */
    182   int i;
    183   int glob_error;
    184   struct amfs_nfsx *nx;
    185   int asked_for_wakeup = 0;
    186 
    187   nx = (struct amfs_nfsx *) mf->mf_private;
    188 
    189   if (nx == 0) {
    190     char **ivec;
    191     char *info = NULL;
    192     char *host;
    193     char *pref;
    194     int error = 0;
    195 
    196     info = xstrdup(mf->mf_info);
    197     if (info == NULL)
    198       return errno;
    199 
    200     host = strchr(info, ':');
    201     if (!host) {
    202       error = EINVAL;
    203       goto errexit;
    204     }
    205     pref = host + 1;
    206     host = info;
    207 
    208     /*
    209      * Split the prefix off from the suffices
    210      */
    211     ivec = strsplit(pref, ',', '\'');
    212 
    213     /*
    214      * Count array size
    215      */
    216     for (i = 0; ivec[i]; i++)
    217       /* nothing */;
    218 
    219     nx = ALLOC(struct amfs_nfsx);
    220     mf->mf_private = (opaque_t) nx;
    221     mf->mf_prfree = amfs_nfsx_prfree;
    222 
    223     nx->nx_c = i - 1;		/* i-1 because we don't want the prefix */
    224     nx->nx_v = (amfs_nfsx_mnt *) xmalloc(nx->nx_c * sizeof(amfs_nfsx_mnt));
    225     nx->nx_mp = NULL;
    226     {
    227       char *mp = NULL;
    228       char *xinfo = NULL;
    229       char *fs = mf->mf_fo->opt_fs;
    230       char *rfs = NULL;
    231       for (i = 0; i < nx->nx_c; i++) {
    232 	char *path = ivec[i + 1];
    233 	rfs = str3cat(rfs, pref, "/", path);
    234 	/*
    235 	 * Determine the mount point.
    236 	 * If this is the root, then don't remove
    237 	 * the trailing slash to avoid mntfs name clashes.
    238 	 */
    239 	mp = str3cat(mp, fs, "/", rfs);
    240 	normalize_slash(mp);
    241 	deslashify(mp);
    242 	/*
    243 	 * Determine the mount info
    244 	 */
    245 	xinfo = str3cat(xinfo, host, *path == '/' ? "" : "/", path);
    246 	normalize_slash(xinfo);
    247 	if (pref[1] != '\0')
    248 	  deslashify(xinfo);
    249 	dlog("amfs_nfsx: init mount for %s on %s", xinfo, mp);
    250 	nx->nx_v[i].n_error = -1;
    251 	nx->nx_v[i].n_mnt = find_mntfs(&nfs_ops, mf->mf_fo, mp, xinfo, "", mf->mf_mopts, mf->mf_remopts);
    252 	/* propagate the on_autofs flag */
    253 	nx->nx_v[i].n_mnt->mf_flags |= mf->mf_flags & MFF_ON_AUTOFS;
    254       }
    255       XFREE(rfs);
    256       XFREE(mp);
    257       XFREE(xinfo);
    258     }
    259 
    260     XFREE(ivec);
    261   errexit:
    262     XFREE(info);
    263     if (error)
    264       return error;
    265   }
    266 
    267   /*
    268    * Iterate through the mntfs's and call
    269    * the underlying init routine on each
    270    */
    271   glob_error = 0;
    272 
    273   for (i = 0; i < nx->nx_c; i++) {
    274     amfs_nfsx_mnt *n = &nx->nx_v[i];
    275     mntfs *m = n->n_mnt;
    276     int error = 0;
    277     if (m->mf_ops->fs_init && !(mf->mf_flags & MFF_RESTART))
    278       error = m->mf_ops->fs_init(m);
    279     /*
    280      * if you just "return error" here, you will have made a failure
    281      * in any submounts to fail the whole group.  There was old unused code
    282      * here before.
    283      */
    284     if (error > 0)
    285       n->n_error = error;
    286 
    287     else if (error < 0) {
    288       glob_error = -1;
    289       if (!asked_for_wakeup) {
    290 	asked_for_wakeup = 1;
    291 	sched_task(wakeup_task, (opaque_t) mf, get_mntfs_wchan(m));
    292       }
    293     }
    294   }
    295 
    296   return glob_error;
    297 }
    298 
    299 
    300 static void
    301 amfs_nfsx_cont(int rc, int term, opaque_t arg)
    302 {
    303   mntfs *mf = (mntfs *) arg;
    304   struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
    305   am_node *mp = nx->nx_mp;
    306   amfs_nfsx_mnt *n = nx->nx_try;
    307 
    308   n->n_mnt->mf_flags &= ~(MFF_ERROR | MFF_MOUNTING);
    309   mf->mf_flags &= ~MFF_ERROR;
    310 
    311   /*
    312    * Wakeup anything waiting for this mount
    313    */
    314   wakeup(get_mntfs_wchan(n->n_mnt));
    315 
    316   if (rc || term) {
    317     if (term) {
    318       /*
    319        * Not sure what to do for an error code.
    320        */
    321       plog(XLOG_ERROR, "mount for %s got signal %d", n->n_mnt->mf_mount, term);
    322       n->n_error = EIO;
    323     } else {
    324       /*
    325        * Check for exit status
    326        */
    327       errno = rc;		/* XXX */
    328       plog(XLOG_ERROR, "%s: mount (amfs_nfsx_cont): %m", n->n_mnt->mf_mount);
    329       n->n_error = rc;
    330     }
    331     free_mntfs(n->n_mnt);
    332     n->n_mnt = new_mntfs();
    333     n->n_mnt->mf_error = n->n_error;
    334     n->n_mnt->mf_flags |= MFF_ERROR;
    335   } else {
    336     /*
    337      * The mount worked.
    338      */
    339     mf_mounted(n->n_mnt, FALSE); /* FALSE => don't free the n_mnt->am_opts */
    340     n->n_error = 0;
    341   }
    342 
    343   /*
    344    * Do the remaining bits
    345    */
    346   if (amfs_nfsx_mount(mp, mf) >= 0)
    347     wakeup(get_mntfs_wchan(mf));
    348 }
    349 
    350 
    351 static int
    352 try_amfs_nfsx_mount(opaque_t mv)
    353 {
    354   mntfs *mf = (mntfs *) mv;
    355   struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
    356   am_node *mp = nx->nx_mp;
    357   int error;
    358 
    359   error = mf->mf_ops->mount_fs(mp, mf);
    360 
    361   return error;
    362 }
    363 
    364 
    365 static int
    366 amfs_nfsx_remount(am_node *am, mntfs *mf, int fg)
    367 {
    368   struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
    369   amfs_nfsx_mnt *n;
    370   int glob_error = -1;
    371 
    372   /* Save the am_node pointer for later use */
    373   nx->nx_mp = am;
    374 
    375   /*
    376    * Iterate through the mntfs's and mount each filesystem
    377    * which is not yet mounted.
    378    */
    379   for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
    380     mntfs *m = n->n_mnt;
    381 
    382     if (m->mf_flags & MFF_MOUNTING)
    383       break;
    384 
    385     if (m->mf_flags & MFF_MOUNTED) {
    386       mf_mounted(m, FALSE);	/* FALSE => don't free the m->am_opts */
    387       n->n_error = glob_error = 0;
    388       continue;
    389     }
    390 
    391     if (n->n_error < 0) {
    392       /* Create the mountpoint, if and as required */
    393       if (!(m->mf_flags & MFF_MKMNT) && m->mf_fsflags & FS_MKMNT) {
    394 	if (!mkdirs(m->mf_mount, 0555))
    395 	  m->mf_flags |= MFF_MKMNT;
    396       }
    397 
    398       dlog("calling underlying mount on %s", m->mf_mount);
    399       if (!fg && foreground && (m->mf_fsflags & FS_MBACKGROUND)) {
    400 	m->mf_flags |= MFF_MOUNTING;
    401 	dlog("backgrounding mount of \"%s\"", m->mf_info);
    402 	nx->nx_try = n;
    403 	run_task(try_amfs_nfsx_mount, (opaque_t) m, amfs_nfsx_cont, (opaque_t) mf);
    404 	n->n_error = -1;
    405 	return -1;
    406       } else {
    407 	dlog("foreground mount of \"%s\" ...", mf->mf_info);
    408 	n->n_error = m->mf_ops->mount_fs(am, m);
    409       }
    410 
    411       if (n->n_error > 0)
    412 	dlog("underlying fmount of %s failed: %s", m->mf_mount, strerror(n->n_error));
    413 
    414       if (n->n_error == 0) {
    415 	glob_error = 0;
    416       } else if (glob_error < 0) {
    417 	glob_error = n->n_error;
    418       }
    419     }
    420   }
    421 
    422   return glob_error < 0 ? 0 : glob_error;
    423 }
    424 
    425 
    426 static int
    427 amfs_nfsx_mount(am_node *am, mntfs *mf)
    428 {
    429   return amfs_nfsx_remount(am, mf, FALSE);
    430 }
    431 
    432 
    433 /*
    434  * Unmount an NFS hierarchy.
    435  * Note that this is called in the foreground
    436  * and so may hang under extremely rare conditions.
    437  */
    438 static int
    439 amfs_nfsx_umount(am_node *am, mntfs *mf)
    440 {
    441   struct amfs_nfsx *nx = (struct amfs_nfsx *) mf->mf_private;
    442   amfs_nfsx_mnt *n;
    443   int glob_error = 0;
    444 
    445   /*
    446    * Iterate in reverse through the mntfs's and unmount each filesystem
    447    * which is mounted.
    448    */
    449   for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) {
    450     mntfs *m = n->n_mnt;
    451     /*
    452      * If this node has not been messed with
    453      * and there has been no error so far
    454      * then try and unmount.
    455      * If an error had occurred then zero
    456      * the error code so that the remount
    457      * only tries to unmount those nodes
    458      * which had been successfully unmounted.
    459      */
    460     if (n->n_error == 0) {
    461       dlog("calling underlying fumount on %s", m->mf_mount);
    462       n->n_error = m->mf_ops->umount_fs(am, m);
    463       if (n->n_error) {
    464 	glob_error = n->n_error;
    465 	n->n_error = 0;
    466       } else {
    467 	/*
    468 	 * Make sure remount gets this node
    469 	 */
    470 	n->n_error = -1;
    471       }
    472     }
    473   }
    474 
    475   /*
    476    * If any unmounts failed then remount the
    477    * whole lot...
    478    */
    479   if (glob_error) {
    480     glob_error = amfs_nfsx_remount(am, mf, TRUE);
    481     if (glob_error) {
    482       errno = glob_error;	/* XXX */
    483       plog(XLOG_USER, "amfs_nfsx: remount of %s failed: %m", mf->mf_mount);
    484     }
    485     glob_error = EBUSY;
    486   } else {
    487     /*
    488      * Remove all the mount points
    489      */
    490     for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
    491       mntfs *m = n->n_mnt;
    492       dlog("calling underlying umounted on %s", m->mf_mount);
    493       if (m->mf_ops->umounted)
    494 	m->mf_ops->umounted(m);
    495 
    496       if (n->n_error < 0) {
    497 	if (m->mf_fsflags & FS_MKMNT) {
    498 	  (void) rmdirs(m->mf_mount);
    499 	  m->mf_flags &= ~MFF_MKMNT;
    500 	}
    501       }
    502       free_mntfs(m);
    503       n->n_mnt = NULL;
    504       n->n_error = -1;
    505     }
    506   }
    507 
    508   return glob_error;
    509 }
    510