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