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