Home | History | Annotate | Line # | Download | only in amd
amfs_host.c revision 1.1.1.2
      1      1.1  christos /*	$NetBSD: amfs_host.c,v 1.1.1.2 2009/03/20 20:26:49 christos Exp $	*/
      2      1.1  christos 
      3      1.1  christos /*
      4  1.1.1.2  christos  * Copyright (c) 1997-2009 Erez Zadok
      5      1.1  christos  * Copyright (c) 1990 Jan-Simon Pendry
      6      1.1  christos  * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
      7      1.1  christos  * Copyright (c) 1990 The Regents of the University of California.
      8      1.1  christos  * All rights reserved.
      9      1.1  christos  *
     10      1.1  christos  * This code is derived from software contributed to Berkeley by
     11      1.1  christos  * Jan-Simon Pendry at Imperial College, London.
     12      1.1  christos  *
     13      1.1  christos  * Redistribution and use in source and binary forms, with or without
     14      1.1  christos  * modification, are permitted provided that the following conditions
     15      1.1  christos  * are met:
     16      1.1  christos  * 1. Redistributions of source code must retain the above copyright
     17      1.1  christos  *    notice, this list of conditions and the following disclaimer.
     18      1.1  christos  * 2. Redistributions in binary form must reproduce the above copyright
     19      1.1  christos  *    notice, this list of conditions and the following disclaimer in the
     20      1.1  christos  *    documentation and/or other materials provided with the distribution.
     21      1.1  christos  * 3. All advertising materials mentioning features or use of this software
     22      1.1  christos  *    must display the following acknowledgment:
     23      1.1  christos  *      This product includes software developed by the University of
     24      1.1  christos  *      California, Berkeley and its contributors.
     25      1.1  christos  * 4. Neither the name of the University nor the names of its contributors
     26      1.1  christos  *    may be used to endorse or promote products derived from this software
     27      1.1  christos  *    without specific prior written permission.
     28      1.1  christos  *
     29      1.1  christos  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     30      1.1  christos  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     31      1.1  christos  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     32      1.1  christos  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     33      1.1  christos  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     34      1.1  christos  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     35      1.1  christos  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     36      1.1  christos  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     37      1.1  christos  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     38      1.1  christos  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     39      1.1  christos  * SUCH DAMAGE.
     40      1.1  christos  *
     41      1.1  christos  *
     42      1.1  christos  * File: am-utils/amd/amfs_host.c
     43      1.1  christos  *
     44      1.1  christos  */
     45      1.1  christos 
     46      1.1  christos /*
     47      1.1  christos  * NFS host file system.
     48      1.1  christos  * Mounts all exported filesystems from a given host.
     49      1.1  christos  * This has now degenerated into a mess but will not
     50      1.1  christos  * be rewritten.  Amd 6 will support the abstractions
     51      1.1  christos  * needed to make this work correctly.
     52      1.1  christos  */
     53      1.1  christos 
     54      1.1  christos #ifdef HAVE_CONFIG_H
     55      1.1  christos # include <config.h>
     56      1.1  christos #endif /* HAVE_CONFIG_H */
     57      1.1  christos #include <am_defs.h>
     58      1.1  christos #include <amd.h>
     59      1.1  christos 
     60      1.1  christos static char *amfs_host_match(am_opts *fo);
     61      1.1  christos static int amfs_host_init(mntfs *mf);
     62      1.1  christos static int amfs_host_mount(am_node *am, mntfs *mf);
     63      1.1  christos static int amfs_host_umount(am_node *am, mntfs *mf);
     64      1.1  christos static void amfs_host_umounted(mntfs *mf);
     65      1.1  christos 
     66      1.1  christos /*
     67      1.1  christos  * Ops structure
     68      1.1  christos  */
     69      1.1  christos am_ops amfs_host_ops =
     70      1.1  christos {
     71      1.1  christos   "host",
     72      1.1  christos   amfs_host_match,
     73      1.1  christos   amfs_host_init,
     74      1.1  christos   amfs_host_mount,
     75      1.1  christos   amfs_host_umount,
     76      1.1  christos   amfs_error_lookup_child,
     77      1.1  christos   amfs_error_mount_child,
     78      1.1  christos   amfs_error_readdir,
     79      1.1  christos   0,				/* amfs_host_readlink */
     80      1.1  christos   0,				/* amfs_host_mounted */
     81      1.1  christos   amfs_host_umounted,
     82      1.1  christos   find_nfs_srvr,
     83      1.1  christos   0,				/* amfs_host_get_wchan */
     84      1.1  christos   FS_MKMNT | FS_BACKGROUND | FS_AMQINFO,
     85      1.1  christos #ifdef HAVE_FS_AUTOFS
     86      1.1  christos   AUTOFS_HOST_FS_FLAGS,
     87      1.1  christos #endif /* HAVE_FS_AUTOFS */
     88      1.1  christos };
     89      1.1  christos 
     90      1.1  christos 
     91      1.1  christos /*
     92      1.1  christos  * Determine the mount point:
     93      1.1  christos  *
     94      1.1  christos  * The next change we put in to better handle PCs.  This is a bit
     95      1.1  christos  * disgusting, so you'd better sit down.  We change the make_mntpt function
     96      1.1  christos  * to look for exported file systems without a leading '/'.  If they don't
     97      1.1  christos  * have a leading '/', we add one.  If the export is 'a:' through 'z:'
     98      1.1  christos  * (without a leading slash), we change it to 'a%' (or b% or z%).  This
     99      1.1  christos  * allows the entire PC disk to be mounted.
    100      1.1  christos  */
    101      1.1  christos static void
    102      1.1  christos make_mntpt(char *mntpt, size_t l, const exports ex, const char *mf_mount)
    103      1.1  christos {
    104      1.1  christos   if (ex->ex_dir[0] == '/') {
    105      1.1  christos     if (ex->ex_dir[1] == 0)
    106      1.1  christos       xstrlcpy(mntpt, mf_mount, l);
    107      1.1  christos     else
    108      1.1  christos       xsnprintf(mntpt, l, "%s%s", mf_mount, ex->ex_dir);
    109      1.1  christos   } else if (ex->ex_dir[0] >= 'a' &&
    110      1.1  christos 	     ex->ex_dir[0] <= 'z' &&
    111      1.1  christos 	     ex->ex_dir[1] == ':' &&
    112      1.1  christos 	     ex->ex_dir[2] == '/' &&
    113      1.1  christos 	     ex->ex_dir[3] == 0)
    114      1.1  christos     xsnprintf(mntpt, l, "%s/%c%%", mf_mount, ex->ex_dir[0]);
    115      1.1  christos   else
    116      1.1  christos     xsnprintf(mntpt, l, "%s/%s", mf_mount, ex->ex_dir);
    117      1.1  christos }
    118      1.1  christos 
    119      1.1  christos 
    120      1.1  christos /*
    121      1.1  christos  * Execute needs the same as NFS plus a helper command
    122      1.1  christos  */
    123      1.1  christos static char *
    124      1.1  christos amfs_host_match(am_opts *fo)
    125      1.1  christos {
    126      1.1  christos   extern am_ops nfs_ops;
    127      1.1  christos 
    128      1.1  christos   /*
    129      1.1  christos    * Make sure rfs is specified to keep nfs_match happy...
    130      1.1  christos    */
    131      1.1  christos   if (!fo->opt_rfs)
    132      1.1  christos     fo->opt_rfs = "/";
    133      1.1  christos 
    134      1.1  christos   return (*nfs_ops.fs_match) (fo);
    135      1.1  christos }
    136      1.1  christos 
    137      1.1  christos 
    138      1.1  christos static int
    139      1.1  christos amfs_host_init(mntfs *mf)
    140      1.1  christos {
    141      1.1  christos   u_short mountd_port;
    142      1.1  christos 
    143      1.1  christos   if (strchr(mf->mf_info, ':') == 0)
    144      1.1  christos     return ENOENT;
    145      1.1  christos 
    146      1.1  christos   /*
    147      1.1  christos    * This is primarily to schedule a wakeup so that as soon
    148      1.1  christos    * as our fileserver is ready, we can continue setting up
    149      1.1  christos    * the host filesystem.  If we don't do this, the standard
    150      1.1  christos    * amfs_auto code will set up a fileserver structure, but it will
    151      1.1  christos    * have to wait for another nfs request from the client to come
    152      1.1  christos    * in before finishing.  Our way is faster since we don't have
    153      1.1  christos    * to wait for the client to resend its request (which could
    154      1.1  christos    * take a second or two).
    155      1.1  christos    */
    156      1.1  christos   /*
    157      1.1  christos    * First, we find the fileserver for this mntfs and then call
    158      1.1  christos    * get_mountd_port with our mntfs passed as the wait channel.
    159      1.1  christos    * get_mountd_port will check some things and then schedule
    160      1.1  christos    * it so that when the fileserver is ready, a wakeup is done
    161      1.1  christos    * on this mntfs.   amfs_cont() is already sleeping on this mntfs
    162      1.1  christos    * so as soon as that wakeup happens amfs_cont() is called and
    163      1.1  christos    * this mount is retried.
    164      1.1  christos    */
    165      1.1  christos   if (mf->mf_server)
    166      1.1  christos     /*
    167      1.1  christos      * We don't really care if there's an error returned.
    168      1.1  christos      * Since this is just to help speed things along, the
    169      1.1  christos      * error will get handled properly elsewhere.
    170      1.1  christos      */
    171      1.1  christos     get_mountd_port(mf->mf_server, &mountd_port, get_mntfs_wchan(mf));
    172      1.1  christos 
    173      1.1  christos   return 0;
    174      1.1  christos }
    175      1.1  christos 
    176      1.1  christos 
    177      1.1  christos static int
    178      1.1  christos do_mount(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf)
    179      1.1  christos {
    180      1.1  christos   struct stat stb;
    181      1.1  christos 
    182      1.1  christos   dlog("amfs_host: mounting fs %s on %s\n", fs_name, mntdir);
    183      1.1  christos 
    184      1.1  christos   (void) mkdirs(mntdir, 0555);
    185      1.1  christos   if (stat(mntdir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) {
    186      1.1  christos     plog(XLOG_ERROR, "No mount point for %s - skipping", mntdir);
    187      1.1  christos     return ENOENT;
    188      1.1  christos   }
    189      1.1  christos 
    190      1.1  christos   return mount_nfs_fh(fhp, mntdir, fs_name, mf);
    191      1.1  christos }
    192      1.1  christos 
    193      1.1  christos 
    194      1.1  christos static int
    195      1.1  christos sortfun(const voidp x, const voidp y)
    196      1.1  christos {
    197      1.1  christos   exports *a = (exports *) x;
    198      1.1  christos   exports *b = (exports *) y;
    199      1.1  christos 
    200      1.1  christos   return strcmp((*a)->ex_dir, (*b)->ex_dir);
    201      1.1  christos }
    202      1.1  christos 
    203      1.1  christos 
    204      1.1  christos /*
    205      1.1  christos  * Get filehandle
    206      1.1  christos  */
    207      1.1  christos static int
    208      1.1  christos fetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_version)
    209      1.1  christos {
    210      1.1  christos   struct timeval tv;
    211      1.1  christos   enum clnt_stat clnt_stat;
    212      1.1  christos   struct fhstatus res;
    213      1.1  christos #ifdef HAVE_FS_NFS3
    214      1.1  christos   struct am_mountres3 res3;
    215      1.1  christos #endif /* HAVE_FS_NFS3 */
    216      1.1  christos 
    217      1.1  christos   /*
    218      1.1  christos    * Pick a number, any number...
    219      1.1  christos    */
    220      1.1  christos   tv.tv_sec = 20;
    221      1.1  christos   tv.tv_usec = 0;
    222      1.1  christos 
    223      1.1  christos   dlog("Fetching fhandle for %s", dir);
    224      1.1  christos 
    225      1.1  christos   /*
    226      1.1  christos    * Call the mount daemon on the remote host to
    227      1.1  christos    * get the filehandle.  Use NFS version specific call.
    228      1.1  christos    */
    229      1.1  christos 
    230      1.1  christos   plog(XLOG_INFO, "fetch_fhandle: NFS version %d", (int) nfs_version);
    231      1.1  christos #ifdef HAVE_FS_NFS3
    232      1.1  christos   if (nfs_version == NFS_VERSION3) {
    233      1.1  christos     memset((char *) &res3, 0, sizeof(res3));
    234      1.1  christos     clnt_stat = clnt_call(client,
    235      1.1  christos 			  MOUNTPROC_MNT,
    236      1.1  christos 			  (XDRPROC_T_TYPE) xdr_dirpath,
    237      1.1  christos 			  (SVC_IN_ARG_TYPE) &dir,
    238      1.1  christos 			  (XDRPROC_T_TYPE) xdr_am_mountres3,
    239      1.1  christos 			  (SVC_IN_ARG_TYPE) &res3,
    240      1.1  christos 			  tv);
    241      1.1  christos     if (clnt_stat != RPC_SUCCESS) {
    242      1.1  christos       plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
    243      1.1  christos       return EIO;
    244      1.1  christos     }
    245      1.1  christos     /* Check the status of the filehandle */
    246      1.1  christos     if ((errno = res3.fhs_status)) {
    247      1.1  christos       dlog("fhandle fetch for mount version 3 failed: %m");
    248      1.1  christos       return errno;
    249      1.1  christos     }
    250      1.1  christos     memset((voidp) &fhp->v3, 0, sizeof(am_nfs_fh3));
    251      1.1  christos     fhp->v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len;
    252      1.1  christos     memmove(fhp->v3.am_fh3_data,
    253      1.1  christos 	    res3.mountres3_u.mountinfo.fhandle.fhandle3_val,
    254      1.1  christos 	    fhp->v3.am_fh3_length);
    255      1.1  christos   } else {			/* not NFS_VERSION3 mount */
    256      1.1  christos #endif /* HAVE_FS_NFS3 */
    257      1.1  christos     clnt_stat = clnt_call(client,
    258      1.1  christos 			  MOUNTPROC_MNT,
    259      1.1  christos 			  (XDRPROC_T_TYPE) xdr_dirpath,
    260      1.1  christos 			  (SVC_IN_ARG_TYPE) &dir,
    261      1.1  christos 			  (XDRPROC_T_TYPE) xdr_fhstatus,
    262      1.1  christos 			  (SVC_IN_ARG_TYPE) &res,
    263      1.1  christos 			  tv);
    264      1.1  christos     if (clnt_stat != RPC_SUCCESS) {
    265      1.1  christos       plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
    266      1.1  christos       return EIO;
    267      1.1  christos     }
    268      1.1  christos     /* Check status of filehandle */
    269      1.1  christos     if (res.fhs_status) {
    270      1.1  christos       errno = res.fhs_status;
    271      1.1  christos       dlog("fhandle fetch for mount version 1 failed: %m");
    272      1.1  christos       return errno;
    273      1.1  christos     }
    274      1.1  christos     memmove(&fhp->v2, &res.fhs_fh, NFS_FHSIZE);
    275      1.1  christos #ifdef HAVE_FS_NFS3
    276      1.1  christos   } /* end of "if (nfs_version == NFS_VERSION3)" statement */
    277      1.1  christos #endif /* HAVE_FS_NFS3 */
    278      1.1  christos 
    279      1.1  christos   /* all is well */
    280      1.1  christos   return 0;
    281      1.1  christos }
    282      1.1  christos 
    283      1.1  christos 
    284      1.1  christos /*
    285      1.1  christos  * Scan mount table to see if something already mounted
    286      1.1  christos  */
    287      1.1  christos static int
    288      1.1  christos already_mounted(mntlist *mlist, char *dir)
    289      1.1  christos {
    290      1.1  christos   mntlist *ml;
    291      1.1  christos 
    292      1.1  christos   for (ml = mlist; ml; ml = ml->mnext)
    293      1.1  christos     if (STREQ(ml->mnt->mnt_dir, dir))
    294      1.1  christos       return 1;
    295      1.1  christos   return 0;
    296      1.1  christos }
    297      1.1  christos 
    298      1.1  christos 
    299      1.1  christos static int
    300      1.1  christos amfs_host_mount(am_node *am, mntfs *mf)
    301      1.1  christos {
    302      1.1  christos   struct timeval tv2;
    303      1.1  christos   CLIENT *client;
    304      1.1  christos   enum clnt_stat clnt_stat;
    305      1.1  christos   int n_export;
    306      1.1  christos   int j, k;
    307      1.1  christos   exports exlist = 0, ex;
    308      1.1  christos   exports *ep = NULL;
    309      1.1  christos   am_nfs_handle_t *fp = NULL;
    310      1.1  christos   char *host;
    311      1.1  christos   int error = 0;
    312      1.1  christos   struct sockaddr_in sin;
    313      1.1  christos   int sock = RPC_ANYSOCK;
    314      1.1  christos   int ok = FALSE;
    315      1.1  christos   mntlist *mlist;
    316      1.1  christos   char fs_name[MAXPATHLEN], *rfs_dir;
    317      1.1  christos   char mntpt[MAXPATHLEN];
    318      1.1  christos   struct timeval tv;
    319      1.1  christos   u_long mnt_version;
    320      1.1  christos 
    321      1.1  christos   /*
    322      1.1  christos    * WebNFS servers don't necessarily run mountd.
    323      1.1  christos    */
    324      1.1  christos   if (mf->mf_flags & MFF_WEBNFS) {
    325      1.1  christos     plog(XLOG_ERROR, "amfs_host_mount: cannot support WebNFS");
    326      1.1  christos     return EIO;
    327      1.1  christos   }
    328      1.1  christos 
    329      1.1  christos   /*
    330      1.1  christos    * Read the mount list
    331      1.1  christos    */
    332      1.1  christos   mlist = read_mtab(mf->mf_mount, mnttab_file_name);
    333      1.1  christos 
    334      1.1  christos #ifdef MOUNT_TABLE_ON_FILE
    335      1.1  christos   /*
    336      1.1  christos    * Unlock the mount list
    337      1.1  christos    */
    338      1.1  christos   unlock_mntlist();
    339      1.1  christos #endif /* MOUNT_TABLE_ON_FILE */
    340      1.1  christos 
    341      1.1  christos   /*
    342      1.1  christos    * Take a copy of the server hostname, address, and nfs version
    343      1.1  christos    * to mount version conversion.
    344      1.1  christos    */
    345      1.1  christos   host = mf->mf_server->fs_host;
    346      1.1  christos   sin = *mf->mf_server->fs_ip;
    347      1.1  christos   plog(XLOG_INFO, "amfs_host_mount: NFS version %d", (int) mf->mf_server->fs_version);
    348      1.1  christos #ifdef HAVE_FS_NFS3
    349      1.1  christos   if (mf->mf_server->fs_version == NFS_VERSION3)
    350      1.1  christos     mnt_version = AM_MOUNTVERS3;
    351      1.1  christos   else
    352      1.1  christos #endif /* HAVE_FS_NFS3 */
    353      1.1  christos     mnt_version = MOUNTVERS;
    354      1.1  christos 
    355      1.1  christos   /*
    356      1.1  christos    * The original 10 second per try timeout is WAY too large, especially
    357      1.1  christos    * if we're only waiting 10 or 20 seconds max for the response.
    358      1.1  christos    * That would mean we'd try only once in 10 seconds, and we could
    359      1.1  christos    * lose the transmit or receive packet, and never try again.
    360      1.1  christos    * A 2-second per try timeout here is much more reasonable.
    361      1.1  christos    * 09/28/92 Mike Mitchell, mcm (at) unx.sas.com
    362      1.1  christos    */
    363      1.1  christos   tv.tv_sec = 2;
    364      1.1  christos   tv.tv_usec = 0;
    365      1.1  christos 
    366      1.1  christos   /*
    367      1.1  christos    * Create a client attached to mountd
    368      1.1  christos    */
    369      1.1  christos   client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
    370      1.1  christos   if (client == NULL) {
    371      1.1  christos #ifdef HAVE_CLNT_SPCREATEERROR
    372      1.1  christos     plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
    373      1.1  christos 	 host, clnt_spcreateerror(""));
    374      1.1  christos #else /* not HAVE_CLNT_SPCREATEERROR */
    375      1.1  christos     plog(XLOG_ERROR, "get_mount_client failed for %s", host);
    376      1.1  christos #endif /* not HAVE_CLNT_SPCREATEERROR */
    377      1.1  christos     error = EIO;
    378      1.1  christos     goto out;
    379      1.1  christos   }
    380      1.1  christos   if (!nfs_auth) {
    381      1.1  christos     error = make_nfs_auth();
    382      1.1  christos     if (error)
    383      1.1  christos       goto out;
    384      1.1  christos   }
    385      1.1  christos   client->cl_auth = nfs_auth;
    386      1.1  christos 
    387      1.1  christos   dlog("Fetching export list from %s", host);
    388      1.1  christos 
    389      1.1  christos   /*
    390      1.1  christos    * Fetch the export list
    391      1.1  christos    */
    392      1.1  christos   tv2.tv_sec = 10;
    393      1.1  christos   tv2.tv_usec = 0;
    394      1.1  christos   clnt_stat = clnt_call(client,
    395      1.1  christos 			MOUNTPROC_EXPORT,
    396      1.1  christos 			(XDRPROC_T_TYPE) xdr_void,
    397      1.1  christos 			0,
    398      1.1  christos 			(XDRPROC_T_TYPE) xdr_exports,
    399      1.1  christos 			(SVC_IN_ARG_TYPE) & exlist,
    400      1.1  christos 			tv2);
    401      1.1  christos   if (clnt_stat != RPC_SUCCESS) {
    402      1.1  christos     const char *msg = clnt_sperrno(clnt_stat);
    403      1.1  christos     plog(XLOG_ERROR, "host_mount rpc failed: %s", msg);
    404      1.1  christos     /* clnt_perror(client, "rpc"); */
    405      1.1  christos     error = EIO;
    406      1.1  christos     goto out;
    407      1.1  christos   }
    408      1.1  christos 
    409      1.1  christos   /*
    410      1.1  christos    * Figure out how many exports were returned
    411      1.1  christos    */
    412      1.1  christos   for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) {
    413      1.1  christos     n_export++;
    414      1.1  christos   }
    415      1.1  christos 
    416      1.1  christos   /*
    417      1.1  christos    * Allocate an array of pointers into the list
    418      1.1  christos    * so that they can be sorted.  If the filesystem
    419      1.1  christos    * is already mounted then ignore it.
    420      1.1  christos    */
    421      1.1  christos   ep = (exports *) xmalloc(n_export * sizeof(exports));
    422      1.1  christos   for (j = 0, ex = exlist; ex; ex = ex->ex_next) {
    423      1.1  christos     make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount);
    424      1.1  christos     if (already_mounted(mlist, mntpt))
    425      1.1  christos       /* we have at least one mounted f/s, so don't fail the mount */
    426      1.1  christos       ok = TRUE;
    427      1.1  christos     else
    428      1.1  christos       ep[j++] = ex;
    429      1.1  christos   }
    430      1.1  christos   n_export = j;
    431      1.1  christos 
    432      1.1  christos   /*
    433      1.1  christos    * Sort into order.
    434      1.1  christos    * This way the mounts are done in order down the tree,
    435      1.1  christos    * instead of any random order returned by the mount
    436      1.1  christos    * daemon (the protocol doesn't specify...).
    437      1.1  christos    */
    438      1.1  christos   qsort(ep, n_export, sizeof(exports), sortfun);
    439      1.1  christos 
    440      1.1  christos   /*
    441      1.1  christos    * Allocate an array of filehandles
    442      1.1  christos    */
    443      1.1  christos   fp = (am_nfs_handle_t *) xmalloc(n_export * sizeof(am_nfs_handle_t));
    444      1.1  christos 
    445      1.1  christos   /*
    446      1.1  christos    * Try to obtain filehandles for each directory.
    447      1.1  christos    * If a fetch fails then just zero out the array
    448      1.1  christos    * reference but discard the error.
    449      1.1  christos    */
    450      1.1  christos   for (j = k = 0; j < n_export; j++) {
    451      1.1  christos     /* Check and avoid a duplicated export entry */
    452      1.1  christos     if (j > k && ep[k] && STREQ(ep[j]->ex_dir, ep[k]->ex_dir)) {
    453      1.1  christos       dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir);
    454      1.1  christos       ep[j] = NULL;
    455      1.1  christos     } else {
    456      1.1  christos       k = j;
    457      1.1  christos       error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j],
    458      1.1  christos 			    mf->mf_server->fs_version);
    459      1.1  christos       if (error)
    460      1.1  christos 	ep[j] = NULL;
    461      1.1  christos     }
    462      1.1  christos   }
    463      1.1  christos 
    464      1.1  christos   /*
    465      1.1  christos    * Mount each filesystem for which we have a filehandle.
    466      1.1  christos    * If any of the mounts succeed then mark "ok" and return
    467      1.1  christos    * error code 0 at the end.  If they all fail then return
    468      1.1  christos    * the last error code.
    469      1.1  christos    */
    470      1.1  christos   xstrlcpy(fs_name, mf->mf_info, sizeof(fs_name));
    471      1.1  christos   if ((rfs_dir = strchr(fs_name, ':')) == (char *) NULL) {
    472      1.1  christos     plog(XLOG_FATAL, "amfs_host_mount: mf_info has no colon");
    473      1.1  christos     error = EINVAL;
    474      1.1  christos     goto out;
    475      1.1  christos   }
    476      1.1  christos   ++rfs_dir;
    477      1.1  christos   for (j = 0; j < n_export; j++) {
    478      1.1  christos     ex = ep[j];
    479      1.1  christos     if (ex) {
    480      1.1  christos       /*
    481      1.1  christos        * Note: the sizeof space left in rfs_dir is what's left in fs_name
    482      1.1  christos        * after strchr() above returned a pointer _inside_ fs_name.  The
    483      1.1  christos        * calculation below also takes into account that rfs_dir was
    484      1.1  christos        * incremented by the ++ above.
    485      1.1  christos        */
    486      1.1  christos       xstrlcpy(rfs_dir, ex->ex_dir, sizeof(fs_name) - (rfs_dir - fs_name));
    487      1.1  christos       make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount);
    488      1.1  christos       if (do_mount(&fp[j], mntpt, fs_name, mf) == 0)
    489      1.1  christos 	ok = TRUE;
    490      1.1  christos     }
    491      1.1  christos   }
    492      1.1  christos 
    493      1.1  christos   /*
    494      1.1  christos    * Clean up and exit
    495      1.1  christos    */
    496      1.1  christos out:
    497      1.1  christos   discard_mntlist(mlist);
    498      1.1  christos   if (ep)
    499      1.1  christos     XFREE(ep);
    500      1.1  christos   if (fp)
    501      1.1  christos     XFREE(fp);
    502      1.1  christos   if (sock != RPC_ANYSOCK)
    503      1.1  christos     (void) amu_close(sock);
    504      1.1  christos   if (client)
    505      1.1  christos     clnt_destroy(client);
    506      1.1  christos   if (exlist)
    507      1.1  christos     xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist);
    508      1.1  christos   if (ok)
    509      1.1  christos     return 0;
    510      1.1  christos   return error;
    511      1.1  christos }
    512      1.1  christos 
    513      1.1  christos 
    514      1.1  christos /*
    515      1.1  christos  * Return true if pref is a directory prefix of dir.
    516      1.1  christos  *
    517      1.1  christos  * XXX TODO:
    518      1.1  christos  * Does not work if pref is "/".
    519      1.1  christos  */
    520      1.1  christos static int
    521      1.1  christos directory_prefix(char *pref, char *dir)
    522      1.1  christos {
    523      1.1  christos   int len = strlen(pref);
    524      1.1  christos 
    525      1.1  christos   if (!NSTREQ(pref, dir, len))
    526      1.1  christos     return FALSE;
    527      1.1  christos   if (dir[len] == '/' || dir[len] == '\0')
    528      1.1  christos     return TRUE;
    529      1.1  christos   return FALSE;
    530      1.1  christos }
    531      1.1  christos 
    532      1.1  christos 
    533      1.1  christos /*
    534      1.1  christos  * Unmount a mount tree
    535      1.1  christos  */
    536      1.1  christos static int
    537      1.1  christos amfs_host_umount(am_node *am, mntfs *mf)
    538      1.1  christos {
    539      1.1  christos   mntlist *ml, *mprev;
    540      1.1  christos   int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
    541      1.1  christos   int xerror = 0;
    542      1.1  christos 
    543      1.1  christos   /*
    544      1.1  christos    * Read the mount list
    545      1.1  christos    */
    546      1.1  christos   mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name);
    547      1.1  christos 
    548      1.1  christos #ifdef MOUNT_TABLE_ON_FILE
    549      1.1  christos   /*
    550      1.1  christos    * Unlock the mount list
    551      1.1  christos    */
    552      1.1  christos   unlock_mntlist();
    553      1.1  christos #endif /* MOUNT_TABLE_ON_FILE */
    554      1.1  christos 
    555      1.1  christos   /*
    556      1.1  christos    * Reverse list...
    557      1.1  christos    */
    558      1.1  christos   ml = mlist;
    559      1.1  christos   mprev = NULL;
    560      1.1  christos   while (ml) {
    561      1.1  christos     mntlist *ml2 = ml->mnext;
    562      1.1  christos     ml->mnext = mprev;
    563      1.1  christos     mprev = ml;
    564      1.1  christos     ml = ml2;
    565      1.1  christos   }
    566      1.1  christos   mlist = mprev;
    567      1.1  christos 
    568      1.1  christos   /*
    569      1.1  christos    * Unmount all filesystems...
    570      1.1  christos    */
    571      1.1  christos   for (ml = mlist; ml && !xerror; ml = ml->mnext) {
    572      1.1  christos     char *dir = ml->mnt->mnt_dir;
    573      1.1  christos     if (directory_prefix(mf->mf_mount, dir)) {
    574      1.1  christos       int error;
    575      1.1  christos       dlog("amfs_host: unmounts %s", dir);
    576      1.1  christos       /*
    577      1.1  christos        * Unmount "dir"
    578      1.1  christos        */
    579      1.1  christos       error = UMOUNT_FS(dir, mnttab_file_name, unmount_flags);
    580      1.1  christos       /*
    581      1.1  christos        * Keep track of errors
    582      1.1  christos        */
    583      1.1  christos       if (error) {
    584      1.1  christos 	/*
    585      1.1  christos 	 * If we have not already set xerror and error is not ENOENT,
    586      1.1  christos 	 * then set xerror equal to error and log it.
    587      1.1  christos 	 * 'xerror' is the return value for this function.
    588      1.1  christos 	 *
    589      1.1  christos 	 * We do not want to pass ENOENT as an error because if the
    590      1.1  christos 	 * directory does not exists our work is done anyway.
    591      1.1  christos 	 */
    592      1.1  christos 	if (!xerror && error != ENOENT)
    593      1.1  christos 	  xerror = error;
    594      1.1  christos 	if (error != EBUSY) {
    595      1.1  christos 	  errno = error;
    596      1.1  christos 	  plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
    597      1.1  christos 	}
    598      1.1  christos       } else {
    599      1.1  christos 	(void) rmdirs(dir);
    600      1.1  christos       }
    601      1.1  christos     }
    602      1.1  christos   }
    603      1.1  christos 
    604      1.1  christos   /*
    605      1.1  christos    * Throw away mount list
    606      1.1  christos    */
    607      1.1  christos   discard_mntlist(mlist);
    608      1.1  christos 
    609      1.1  christos   /*
    610      1.1  christos    * Try to remount, except when we are shutting down.
    611      1.1  christos    */
    612      1.1  christos   if (xerror && amd_state != Finishing) {
    613      1.1  christos     xerror = amfs_host_mount(am, mf);
    614      1.1  christos     if (!xerror) {
    615      1.1  christos       /*
    616      1.1  christos        * Don't log this - it's usually too verbose
    617      1.1  christos        plog(XLOG_INFO, "Remounted host %s", mf->mf_info);
    618      1.1  christos        */
    619      1.1  christos       xerror = EBUSY;
    620      1.1  christos     }
    621      1.1  christos   }
    622      1.1  christos   return xerror;
    623      1.1  christos }
    624      1.1  christos 
    625      1.1  christos 
    626      1.1  christos /*
    627      1.1  christos  * Tell mountd we're done.
    628      1.1  christos  * This is not quite right, because we may still
    629      1.1  christos  * have other filesystems mounted, but the existing
    630      1.1  christos  * mountd protocol is badly broken anyway.
    631      1.1  christos  */
    632      1.1  christos static void
    633      1.1  christos amfs_host_umounted(mntfs *mf)
    634      1.1  christos {
    635      1.1  christos   char *host;
    636      1.1  christos   CLIENT *client;
    637      1.1  christos   enum clnt_stat clnt_stat;
    638      1.1  christos   struct sockaddr_in sin;
    639      1.1  christos   int sock = RPC_ANYSOCK;
    640      1.1  christos   struct timeval tv;
    641      1.1  christos   u_long mnt_version;
    642      1.1  christos 
    643      1.1  christos   if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server)
    644      1.1  christos     return;
    645      1.1  christos 
    646      1.1  christos   /*
    647      1.1  christos    * WebNFS servers shouldn't ever get here.
    648      1.1  christos    */
    649      1.1  christos   if (mf->mf_flags & MFF_WEBNFS) {
    650      1.1  christos     plog(XLOG_ERROR, "amfs_host_umounted: cannot support WebNFS");
    651      1.1  christos     return;
    652      1.1  christos   }
    653      1.1  christos 
    654      1.1  christos   /*
    655      1.1  christos    * Take a copy of the server hostname, address, and NFS version
    656      1.1  christos    * to mount version conversion.
    657      1.1  christos    */
    658      1.1  christos   host = mf->mf_server->fs_host;
    659      1.1  christos   sin = *mf->mf_server->fs_ip;
    660      1.1  christos   plog(XLOG_INFO, "amfs_host_umounted: NFS version %d", (int) mf->mf_server->fs_version);
    661      1.1  christos #ifdef HAVE_FS_NFS3
    662      1.1  christos   if (mf->mf_server->fs_version == NFS_VERSION3)
    663      1.1  christos     mnt_version = AM_MOUNTVERS3;
    664      1.1  christos   else
    665      1.1  christos #endif /* HAVE_FS_NFS3 */
    666      1.1  christos     mnt_version = MOUNTVERS;
    667      1.1  christos 
    668      1.1  christos   /*
    669      1.1  christos    * Create a client attached to mountd
    670      1.1  christos    */
    671      1.1  christos   tv.tv_sec = 10;
    672      1.1  christos   tv.tv_usec = 0;
    673      1.1  christos   client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
    674      1.1  christos   if (client == NULL) {
    675      1.1  christos #ifdef HAVE_CLNT_SPCREATEERROR
    676      1.1  christos     plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
    677      1.1  christos 	 host, clnt_spcreateerror(""));
    678      1.1  christos #else /* not HAVE_CLNT_SPCREATEERROR */
    679      1.1  christos     plog(XLOG_ERROR, "get_mount_client failed for %s", host);
    680      1.1  christos #endif /* not HAVE_CLNT_SPCREATEERROR */
    681      1.1  christos     goto out;
    682      1.1  christos   }
    683      1.1  christos 
    684      1.1  christos   if (!nfs_auth) {
    685      1.1  christos     if (make_nfs_auth())
    686      1.1  christos       goto out;
    687      1.1  christos   }
    688      1.1  christos   client->cl_auth = nfs_auth;
    689      1.1  christos 
    690      1.1  christos   dlog("Unmounting all from %s", host);
    691      1.1  christos 
    692      1.1  christos   clnt_stat = clnt_call(client,
    693      1.1  christos 			MOUNTPROC_UMNTALL,
    694      1.1  christos 			(XDRPROC_T_TYPE) xdr_void,
    695      1.1  christos 			0,
    696      1.1  christos 			(XDRPROC_T_TYPE) xdr_void,
    697      1.1  christos 			0,
    698      1.1  christos 			tv);
    699      1.1  christos   if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_SYSTEMERROR) {
    700      1.1  christos     /* RPC_SYSTEMERROR seems to be returned for no good reason ... */
    701      1.1  christos     const char *msg = clnt_sperrno(clnt_stat);
    702      1.1  christos     plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg);
    703      1.1  christos     goto out;
    704      1.1  christos   }
    705      1.1  christos 
    706      1.1  christos out:
    707      1.1  christos   if (sock != RPC_ANYSOCK)
    708      1.1  christos     (void) amu_close(sock);
    709      1.1  christos   if (client)
    710      1.1  christos     clnt_destroy(client);
    711      1.1  christos }
    712