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