138494Sobrien/*
2174294Sobrien * Copyright (c) 1997-2006 Erez Zadok
338494Sobrien * Copyright (c) 1990 Jan-Simon Pendry
438494Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
538494Sobrien * Copyright (c) 1990 The Regents of the University of California.
638494Sobrien * All rights reserved.
738494Sobrien *
838494Sobrien * This code is derived from software contributed to Berkeley by
938494Sobrien * Jan-Simon Pendry at Imperial College, London.
1038494Sobrien *
1138494Sobrien * Redistribution and use in source and binary forms, with or without
1238494Sobrien * modification, are permitted provided that the following conditions
1338494Sobrien * are met:
1438494Sobrien * 1. Redistributions of source code must retain the above copyright
1538494Sobrien *    notice, this list of conditions and the following disclaimer.
1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1738494Sobrien *    notice, this list of conditions and the following disclaimer in the
1838494Sobrien *    documentation and/or other materials provided with the distribution.
1938494Sobrien * 3. All advertising materials mentioning features or use of this software
2042629Sobrien *    must display the following acknowledgment:
2138494Sobrien *      This product includes software developed by the University of
2238494Sobrien *      California, Berkeley and its contributors.
2338494Sobrien * 4. Neither the name of the University nor the names of its contributors
2438494Sobrien *    may be used to endorse or promote products derived from this software
2538494Sobrien *    without specific prior written permission.
2638494Sobrien *
2738494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2838494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2938494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3038494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3138494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3238494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3338494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3438494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3538494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3638494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3738494Sobrien * SUCH DAMAGE.
3838494Sobrien *
3938494Sobrien *
40174294Sobrien * File: am-utils/amd/amfs_host.c
4138494Sobrien *
4238494Sobrien */
4338494Sobrien
4438494Sobrien/*
4538494Sobrien * NFS host file system.
4638494Sobrien * Mounts all exported filesystems from a given host.
4738494Sobrien * This has now degenerated into a mess but will not
4838494Sobrien * be rewritten.  Amd 6 will support the abstractions
4938494Sobrien * needed to make this work correctly.
5038494Sobrien */
5138494Sobrien
5238494Sobrien#ifdef HAVE_CONFIG_H
5338494Sobrien# include <config.h>
5438494Sobrien#endif /* HAVE_CONFIG_H */
5538494Sobrien#include <am_defs.h>
5638494Sobrien#include <amd.h>
5738494Sobrien
5838494Sobrienstatic char *amfs_host_match(am_opts *fo);
5938494Sobrienstatic int amfs_host_init(mntfs *mf);
60174294Sobrienstatic int amfs_host_mount(am_node *am, mntfs *mf);
61174294Sobrienstatic int amfs_host_umount(am_node *am, mntfs *mf);
62174294Sobrienstatic void amfs_host_umounted(mntfs *mf);
6338494Sobrien
6438494Sobrien/*
6538494Sobrien * Ops structure
6638494Sobrien */
6738494Sobrienam_ops amfs_host_ops =
6838494Sobrien{
6938494Sobrien  "host",
7038494Sobrien  amfs_host_match,
7138494Sobrien  amfs_host_init,
72174294Sobrien  amfs_host_mount,
73174294Sobrien  amfs_host_umount,
74174294Sobrien  amfs_error_lookup_child,
75174294Sobrien  amfs_error_mount_child,
7638494Sobrien  amfs_error_readdir,
7738494Sobrien  0,				/* amfs_host_readlink */
7838494Sobrien  0,				/* amfs_host_mounted */
7938494Sobrien  amfs_host_umounted,
8038494Sobrien  find_nfs_srvr,
81174294Sobrien  0,				/* amfs_host_get_wchan */
82174294Sobrien  FS_MKMNT | FS_BACKGROUND | FS_AMQINFO,
83174294Sobrien#ifdef HAVE_FS_AUTOFS
84174294Sobrien  AUTOFS_HOST_FS_FLAGS,
85174294Sobrien#endif /* HAVE_FS_AUTOFS */
8638494Sobrien};
8738494Sobrien
8838494Sobrien
8938494Sobrien/*
9038494Sobrien * Determine the mount point:
9138494Sobrien *
9238494Sobrien * The next change we put in to better handle PCs.  This is a bit
9338494Sobrien * disgusting, so you'd better sit down.  We change the make_mntpt function
9438494Sobrien * to look for exported file systems without a leading '/'.  If they don't
9538494Sobrien * have a leading '/', we add one.  If the export is 'a:' through 'z:'
9638494Sobrien * (without a leading slash), we change it to 'a%' (or b% or z%).  This
9738494Sobrien * allows the entire PC disk to be mounted.
9838494Sobrien */
9938494Sobrienstatic void
100174294Sobrienmake_mntpt(char *mntpt, size_t l, const exports ex, const char *mf_mount)
10138494Sobrien{
10238494Sobrien  if (ex->ex_dir[0] == '/') {
10338494Sobrien    if (ex->ex_dir[1] == 0)
104174294Sobrien      xstrlcpy(mntpt, mf_mount, l);
10538494Sobrien    else
106174294Sobrien      xsnprintf(mntpt, l, "%s%s", mf_mount, ex->ex_dir);
10738494Sobrien  } else if (ex->ex_dir[0] >= 'a' &&
10838494Sobrien	     ex->ex_dir[0] <= 'z' &&
10938494Sobrien	     ex->ex_dir[1] == ':' &&
11038494Sobrien	     ex->ex_dir[2] == '/' &&
11138494Sobrien	     ex->ex_dir[3] == 0)
112174294Sobrien    xsnprintf(mntpt, l, "%s/%c%%", mf_mount, ex->ex_dir[0]);
11338494Sobrien  else
114174294Sobrien    xsnprintf(mntpt, l, "%s/%s", mf_mount, ex->ex_dir);
11538494Sobrien}
11638494Sobrien
11738494Sobrien
11838494Sobrien/*
11938494Sobrien * Execute needs the same as NFS plus a helper command
12038494Sobrien */
12138494Sobrienstatic char *
12238494Sobrienamfs_host_match(am_opts *fo)
12338494Sobrien{
12438494Sobrien  extern am_ops nfs_ops;
12538494Sobrien
12638494Sobrien  /*
12738494Sobrien   * Make sure rfs is specified to keep nfs_match happy...
12838494Sobrien   */
12938494Sobrien  if (!fo->opt_rfs)
13038494Sobrien    fo->opt_rfs = "/";
13138494Sobrien
13238494Sobrien  return (*nfs_ops.fs_match) (fo);
13338494Sobrien}
13438494Sobrien
13538494Sobrien
13638494Sobrienstatic int
13738494Sobrienamfs_host_init(mntfs *mf)
13838494Sobrien{
139174294Sobrien  u_short mountd_port;
14038494Sobrien
14138494Sobrien  if (strchr(mf->mf_info, ':') == 0)
14238494Sobrien    return ENOENT;
14338494Sobrien
14438494Sobrien  /*
14538494Sobrien   * This is primarily to schedule a wakeup so that as soon
14638494Sobrien   * as our fileserver is ready, we can continue setting up
14738494Sobrien   * the host filesystem.  If we don't do this, the standard
14838494Sobrien   * amfs_auto code will set up a fileserver structure, but it will
14938494Sobrien   * have to wait for another nfs request from the client to come
15038494Sobrien   * in before finishing.  Our way is faster since we don't have
15138494Sobrien   * to wait for the client to resend its request (which could
15238494Sobrien   * take a second or two).
15338494Sobrien   */
15438494Sobrien  /*
15538494Sobrien   * First, we find the fileserver for this mntfs and then call
156174294Sobrien   * get_mountd_port with our mntfs passed as the wait channel.
157174294Sobrien   * get_mountd_port will check some things and then schedule
15838494Sobrien   * it so that when the fileserver is ready, a wakeup is done
159174294Sobrien   * on this mntfs.   amfs_cont() is already sleeping on this mntfs
160174294Sobrien   * so as soon as that wakeup happens amfs_cont() is called and
16138494Sobrien   * this mount is retried.
16238494Sobrien   */
163174294Sobrien  if (mf->mf_server)
16438494Sobrien    /*
16538494Sobrien     * We don't really care if there's an error returned.
16638494Sobrien     * Since this is just to help speed things along, the
16738494Sobrien     * error will get handled properly elsewhere.
16838494Sobrien     */
169174294Sobrien    get_mountd_port(mf->mf_server, &mountd_port, get_mntfs_wchan(mf));
17038494Sobrien
17138494Sobrien  return 0;
17238494Sobrien}
17338494Sobrien
17438494Sobrien
17538494Sobrienstatic int
176174294Sobriendo_mount(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf)
17738494Sobrien{
17838494Sobrien  struct stat stb;
17938494Sobrien
180174294Sobrien  dlog("amfs_host: mounting fs %s on %s\n", fs_name, mntdir);
18138494Sobrien
182174294Sobrien  (void) mkdirs(mntdir, 0555);
183174294Sobrien  if (stat(mntdir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) {
184174294Sobrien    plog(XLOG_ERROR, "No mount point for %s - skipping", mntdir);
18538494Sobrien    return ENOENT;
18638494Sobrien  }
18738494Sobrien
188174294Sobrien  return mount_nfs_fh(fhp, mntdir, fs_name, mf);
18938494Sobrien}
19038494Sobrien
19138494Sobrien
19238494Sobrienstatic int
19338494Sobriensortfun(const voidp x, const voidp y)
19438494Sobrien{
19538494Sobrien  exports *a = (exports *) x;
19638494Sobrien  exports *b = (exports *) y;
19738494Sobrien
19838494Sobrien  return strcmp((*a)->ex_dir, (*b)->ex_dir);
19938494Sobrien}
20038494Sobrien
20138494Sobrien
20238494Sobrien/*
20338494Sobrien * Get filehandle
20438494Sobrien */
20538494Sobrienstatic int
20682794Sobrienfetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_version)
20738494Sobrien{
20838494Sobrien  struct timeval tv;
20938494Sobrien  enum clnt_stat clnt_stat;
210174294Sobrien  struct fhstatus res;
211174294Sobrien#ifdef HAVE_FS_NFS3
212174294Sobrien  struct am_mountres3 res3;
213174294Sobrien#endif /* HAVE_FS_NFS3 */
21438494Sobrien
21538494Sobrien  /*
21638494Sobrien   * Pick a number, any number...
21738494Sobrien   */
21838494Sobrien  tv.tv_sec = 20;
21938494Sobrien  tv.tv_usec = 0;
22038494Sobrien
22138494Sobrien  dlog("Fetching fhandle for %s", dir);
22238494Sobrien
22338494Sobrien  /*
22438494Sobrien   * Call the mount daemon on the remote host to
22538494Sobrien   * get the filehandle.  Use NFS version specific call.
22638494Sobrien   */
22738494Sobrien
22851292Sobrien  plog(XLOG_INFO, "fetch_fhandle: NFS version %d", (int) nfs_version);
22938494Sobrien#ifdef HAVE_FS_NFS3
23038494Sobrien  if (nfs_version == NFS_VERSION3) {
231174294Sobrien    memset((char *) &res3, 0, sizeof(res3));
23238494Sobrien    clnt_stat = clnt_call(client,
23338494Sobrien			  MOUNTPROC_MNT,
23438494Sobrien			  (XDRPROC_T_TYPE) xdr_dirpath,
23538494Sobrien			  (SVC_IN_ARG_TYPE) &dir,
236174294Sobrien			  (XDRPROC_T_TYPE) xdr_am_mountres3,
237174294Sobrien			  (SVC_IN_ARG_TYPE) &res3,
23838494Sobrien			  tv);
23938494Sobrien    if (clnt_stat != RPC_SUCCESS) {
24038494Sobrien      plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
24138494Sobrien      return EIO;
24238494Sobrien    }
24338494Sobrien    /* Check the status of the filehandle */
244174294Sobrien    if ((errno = res3.fhs_status)) {
24538494Sobrien      dlog("fhandle fetch for mount version 3 failed: %m");
24638494Sobrien      return errno;
24738494Sobrien    }
248174294Sobrien    memset((voidp) &fhp->v3, 0, sizeof(am_nfs_fh3));
249174294Sobrien    fhp->v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len;
250174294Sobrien    memmove(fhp->v3.am_fh3_data,
251174294Sobrien	    res3.mountres3_u.mountinfo.fhandle.fhandle3_val,
252174294Sobrien	    fhp->v3.am_fh3_length);
25338494Sobrien  } else {			/* not NFS_VERSION3 mount */
25438494Sobrien#endif /* HAVE_FS_NFS3 */
25538494Sobrien    clnt_stat = clnt_call(client,
25638494Sobrien			  MOUNTPROC_MNT,
25738494Sobrien			  (XDRPROC_T_TYPE) xdr_dirpath,
25838494Sobrien			  (SVC_IN_ARG_TYPE) &dir,
25938494Sobrien			  (XDRPROC_T_TYPE) xdr_fhstatus,
260174294Sobrien			  (SVC_IN_ARG_TYPE) &res,
26138494Sobrien			  tv);
26238494Sobrien    if (clnt_stat != RPC_SUCCESS) {
26382794Sobrien      plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
26438494Sobrien      return EIO;
26538494Sobrien    }
26638494Sobrien    /* Check status of filehandle */
267174294Sobrien    if (res.fhs_status) {
268174294Sobrien      errno = res.fhs_status;
26938494Sobrien      dlog("fhandle fetch for mount version 1 failed: %m");
27038494Sobrien      return errno;
27138494Sobrien    }
272174294Sobrien    memmove(&fhp->v2, &res.fhs_fh, NFS_FHSIZE);
27338494Sobrien#ifdef HAVE_FS_NFS3
27438494Sobrien  } /* end of "if (nfs_version == NFS_VERSION3)" statement */
27538494Sobrien#endif /* HAVE_FS_NFS3 */
27638494Sobrien
27738494Sobrien  /* all is well */
27838494Sobrien  return 0;
27938494Sobrien}
28038494Sobrien
28138494Sobrien
28238494Sobrien/*
28338494Sobrien * Scan mount table to see if something already mounted
28438494Sobrien */
28538494Sobrienstatic int
28638494Sobrienalready_mounted(mntlist *mlist, char *dir)
28738494Sobrien{
28838494Sobrien  mntlist *ml;
28938494Sobrien
29038494Sobrien  for (ml = mlist; ml; ml = ml->mnext)
29138494Sobrien    if (STREQ(ml->mnt->mnt_dir, dir))
29238494Sobrien      return 1;
29338494Sobrien  return 0;
29438494Sobrien}
29538494Sobrien
29638494Sobrien
29738494Sobrienstatic int
298174294Sobrienamfs_host_mount(am_node *am, mntfs *mf)
29938494Sobrien{
30038494Sobrien  struct timeval tv2;
30138494Sobrien  CLIENT *client;
30238494Sobrien  enum clnt_stat clnt_stat;
30338494Sobrien  int n_export;
30438494Sobrien  int j, k;
30538494Sobrien  exports exlist = 0, ex;
30638494Sobrien  exports *ep = 0;
30738494Sobrien  am_nfs_handle_t *fp = 0;
30838494Sobrien  char *host;
30938494Sobrien  int error = 0;
31038494Sobrien  struct sockaddr_in sin;
31138494Sobrien  int sock = RPC_ANYSOCK;
31238494Sobrien  int ok = FALSE;
31338494Sobrien  mntlist *mlist;
31438494Sobrien  char fs_name[MAXPATHLEN], *rfs_dir;
31538494Sobrien  char mntpt[MAXPATHLEN];
31638494Sobrien  struct timeval tv;
31738494Sobrien  u_long mnt_version;
31838494Sobrien
31938494Sobrien  /*
320174294Sobrien   * WebNFS servers don't necessarily run mountd.
321174294Sobrien   */
322174294Sobrien  if (mf->mf_flags & MFF_WEBNFS) {
323174294Sobrien    plog(XLOG_ERROR, "amfs_host_mount: cannot support WebNFS");
324174294Sobrien    return EIO;
325174294Sobrien  }
326174294Sobrien
327174294Sobrien  /*
32838494Sobrien   * Read the mount list
32938494Sobrien   */
33038494Sobrien  mlist = read_mtab(mf->mf_mount, mnttab_file_name);
33138494Sobrien
33238494Sobrien#ifdef MOUNT_TABLE_ON_FILE
33338494Sobrien  /*
33438494Sobrien   * Unlock the mount list
33538494Sobrien   */
33638494Sobrien  unlock_mntlist();
33738494Sobrien#endif /* MOUNT_TABLE_ON_FILE */
33838494Sobrien
33938494Sobrien  /*
34038494Sobrien   * Take a copy of the server hostname, address, and nfs version
34138494Sobrien   * to mount version conversion.
34238494Sobrien   */
34338494Sobrien  host = mf->mf_server->fs_host;
34438494Sobrien  sin = *mf->mf_server->fs_ip;
345174294Sobrien  plog(XLOG_INFO, "amfs_host_mount: NFS version %d", (int) mf->mf_server->fs_version);
34638494Sobrien#ifdef HAVE_FS_NFS3
34738494Sobrien  if (mf->mf_server->fs_version == NFS_VERSION3)
348174294Sobrien    mnt_version = AM_MOUNTVERS3;
34938494Sobrien  else
35038494Sobrien#endif /* HAVE_FS_NFS3 */
35138494Sobrien    mnt_version = MOUNTVERS;
35238494Sobrien
35338494Sobrien  /*
35438494Sobrien   * The original 10 second per try timeout is WAY too large, especially
35538494Sobrien   * if we're only waiting 10 or 20 seconds max for the response.
35638494Sobrien   * That would mean we'd try only once in 10 seconds, and we could
35742629Sobrien   * lose the transmit or receive packet, and never try again.
35838494Sobrien   * A 2-second per try timeout here is much more reasonable.
35938494Sobrien   * 09/28/92 Mike Mitchell, mcm@unx.sas.com
36038494Sobrien   */
36138494Sobrien  tv.tv_sec = 2;
36238494Sobrien  tv.tv_usec = 0;
36338494Sobrien
36438494Sobrien  /*
36538494Sobrien   * Create a client attached to mountd
36638494Sobrien   */
36738494Sobrien  client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
36838494Sobrien  if (client == NULL) {
36938494Sobrien#ifdef HAVE_CLNT_SPCREATEERROR
37038494Sobrien    plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
37138494Sobrien	 host, clnt_spcreateerror(""));
37238494Sobrien#else /* not HAVE_CLNT_SPCREATEERROR */
37338494Sobrien    plog(XLOG_ERROR, "get_mount_client failed for %s", host);
37438494Sobrien#endif /* not HAVE_CLNT_SPCREATEERROR */
37538494Sobrien    error = EIO;
37638494Sobrien    goto out;
37738494Sobrien  }
37838494Sobrien  if (!nfs_auth) {
37938494Sobrien    error = make_nfs_auth();
38038494Sobrien    if (error)
38138494Sobrien      goto out;
38238494Sobrien  }
38338494Sobrien  client->cl_auth = nfs_auth;
38438494Sobrien
38538494Sobrien  dlog("Fetching export list from %s", host);
38638494Sobrien
38738494Sobrien  /*
38838494Sobrien   * Fetch the export list
38938494Sobrien   */
39038494Sobrien  tv2.tv_sec = 10;
39138494Sobrien  tv2.tv_usec = 0;
39238494Sobrien  clnt_stat = clnt_call(client,
39338494Sobrien			MOUNTPROC_EXPORT,
39438494Sobrien			(XDRPROC_T_TYPE) xdr_void,
39538494Sobrien			0,
39638494Sobrien			(XDRPROC_T_TYPE) xdr_exports,
39738494Sobrien			(SVC_IN_ARG_TYPE) & exlist,
39838494Sobrien			tv2);
39938494Sobrien  if (clnt_stat != RPC_SUCCESS) {
40041142Sobrien    const char *msg = clnt_sperrno(clnt_stat);
401174294Sobrien    plog(XLOG_ERROR, "host_mount rpc failed: %s", msg);
40238494Sobrien    /* clnt_perror(client, "rpc"); */
40338494Sobrien    error = EIO;
40438494Sobrien    goto out;
40538494Sobrien  }
40638494Sobrien
40738494Sobrien  /*
40838494Sobrien   * Figure out how many exports were returned
40938494Sobrien   */
41038494Sobrien  for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) {
41138494Sobrien    n_export++;
41238494Sobrien  }
41338494Sobrien
41438494Sobrien  /*
41538494Sobrien   * Allocate an array of pointers into the list
41638494Sobrien   * so that they can be sorted.  If the filesystem
41738494Sobrien   * is already mounted then ignore it.
41838494Sobrien   */
41938494Sobrien  ep = (exports *) xmalloc(n_export * sizeof(exports));
42038494Sobrien  for (j = 0, ex = exlist; ex; ex = ex->ex_next) {
421174294Sobrien    make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount);
42282794Sobrien    if (already_mounted(mlist, mntpt))
42382794Sobrien      /* we have at least one mounted f/s, so don't fail the mount */
42482794Sobrien      ok = TRUE;
42582794Sobrien    else
42638494Sobrien      ep[j++] = ex;
42738494Sobrien  }
42838494Sobrien  n_export = j;
42938494Sobrien
43038494Sobrien  /*
43138494Sobrien   * Sort into order.
43238494Sobrien   * This way the mounts are done in order down the tree,
43338494Sobrien   * instead of any random order returned by the mount
43438494Sobrien   * daemon (the protocol doesn't specify...).
43538494Sobrien   */
43638494Sobrien  qsort(ep, n_export, sizeof(exports), sortfun);
43738494Sobrien
43838494Sobrien  /*
43938494Sobrien   * Allocate an array of filehandles
44038494Sobrien   */
44138494Sobrien  fp = (am_nfs_handle_t *) xmalloc(n_export * sizeof(am_nfs_handle_t));
44238494Sobrien
44338494Sobrien  /*
44438494Sobrien   * Try to obtain filehandles for each directory.
44538494Sobrien   * If a fetch fails then just zero out the array
44638494Sobrien   * reference but discard the error.
44738494Sobrien   */
44838494Sobrien  for (j = k = 0; j < n_export; j++) {
44938494Sobrien    /* Check and avoid a duplicated export entry */
45038494Sobrien    if (j > k && ep[k] && STREQ(ep[j]->ex_dir, ep[k]->ex_dir)) {
45138494Sobrien      dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir);
45238494Sobrien      ep[j] = 0;
45338494Sobrien    } else {
45438494Sobrien      k = j;
45538494Sobrien      error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j],
45638494Sobrien			    mf->mf_server->fs_version);
45738494Sobrien      if (error)
45838494Sobrien	ep[j] = 0;
45938494Sobrien    }
46038494Sobrien  }
46138494Sobrien
46238494Sobrien  /*
46338494Sobrien   * Mount each filesystem for which we have a filehandle.
46438494Sobrien   * If any of the mounts succeed then mark "ok" and return
46538494Sobrien   * error code 0 at the end.  If they all fail then return
46638494Sobrien   * the last error code.
46738494Sobrien   */
468174294Sobrien  xstrlcpy(fs_name, mf->mf_info, MAXPATHLEN);
46938494Sobrien  if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) {
470174294Sobrien    plog(XLOG_FATAL, "amfs_host_mount: mf_info has no colon");
47138494Sobrien    error = EINVAL;
47238494Sobrien    goto out;
47338494Sobrien  }
47438494Sobrien  ++rfs_dir;
47538494Sobrien  for (j = 0; j < n_export; j++) {
47638494Sobrien    ex = ep[j];
47738494Sobrien    if (ex) {
478174294Sobrien      /*
479174294Sobrien       * Note: the sizeof space left in rfs_dir is what's left in fs_name
480174294Sobrien       * after strchr() above returned a pointer _inside_ fs_name.  The
481174294Sobrien       * calculation below also takes into account that rfs_dir was
482174294Sobrien       * incremented by the ++ above.
483174294Sobrien       */
484174294Sobrien      xstrlcpy(rfs_dir, ex->ex_dir, sizeof(fs_name) - (rfs_dir - fs_name));
485174294Sobrien      make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount);
486174294Sobrien      if (do_mount(&fp[j], mntpt, fs_name, mf) == 0)
48738494Sobrien	ok = TRUE;
48838494Sobrien    }
48938494Sobrien  }
49038494Sobrien
49138494Sobrien  /*
49238494Sobrien   * Clean up and exit
49338494Sobrien   */
49438494Sobrienout:
49538494Sobrien  discard_mntlist(mlist);
49638494Sobrien  if (ep)
49738494Sobrien    XFREE(ep);
49838494Sobrien  if (fp)
49938494Sobrien    XFREE(fp);
50038494Sobrien  if (sock != RPC_ANYSOCK)
50138494Sobrien    (void) amu_close(sock);
50238494Sobrien  if (client)
50338494Sobrien    clnt_destroy(client);
50438494Sobrien  if (exlist)
50538494Sobrien    xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist);
50638494Sobrien  if (ok)
50738494Sobrien    return 0;
50838494Sobrien  return error;
50938494Sobrien}
51038494Sobrien
51138494Sobrien
51238494Sobrien/*
51338494Sobrien * Return true if pref is a directory prefix of dir.
51438494Sobrien *
51538494Sobrien * XXX TODO:
51638494Sobrien * Does not work if pref is "/".
51738494Sobrien */
51838494Sobrienstatic int
51938494Sobriendirectory_prefix(char *pref, char *dir)
52038494Sobrien{
52138494Sobrien  int len = strlen(pref);
52238494Sobrien
52338494Sobrien  if (!NSTREQ(pref, dir, len))
52438494Sobrien    return FALSE;
52538494Sobrien  if (dir[len] == '/' || dir[len] == '\0')
52638494Sobrien    return TRUE;
52738494Sobrien  return FALSE;
52838494Sobrien}
52938494Sobrien
53038494Sobrien
53138494Sobrien/*
53238494Sobrien * Unmount a mount tree
53338494Sobrien */
53438494Sobrienstatic int
535174294Sobrienamfs_host_umount(am_node *am, mntfs *mf)
53638494Sobrien{
53738494Sobrien  mntlist *ml, *mprev;
538174294Sobrien  int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
53938494Sobrien  int xerror = 0;
54038494Sobrien
54138494Sobrien  /*
54238494Sobrien   * Read the mount list
54338494Sobrien   */
54438494Sobrien  mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name);
54538494Sobrien
54638494Sobrien#ifdef MOUNT_TABLE_ON_FILE
54738494Sobrien  /*
54838494Sobrien   * Unlock the mount list
54938494Sobrien   */
55038494Sobrien  unlock_mntlist();
55138494Sobrien#endif /* MOUNT_TABLE_ON_FILE */
55238494Sobrien
55338494Sobrien  /*
55438494Sobrien   * Reverse list...
55538494Sobrien   */
55638494Sobrien  ml = mlist;
55738494Sobrien  mprev = 0;
55838494Sobrien  while (ml) {
55938494Sobrien    mntlist *ml2 = ml->mnext;
56038494Sobrien    ml->mnext = mprev;
56138494Sobrien    mprev = ml;
56238494Sobrien    ml = ml2;
56338494Sobrien  }
56438494Sobrien  mlist = mprev;
56538494Sobrien
56638494Sobrien  /*
56738494Sobrien   * Unmount all filesystems...
56838494Sobrien   */
56938494Sobrien  for (ml = mlist; ml && !xerror; ml = ml->mnext) {
57038494Sobrien    char *dir = ml->mnt->mnt_dir;
57138494Sobrien    if (directory_prefix(mf->mf_mount, dir)) {
57238494Sobrien      int error;
57338494Sobrien      dlog("amfs_host: unmounts %s", dir);
57438494Sobrien      /*
57538494Sobrien       * Unmount "dir"
57638494Sobrien       */
577174294Sobrien      error = UMOUNT_FS(dir, mnttab_file_name, unmount_flags);
57838494Sobrien      /*
57938494Sobrien       * Keep track of errors
58038494Sobrien       */
58138494Sobrien      if (error) {
582174294Sobrien	/*
583174294Sobrien	 * If we have not already set xerror and error is not ENOENT,
584174294Sobrien	 * then set xerror equal to error and log it.
585174294Sobrien	 * 'xerror' is the return value for this function.
586174294Sobrien	 *
587174294Sobrien	 * We do not want to pass ENOENT as an error because if the
588174294Sobrien	 * directory does not exists our work is done anyway.
589174294Sobrien	 */
590174294Sobrien	if (!xerror && error != ENOENT)
59138494Sobrien	  xerror = error;
59238494Sobrien	if (error != EBUSY) {
59338494Sobrien	  errno = error;
59438494Sobrien	  plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
59538494Sobrien	}
59638494Sobrien      } else {
59738494Sobrien	(void) rmdirs(dir);
59838494Sobrien      }
59938494Sobrien    }
60038494Sobrien  }
60138494Sobrien
60238494Sobrien  /*
60338494Sobrien   * Throw away mount list
60438494Sobrien   */
60538494Sobrien  discard_mntlist(mlist);
60638494Sobrien
60738494Sobrien  /*
60838494Sobrien   * Try to remount, except when we are shutting down.
60938494Sobrien   */
61038494Sobrien  if (xerror && amd_state != Finishing) {
611174294Sobrien    xerror = amfs_host_mount(am, mf);
61238494Sobrien    if (!xerror) {
61338494Sobrien      /*
61438494Sobrien       * Don't log this - it's usually too verbose
61538494Sobrien       plog(XLOG_INFO, "Remounted host %s", mf->mf_info);
61638494Sobrien       */
61738494Sobrien      xerror = EBUSY;
61838494Sobrien    }
61938494Sobrien  }
62038494Sobrien  return xerror;
62138494Sobrien}
62238494Sobrien
62338494Sobrien
62438494Sobrien/*
62538494Sobrien * Tell mountd we're done.
62638494Sobrien * This is not quite right, because we may still
62738494Sobrien * have other filesystems mounted, but the existing
62838494Sobrien * mountd protocol is badly broken anyway.
62938494Sobrien */
63038494Sobrienstatic void
631174294Sobrienamfs_host_umounted(mntfs *mf)
63238494Sobrien{
63338494Sobrien  char *host;
63438494Sobrien  CLIENT *client;
63538494Sobrien  enum clnt_stat clnt_stat;
63638494Sobrien  struct sockaddr_in sin;
63738494Sobrien  int sock = RPC_ANYSOCK;
63838494Sobrien  struct timeval tv;
63938494Sobrien  u_long mnt_version;
64038494Sobrien
64138494Sobrien  if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server)
64238494Sobrien    return;
64338494Sobrien
64438494Sobrien  /*
645174294Sobrien   * WebNFS servers shouldn't ever get here.
646174294Sobrien   */
647174294Sobrien  if (mf->mf_flags & MFF_WEBNFS) {
648174294Sobrien    plog(XLOG_ERROR, "amfs_host_umounted: cannot support WebNFS");
649174294Sobrien    return;
650174294Sobrien  }
651174294Sobrien
652174294Sobrien  /*
65338494Sobrien   * Take a copy of the server hostname, address, and NFS version
65438494Sobrien   * to mount version conversion.
65538494Sobrien   */
65638494Sobrien  host = mf->mf_server->fs_host;
65738494Sobrien  sin = *mf->mf_server->fs_ip;
65851292Sobrien  plog(XLOG_INFO, "amfs_host_umounted: NFS version %d", (int) mf->mf_server->fs_version);
65938494Sobrien#ifdef HAVE_FS_NFS3
66038494Sobrien  if (mf->mf_server->fs_version == NFS_VERSION3)
661174294Sobrien    mnt_version = AM_MOUNTVERS3;
66238494Sobrien  else
66338494Sobrien#endif /* HAVE_FS_NFS3 */
66438494Sobrien    mnt_version = MOUNTVERS;
66538494Sobrien
66638494Sobrien  /*
66738494Sobrien   * Create a client attached to mountd
66838494Sobrien   */
66938494Sobrien  tv.tv_sec = 10;
67038494Sobrien  tv.tv_usec = 0;
67138494Sobrien  client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
67238494Sobrien  if (client == NULL) {
67338494Sobrien#ifdef HAVE_CLNT_SPCREATEERROR
67438494Sobrien    plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
67538494Sobrien	 host, clnt_spcreateerror(""));
67638494Sobrien#else /* not HAVE_CLNT_SPCREATEERROR */
67738494Sobrien    plog(XLOG_ERROR, "get_mount_client failed for %s", host);
67838494Sobrien#endif /* not HAVE_CLNT_SPCREATEERROR */
67938494Sobrien    goto out;
68038494Sobrien  }
68138494Sobrien
68238494Sobrien  if (!nfs_auth) {
68338494Sobrien    if (make_nfs_auth())
68438494Sobrien      goto out;
68538494Sobrien  }
68638494Sobrien  client->cl_auth = nfs_auth;
68738494Sobrien
68838494Sobrien  dlog("Unmounting all from %s", host);
68938494Sobrien
69038494Sobrien  clnt_stat = clnt_call(client,
69138494Sobrien			MOUNTPROC_UMNTALL,
69238494Sobrien			(XDRPROC_T_TYPE) xdr_void,
69338494Sobrien			0,
69438494Sobrien			(XDRPROC_T_TYPE) xdr_void,
69538494Sobrien			0,
69638494Sobrien			tv);
69738494Sobrien  if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_SYSTEMERROR) {
69838494Sobrien    /* RPC_SYSTEMERROR seems to be returned for no good reason ... */
69941142Sobrien    const char *msg = clnt_sperrno(clnt_stat);
70051292Sobrien    plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg);
70138494Sobrien    goto out;
70238494Sobrien  }
70338494Sobrien
70438494Sobrienout:
70538494Sobrien  if (sock != RPC_ANYSOCK)
70638494Sobrien    (void) amu_close(sock);
70738494Sobrien  if (client)
70838494Sobrien    clnt_destroy(client);
70938494Sobrien}
710