amfs_host.c revision 267654
1181834Sroberto/*
2285612Sdelphij * Copyright (c) 1997-2006 Erez Zadok
3181834Sroberto * Copyright (c) 1990 Jan-Simon Pendry
4285612Sdelphij * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5285612Sdelphij * Copyright (c) 1990 The Regents of the University of California.
6181834Sroberto * All rights reserved.
7181834Sroberto *
8181834Sroberto * This code is derived from software contributed to Berkeley by
9181834Sroberto * Jan-Simon Pendry at Imperial College, London.
10181834Sroberto *
11181834Sroberto * Redistribution and use in source and binary forms, with or without
12285612Sdelphij * modification, are permitted provided that the following conditions
13181834Sroberto * are met:
14285612Sdelphij * 1. Redistributions of source code must retain the above copyright
15285612Sdelphij *    notice, this list of conditions and the following disclaimer.
16285612Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
17285612Sdelphij *    notice, this list of conditions and the following disclaimer in the
18285612Sdelphij *    documentation and/or other materials provided with the distribution.
19285612Sdelphij * 3. All advertising materials mentioning features or use of this software
20285612Sdelphij *    must display the following acknowledgment:
21285612Sdelphij *      This product includes software developed by the University of
22285612Sdelphij *      California, Berkeley and its contributors.
23285612Sdelphij * 4. Neither the name of the University nor the names of its contributors
24285612Sdelphij *    may be used to endorse or promote products derived from this software
25285612Sdelphij *    without specific prior written permission.
26285612Sdelphij *
27285612Sdelphij * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28285612Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29285612Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30285612Sdelphij * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31285612Sdelphij * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32285612Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33181834Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34181834Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35285612Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36285612Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37285612Sdelphij * SUCH DAMAGE.
38285612Sdelphij *
39285612Sdelphij *
40285612Sdelphij * File: am-utils/amd/amfs_host.c
41181834Sroberto *
42285612Sdelphij */
43181834Sroberto
44285612Sdelphij/*
45285612Sdelphij * NFS host file system.
46285612Sdelphij * Mounts all exported filesystems from a given host.
47285612Sdelphij * This has now degenerated into a mess but will not
48285612Sdelphij * be rewritten.  Amd 6 will support the abstractions
49285612Sdelphij * needed to make this work correctly.
50285612Sdelphij */
51285612Sdelphij
52285612Sdelphij#ifdef HAVE_CONFIG_H
53285612Sdelphij# include <config.h>
54181834Sroberto#endif /* HAVE_CONFIG_H */
55285612Sdelphij#include <am_defs.h>
56285612Sdelphij#include <amd.h>
57285612Sdelphij
58285612Sdelphijstatic char *amfs_host_match(am_opts *fo);
59285612Sdelphijstatic int amfs_host_init(mntfs *mf);
60181834Srobertostatic int amfs_host_mount(am_node *am, mntfs *mf);
61285612Sdelphijstatic int amfs_host_umount(am_node *am, mntfs *mf);
62285612Sdelphijstatic void amfs_host_umounted(mntfs *mf);
63285612Sdelphij
64181834Sroberto/*
65285612Sdelphij * Ops structure
66285612Sdelphij */
67285612Sdelphijam_ops amfs_host_ops =
68285612Sdelphij{
69285612Sdelphij  "host",
70181834Sroberto  amfs_host_match,
71285612Sdelphij  amfs_host_init,
72285612Sdelphij  amfs_host_mount,
73285612Sdelphij  amfs_host_umount,
74285612Sdelphij  amfs_error_lookup_child,
75285612Sdelphij  amfs_error_mount_child,
76285612Sdelphij  amfs_error_readdir,
77285612Sdelphij  0,				/* amfs_host_readlink */
78285612Sdelphij  0,				/* amfs_host_mounted */
79285612Sdelphij  amfs_host_umounted,
80285612Sdelphij  find_nfs_srvr,
81285612Sdelphij  0,				/* amfs_host_get_wchan */
82285612Sdelphij  FS_MKMNT | FS_BACKGROUND | FS_AMQINFO,
83285612Sdelphij#ifdef HAVE_FS_AUTOFS
84285612Sdelphij  AUTOFS_HOST_FS_FLAGS,
85285612Sdelphij#endif /* HAVE_FS_AUTOFS */
86285612Sdelphij};
87285612Sdelphij
88285612Sdelphij
89285612Sdelphij/*
90285612Sdelphij * Determine the mount point:
91285612Sdelphij *
92285612Sdelphij * The next change we put in to better handle PCs.  This is a bit
93285612Sdelphij * disgusting, so you'd better sit down.  We change the make_mntpt function
94181834Sroberto * to look for exported file systems without a leading '/'.  If they don't
95181834Sroberto * have a leading '/', we add one.  If the export is 'a:' through 'z:'
96181834Sroberto * (without a leading slash), we change it to 'a%' (or b% or z%).  This
97181834Sroberto * allows the entire PC disk to be mounted.
98181834Sroberto */
99181834Srobertostatic void
100181834Srobertomake_mntpt(char *mntpt, size_t l, const exports ex, const char *mf_mount)
101285612Sdelphij{
102181834Sroberto  if (ex->ex_dir[0] == '/') {
103285612Sdelphij    if (ex->ex_dir[1] == 0)
104285612Sdelphij      xstrlcpy(mntpt, mf_mount, l);
105181834Sroberto    else
106181834Sroberto      xsnprintf(mntpt, l, "%s%s", mf_mount, ex->ex_dir);
107181834Sroberto  } else if (ex->ex_dir[0] >= 'a' &&
108285612Sdelphij	     ex->ex_dir[0] <= 'z' &&
109285612Sdelphij	     ex->ex_dir[1] == ':' &&
110181834Sroberto	     ex->ex_dir[2] == '/' &&
111285612Sdelphij	     ex->ex_dir[3] == 0)
112285612Sdelphij    xsnprintf(mntpt, l, "%s/%c%%", mf_mount, ex->ex_dir[0]);
113285612Sdelphij  else
114285612Sdelphij    xsnprintf(mntpt, l, "%s/%s", mf_mount, ex->ex_dir);
115285612Sdelphij}
116285612Sdelphij
117285612Sdelphij
118285612Sdelphij/*
119285612Sdelphij * Execute needs the same as NFS plus a helper command
120285612Sdelphij */
121285612Sdelphijstatic char *
122285612Sdelphijamfs_host_match(am_opts *fo)
123285612Sdelphij{
124181834Sroberto  extern am_ops nfs_ops;
125285612Sdelphij
126285612Sdelphij  /*
127285612Sdelphij   * Make sure rfs is specified to keep nfs_match happy...
128285612Sdelphij   */
129285612Sdelphij  if (!fo->opt_rfs)
130181834Sroberto    fo->opt_rfs = "/";
131285612Sdelphij
132285612Sdelphij  return (*nfs_ops.fs_match) (fo);
133285612Sdelphij}
134285612Sdelphij
135285612Sdelphij
136285612Sdelphijstatic int
137285612Sdelphijamfs_host_init(mntfs *mf)
138285612Sdelphij{
139285612Sdelphij  u_short mountd_port;
140285612Sdelphij
141285612Sdelphij  if (strchr(mf->mf_info, ':') == 0)
142285612Sdelphij    return ENOENT;
143285612Sdelphij
144181834Sroberto  /*
145181834Sroberto   * This is primarily to schedule a wakeup so that as soon
146285612Sdelphij   * as our fileserver is ready, we can continue setting up
147285612Sdelphij   * the host filesystem.  If we don't do this, the standard
148285612Sdelphij   * amfs_auto code will set up a fileserver structure, but it will
149181834Sroberto   * have to wait for another nfs request from the client to come
150285612Sdelphij   * in before finishing.  Our way is faster since we don't have
151285612Sdelphij   * to wait for the client to resend its request (which could
152181834Sroberto   * take a second or two).
153285612Sdelphij   */
154285612Sdelphij  /*
155285612Sdelphij   * First, we find the fileserver for this mntfs and then call
156285612Sdelphij   * get_mountd_port with our mntfs passed as the wait channel.
157285612Sdelphij   * get_mountd_port will check some things and then schedule
158285612Sdelphij   * it so that when the fileserver is ready, a wakeup is done
159181834Sroberto   * on this mntfs.   amfs_cont() is already sleeping on this mntfs
160181834Sroberto   * so as soon as that wakeup happens amfs_cont() is called and
161181834Sroberto   * this mount is retried.
162285612Sdelphij   */
163285612Sdelphij  if (mf->mf_server)
164181834Sroberto    /*
165181834Sroberto     * We don't really care if there's an error returned.
166285612Sdelphij     * Since this is just to help speed things along, the
167285612Sdelphij     * error will get handled properly elsewhere.
168285612Sdelphij     */
169285612Sdelphij    get_mountd_port(mf->mf_server, &mountd_port, get_mntfs_wchan(mf));
170285612Sdelphij
171285612Sdelphij  return 0;
172285612Sdelphij}
173285612Sdelphij
174285612Sdelphij
175181834Srobertostatic int
176285612Sdelphijdo_mount(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf)
177285612Sdelphij{
178285612Sdelphij  struct stat stb;
179285612Sdelphij
180285612Sdelphij  dlog("amfs_host: mounting fs %s on %s\n", fs_name, mntdir);
181285612Sdelphij
182285612Sdelphij  (void) mkdirs(mntdir, 0555);
183181834Sroberto  if (stat(mntdir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) {
184285612Sdelphij    plog(XLOG_ERROR, "No mount point for %s - skipping", mntdir);
185285612Sdelphij    return ENOENT;
186285612Sdelphij  }
187285612Sdelphij
188285612Sdelphij  return mount_nfs_fh(fhp, mntdir, fs_name, mf);
189285612Sdelphij}
190285612Sdelphij
191285612Sdelphij
192285612Sdelphijstatic int
193285612Sdelphijsortfun(const voidp x, const voidp y)
194285612Sdelphij{
195181834Sroberto  exports *a = (exports *) x;
196285612Sdelphij  exports *b = (exports *) y;
197285612Sdelphij
198285612Sdelphij  return strcmp((*a)->ex_dir, (*b)->ex_dir);
199285612Sdelphij}
200285612Sdelphij
201181834Sroberto
202285612Sdelphij/*
203285612Sdelphij * Get filehandle
204285612Sdelphij */
205285612Sdelphijstatic int
206181834Srobertofetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_version)
207285612Sdelphij{
208285612Sdelphij  struct timeval tv;
209181834Sroberto  enum clnt_stat clnt_stat;
210285612Sdelphij  struct fhstatus res;
211285612Sdelphij#ifdef HAVE_FS_NFS3
212285612Sdelphij  struct am_mountres3 res3;
213285612Sdelphij#endif /* HAVE_FS_NFS3 */
214285612Sdelphij
215285612Sdelphij  /*
216285612Sdelphij   * Pick a number, any number...
217285612Sdelphij   */
218285612Sdelphij  tv.tv_sec = 20;
219285612Sdelphij  tv.tv_usec = 0;
220285612Sdelphij
221285612Sdelphij  dlog("Fetching fhandle for %s", dir);
222285612Sdelphij
223285612Sdelphij  /*
224285612Sdelphij   * Call the mount daemon on the remote host to
225285612Sdelphij   * get the filehandle.  Use NFS version specific call.
226285612Sdelphij   */
227285612Sdelphij
228285612Sdelphij  plog(XLOG_INFO, "fetch_fhandle: NFS version %d", (int) nfs_version);
229285612Sdelphij#ifdef HAVE_FS_NFS3
230285612Sdelphij  if (nfs_version == NFS_VERSION3) {
231285612Sdelphij    memset((char *) &res3, 0, sizeof(res3));
232285612Sdelphij    clnt_stat = clnt_call(client,
233285612Sdelphij			  MOUNTPROC_MNT,
234285612Sdelphij			  (XDRPROC_T_TYPE) xdr_dirpath,
235285612Sdelphij			  (SVC_IN_ARG_TYPE) &dir,
236285612Sdelphij			  (XDRPROC_T_TYPE) xdr_am_mountres3,
237285612Sdelphij			  (SVC_IN_ARG_TYPE) &res3,
238285612Sdelphij			  tv);
239285612Sdelphij    if (clnt_stat != RPC_SUCCESS) {
240285612Sdelphij      plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
241285612Sdelphij      return EIO;
242285612Sdelphij    }
243285612Sdelphij    /* Check the status of the filehandle */
244285612Sdelphij    if ((errno = res3.fhs_status)) {
245285612Sdelphij      dlog("fhandle fetch for mount version 3 failed: %m");
246285612Sdelphij      return errno;
247285612Sdelphij    }
248285612Sdelphij    memset((voidp) &fhp->v3, 0, sizeof(am_nfs_fh3));
249285612Sdelphij    fhp->v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len;
250285612Sdelphij    memmove(fhp->v3.am_fh3_data,
251285612Sdelphij	    res3.mountres3_u.mountinfo.fhandle.fhandle3_val,
252285612Sdelphij	    fhp->v3.am_fh3_length);
253285612Sdelphij  } else {			/* not NFS_VERSION3 mount */
254285612Sdelphij#endif /* HAVE_FS_NFS3 */
255285612Sdelphij    clnt_stat = clnt_call(client,
256285612Sdelphij			  MOUNTPROC_MNT,
257285612Sdelphij			  (XDRPROC_T_TYPE) xdr_dirpath,
258285612Sdelphij			  (SVC_IN_ARG_TYPE) &dir,
259285612Sdelphij			  (XDRPROC_T_TYPE) xdr_fhstatus,
260285612Sdelphij			  (SVC_IN_ARG_TYPE) &res,
261285612Sdelphij			  tv);
262285612Sdelphij    if (clnt_stat != RPC_SUCCESS) {
263285612Sdelphij      plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
264285612Sdelphij      return EIO;
265285612Sdelphij    }
266285612Sdelphij    /* Check status of filehandle */
267285612Sdelphij    if (res.fhs_status) {
268285612Sdelphij      errno = res.fhs_status;
269285612Sdelphij      dlog("fhandle fetch for mount version 1 failed: %m");
270285612Sdelphij      return errno;
271285612Sdelphij    }
272285612Sdelphij    memmove(&fhp->v2, &res.fhs_fh, NFS_FHSIZE);
273285612Sdelphij#ifdef HAVE_FS_NFS3
274285612Sdelphij  } /* end of "if (nfs_version == NFS_VERSION3)" statement */
275285612Sdelphij#endif /* HAVE_FS_NFS3 */
276285612Sdelphij
277285612Sdelphij  /* all is well */
278285612Sdelphij  return 0;
279285612Sdelphij}
280285612Sdelphij
281285612Sdelphij
282285612Sdelphij/*
283285612Sdelphij * Scan mount table to see if something already mounted
284285612Sdelphij */
285285612Sdelphijstatic int
286285612Sdelphijalready_mounted(mntlist *mlist, char *dir)
287285612Sdelphij{
288285612Sdelphij  mntlist *ml;
289285612Sdelphij
290285612Sdelphij  for (ml = mlist; ml; ml = ml->mnext)
291285612Sdelphij    if (STREQ(ml->mnt->mnt_dir, dir))
292285612Sdelphij      return 1;
293285612Sdelphij  return 0;
294285612Sdelphij}
295285612Sdelphij
296285612Sdelphij
297285612Sdelphijstatic int
298285612Sdelphijamfs_host_mount(am_node *am, mntfs *mf)
299285612Sdelphij{
300285612Sdelphij  struct timeval tv2;
301285612Sdelphij  CLIENT *client;
302285612Sdelphij  enum clnt_stat clnt_stat;
303285612Sdelphij  int n_export;
304285612Sdelphij  int j, k;
305285612Sdelphij  exports exlist = 0, ex;
306285612Sdelphij  exports *ep = 0;
307285612Sdelphij  am_nfs_handle_t *fp = 0;
308285612Sdelphij  char *host;
309285612Sdelphij  int error = 0;
310285612Sdelphij  struct sockaddr_in sin;
311181834Sroberto  int sock = RPC_ANYSOCK;
312181834Sroberto  int ok = FALSE;
313181834Sroberto  mntlist *mlist;
314181834Sroberto  char fs_name[MAXPATHLEN], *rfs_dir;
315181834Sroberto  char mntpt[MAXPATHLEN];
316285612Sdelphij  struct timeval tv;
317181834Sroberto  u_long mnt_version;
318285612Sdelphij
319285612Sdelphij  /*
320285612Sdelphij   * WebNFS servers don't necessarily run mountd.
321285612Sdelphij   */
322285612Sdelphij  if (mf->mf_flags & MFF_WEBNFS) {
323285612Sdelphij    plog(XLOG_ERROR, "amfs_host_mount: cannot support WebNFS");
324285612Sdelphij    return EIO;
325181834Sroberto  }
326285612Sdelphij
327285612Sdelphij  /*
328181834Sroberto   * Read the mount list
329181834Sroberto   */
330181834Sroberto  mlist = read_mtab(mf->mf_mount, mnttab_file_name);
331181834Sroberto
332181834Sroberto#ifdef MOUNT_TABLE_ON_FILE
333181834Sroberto  /*
334181834Sroberto   * Unlock the mount list
335285612Sdelphij   */
336181834Sroberto  unlock_mntlist();
337285612Sdelphij#endif /* MOUNT_TABLE_ON_FILE */
338181834Sroberto
339181834Sroberto  /*
340285612Sdelphij   * Take a copy of the server hostname, address, and nfs version
341285612Sdelphij   * to mount version conversion.
342181834Sroberto   */
343285612Sdelphij  host = mf->mf_server->fs_host;
344285612Sdelphij  sin = *mf->mf_server->fs_ip;
345285612Sdelphij  plog(XLOG_INFO, "amfs_host_mount: NFS version %d", (int) mf->mf_server->fs_version);
346285612Sdelphij#ifdef HAVE_FS_NFS3
347285612Sdelphij  if (mf->mf_server->fs_version == NFS_VERSION3)
348285612Sdelphij    mnt_version = AM_MOUNTVERS3;
349285612Sdelphij  else
350285612Sdelphij#endif /* HAVE_FS_NFS3 */
351285612Sdelphij    mnt_version = MOUNTVERS;
352285612Sdelphij
353285612Sdelphij  /*
354285612Sdelphij   * The original 10 second per try timeout is WAY too large, especially
355285612Sdelphij   * if we're only waiting 10 or 20 seconds max for the response.
356285612Sdelphij   * That would mean we'd try only once in 10 seconds, and we could
357285612Sdelphij   * lose the transmit or receive packet, and never try again.
358285612Sdelphij   * A 2-second per try timeout here is much more reasonable.
359285612Sdelphij   * 09/28/92 Mike Mitchell, mcm@unx.sas.com
360285612Sdelphij   */
361285612Sdelphij  tv.tv_sec = 2;
362285612Sdelphij  tv.tv_usec = 0;
363285612Sdelphij
364285612Sdelphij  /*
365285612Sdelphij   * Create a client attached to mountd
366285612Sdelphij   */
367285612Sdelphij  client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
368285612Sdelphij  if (client == NULL) {
369285612Sdelphij#ifdef HAVE_CLNT_SPCREATEERROR
370285612Sdelphij    plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
371285612Sdelphij	 host, clnt_spcreateerror(""));
372285612Sdelphij#else /* not HAVE_CLNT_SPCREATEERROR */
373285612Sdelphij    plog(XLOG_ERROR, "get_mount_client failed for %s", host);
374285612Sdelphij#endif /* not HAVE_CLNT_SPCREATEERROR */
375285612Sdelphij    error = EIO;
376285612Sdelphij    goto out;
377285612Sdelphij  }
378285612Sdelphij  if (!nfs_auth) {
379285612Sdelphij    error = make_nfs_auth();
380285612Sdelphij    if (error)
381285612Sdelphij      goto out;
382285612Sdelphij  }
383285612Sdelphij  client->cl_auth = nfs_auth;
384285612Sdelphij
385285612Sdelphij  dlog("Fetching export list from %s", host);
386285612Sdelphij
387285612Sdelphij  /*
388285612Sdelphij   * Fetch the export list
389181834Sroberto   */
390285612Sdelphij  tv2.tv_sec = 10;
391285612Sdelphij  tv2.tv_usec = 0;
392285612Sdelphij  clnt_stat = clnt_call(client,
393285612Sdelphij			MOUNTPROC_EXPORT,
394285612Sdelphij			(XDRPROC_T_TYPE) xdr_void,
395285612Sdelphij			0,
396285612Sdelphij			(XDRPROC_T_TYPE) xdr_exports,
397285612Sdelphij			(SVC_IN_ARG_TYPE) & exlist,
398285612Sdelphij			tv2);
399285612Sdelphij  if (clnt_stat != RPC_SUCCESS) {
400285612Sdelphij    const char *msg = clnt_sperrno(clnt_stat);
401285612Sdelphij    plog(XLOG_ERROR, "host_mount rpc failed: %s", msg);
402285612Sdelphij    /* clnt_perror(client, "rpc"); */
403285612Sdelphij    error = EIO;
404285612Sdelphij    goto out;
405181834Sroberto  }
406181834Sroberto
407181834Sroberto  /*
408285612Sdelphij   * Figure out how many exports were returned
409285612Sdelphij   */
410285612Sdelphij  for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) {
411181834Sroberto    n_export++;
412285612Sdelphij  }
413285612Sdelphij
414285612Sdelphij  /*
415285612Sdelphij   * Allocate an array of pointers into the list
416285612Sdelphij   * so that they can be sorted.  If the filesystem
417285612Sdelphij   * is already mounted then ignore it.
418181834Sroberto   */
419285612Sdelphij  ep = (exports *) xmalloc(n_export * sizeof(exports));
420285612Sdelphij  for (j = 0, ex = exlist; ex; ex = ex->ex_next) {
421285612Sdelphij    make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount);
422285612Sdelphij    if (already_mounted(mlist, mntpt))
423285612Sdelphij      /* we have at least one mounted f/s, so don't fail the mount */
424285612Sdelphij      ok = TRUE;
425285612Sdelphij    else
426285612Sdelphij      ep[j++] = ex;
427285612Sdelphij  }
428285612Sdelphij  n_export = j;
429285612Sdelphij
430285612Sdelphij  /*
431285612Sdelphij   * Sort into order.
432285612Sdelphij   * This way the mounts are done in order down the tree,
433285612Sdelphij   * instead of any random order returned by the mount
434181834Sroberto   * daemon (the protocol doesn't specify...).
435285612Sdelphij   */
436285612Sdelphij  qsort(ep, n_export, sizeof(exports), sortfun);
437285612Sdelphij
438181834Sroberto  /*
439181834Sroberto   * Allocate an array of filehandles
440285612Sdelphij   */
441181834Sroberto  fp = (am_nfs_handle_t *) xmalloc(n_export * sizeof(am_nfs_handle_t));
442181834Sroberto
443181834Sroberto  /*
444285612Sdelphij   * Try to obtain filehandles for each directory.
445181834Sroberto   * If a fetch fails then just zero out the array
446285612Sdelphij   * reference but discard the error.
447285612Sdelphij   */
448285612Sdelphij  for (j = k = 0; j < n_export; j++) {
449285612Sdelphij    /* Check and avoid a duplicated export entry */
450285612Sdelphij    if (j > k && ep[k] && STREQ(ep[j]->ex_dir, ep[k]->ex_dir)) {
451285612Sdelphij      dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir);
452181834Sroberto      ep[j] = 0;
453181834Sroberto    } else {
454181834Sroberto      k = j;
455285612Sdelphij      error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j],
456285612Sdelphij			    mf->mf_server->fs_version);
457181834Sroberto      if (error)
458285612Sdelphij	ep[j] = 0;
459285612Sdelphij    }
460285612Sdelphij  }
461285612Sdelphij
462285612Sdelphij  /*
463285612Sdelphij   * Mount each filesystem for which we have a filehandle.
464285612Sdelphij   * If any of the mounts succeed then mark "ok" and return
465181834Sroberto   * error code 0 at the end.  If they all fail then return
466181834Sroberto   * the last error code.
467181834Sroberto   */
468285612Sdelphij  xstrlcpy(fs_name, mf->mf_info, MAXPATHLEN);
469181834Sroberto  if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) {
470285612Sdelphij    plog(XLOG_FATAL, "amfs_host_mount: mf_info has no colon");
471285612Sdelphij    error = EINVAL;
472285612Sdelphij    goto out;
473181834Sroberto  }
474285612Sdelphij  ++rfs_dir;
475181834Sroberto  for (j = 0; j < n_export; j++) {
476285612Sdelphij    ex = ep[j];
477285612Sdelphij    if (ex) {
478285612Sdelphij      /*
479285612Sdelphij       * Note: the sizeof space left in rfs_dir is what's left in fs_name
480181834Sroberto       * after strchr() above returned a pointer _inside_ fs_name.  The
481285612Sdelphij       * calculation below also takes into account that rfs_dir was
482285612Sdelphij       * incremented by the ++ above.
483285612Sdelphij       */
484285612Sdelphij      xstrlcpy(rfs_dir, ex->ex_dir, sizeof(fs_name) - (rfs_dir - fs_name));
485285612Sdelphij      make_mntpt(mntpt, sizeof(mntpt), ex, mf->mf_mount);
486285612Sdelphij      if (do_mount(&fp[j], mntpt, fs_name, mf) == 0)
487285612Sdelphij	ok = TRUE;
488285612Sdelphij    }
489285612Sdelphij  }
490285612Sdelphij
491181834Sroberto  /*
492285612Sdelphij   * Clean up and exit
493285612Sdelphij   */
494285612Sdelphijout:
495285612Sdelphij  discard_mntlist(mlist);
496181834Sroberto  if (ep)
497285612Sdelphij    XFREE(ep);
498285612Sdelphij  if (fp)
499285612Sdelphij    XFREE(fp);
500285612Sdelphij  if (sock != RPC_ANYSOCK)
501285612Sdelphij    (void) amu_close(sock);
502285612Sdelphij  if (client)
503285612Sdelphij    clnt_destroy(client);
504285612Sdelphij  if (exlist)
505285612Sdelphij    xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist);
506285612Sdelphij  if (ok)
507285612Sdelphij    return 0;
508285612Sdelphij  return error;
509285612Sdelphij}
510285612Sdelphij
511285612Sdelphij
512285612Sdelphij/*
513181834Sroberto * Return true if pref is a directory prefix of dir.
514285612Sdelphij *
515181834Sroberto * XXX TODO:
516181834Sroberto * Does not work if pref is "/".
517181834Sroberto */
518285612Sdelphijstatic int
519285612Sdelphijdirectory_prefix(char *pref, char *dir)
520285612Sdelphij{
521285612Sdelphij  int len = strlen(pref);
522285612Sdelphij
523285612Sdelphij  if (!NSTREQ(pref, dir, len))
524285612Sdelphij    return FALSE;
525285612Sdelphij  if (dir[len] == '/' || dir[len] == '\0')
526285612Sdelphij    return TRUE;
527181834Sroberto  return FALSE;
528285612Sdelphij}
529285612Sdelphij
530285612Sdelphij
531285612Sdelphij/*
532285612Sdelphij * Unmount a mount tree
533285612Sdelphij */
534285612Sdelphijstatic int
535285612Sdelphijamfs_host_umount(am_node *am, mntfs *mf)
536285612Sdelphij{
537181834Sroberto  mntlist *ml, *mprev;
538285612Sdelphij  int unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
539285612Sdelphij  int xerror = 0;
540285612Sdelphij
541285612Sdelphij  /*
542285612Sdelphij   * Read the mount list
543285612Sdelphij   */
544285612Sdelphij  mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name);
545285612Sdelphij
546285612Sdelphij#ifdef MOUNT_TABLE_ON_FILE
547285612Sdelphij  /*
548181834Sroberto   * Unlock the mount list
549285612Sdelphij   */
550285612Sdelphij  unlock_mntlist();
551285612Sdelphij#endif /* MOUNT_TABLE_ON_FILE */
552181834Sroberto
553285612Sdelphij  /*
554285612Sdelphij   * Reverse list...
555285612Sdelphij   */
556285612Sdelphij  ml = mlist;
557285612Sdelphij  mprev = 0;
558285612Sdelphij  while (ml) {
559285612Sdelphij    mntlist *ml2 = ml->mnext;
560285612Sdelphij    ml->mnext = mprev;
561285612Sdelphij    mprev = ml;
562285612Sdelphij    ml = ml2;
563285612Sdelphij  }
564285612Sdelphij  mlist = mprev;
565285612Sdelphij
566285612Sdelphij  /*
567285612Sdelphij   * Unmount all filesystems...
568285612Sdelphij   */
569285612Sdelphij  for (ml = mlist; ml && !xerror; ml = ml->mnext) {
570285612Sdelphij    char *dir = ml->mnt->mnt_dir;
571285612Sdelphij    if (directory_prefix(mf->mf_mount, dir)) {
572285612Sdelphij      int error;
573285612Sdelphij      dlog("amfs_host: unmounts %s", dir);
574285612Sdelphij      /*
575285612Sdelphij       * Unmount "dir"
576181834Sroberto       */
577181834Sroberto      error = UMOUNT_FS(dir, mnttab_file_name, unmount_flags);
578285612Sdelphij      /*
579181834Sroberto       * Keep track of errors
580285612Sdelphij       */
581181834Sroberto      if (error) {
582285612Sdelphij	/*
583285612Sdelphij	 * If we have not already set xerror and error is not ENOENT,
584285612Sdelphij	 * then set xerror equal to error and log it.
585285612Sdelphij	 * 'xerror' is the return value for this function.
586285612Sdelphij	 *
587285612Sdelphij	 * We do not want to pass ENOENT as an error because if the
588285612Sdelphij	 * directory does not exists our work is done anyway.
589285612Sdelphij	 */
590181834Sroberto	if (!xerror && error != ENOENT)
591285612Sdelphij	  xerror = error;
592181834Sroberto	if (error != EBUSY) {
593181834Sroberto	  errno = error;
594181834Sroberto	  plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
595181834Sroberto	}
596285612Sdelphij      } else {
597285612Sdelphij	(void) rmdirs(dir);
598285612Sdelphij      }
599285612Sdelphij    }
600285612Sdelphij  }
601285612Sdelphij
602181834Sroberto  /*
603285612Sdelphij   * Throw away mount list
604285612Sdelphij   */
605285612Sdelphij  discard_mntlist(mlist);
606285612Sdelphij
607285612Sdelphij  /*
608285612Sdelphij   * Try to remount, except when we are shutting down.
609181834Sroberto   */
610285612Sdelphij  if (xerror && amd_state != Finishing) {
611285612Sdelphij    xerror = amfs_host_mount(am, mf);
612285612Sdelphij    if (!xerror) {
613285612Sdelphij      /*
614285612Sdelphij       * Don't log this - it's usually too verbose
615285612Sdelphij       plog(XLOG_INFO, "Remounted host %s", mf->mf_info);
616285612Sdelphij       */
617285612Sdelphij      xerror = EBUSY;
618285612Sdelphij    }
619285612Sdelphij  }
620285612Sdelphij  return xerror;
621285612Sdelphij}
622285612Sdelphij
623285612Sdelphij
624285612Sdelphij/*
625285612Sdelphij * Tell mountd we're done.
626285612Sdelphij * This is not quite right, because we may still
627285612Sdelphij * have other filesystems mounted, but the existing
628285612Sdelphij * mountd protocol is badly broken anyway.
629285612Sdelphij */
630285612Sdelphijstatic void
631285612Sdelphijamfs_host_umounted(mntfs *mf)
632285612Sdelphij{
633285612Sdelphij  char *host;
634285612Sdelphij  CLIENT *client;
635285612Sdelphij  enum clnt_stat clnt_stat;
636285612Sdelphij  struct sockaddr_in sin;
637181834Sroberto  int sock = RPC_ANYSOCK;
638285612Sdelphij  struct timeval tv;
639285612Sdelphij  u_long mnt_version;
640285612Sdelphij
641285612Sdelphij  if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server)
642181834Sroberto    return;
643285612Sdelphij
644285612Sdelphij  /*
645285612Sdelphij   * WebNFS servers shouldn't ever get here.
646285612Sdelphij   */
647285612Sdelphij  if (mf->mf_flags & MFF_WEBNFS) {
648181834Sroberto    plog(XLOG_ERROR, "amfs_host_umounted: cannot support WebNFS");
649285612Sdelphij    return;
650285612Sdelphij  }
651285612Sdelphij
652285612Sdelphij  /*
653285612Sdelphij   * Take a copy of the server hostname, address, and NFS version
654285612Sdelphij   * to mount version conversion.
655285612Sdelphij   */
656285612Sdelphij  host = mf->mf_server->fs_host;
657285612Sdelphij  sin = *mf->mf_server->fs_ip;
658285612Sdelphij  plog(XLOG_INFO, "amfs_host_umounted: NFS version %d", (int) mf->mf_server->fs_version);
659285612Sdelphij#ifdef HAVE_FS_NFS3
660285612Sdelphij  if (mf->mf_server->fs_version == NFS_VERSION3)
661285612Sdelphij    mnt_version = AM_MOUNTVERS3;
662285612Sdelphij  else
663285612Sdelphij#endif /* HAVE_FS_NFS3 */
664285612Sdelphij    mnt_version = MOUNTVERS;
665285612Sdelphij
666285612Sdelphij  /*
667285612Sdelphij   * Create a client attached to mountd
668285612Sdelphij   */
669181834Sroberto  tv.tv_sec = 10;
670181834Sroberto  tv.tv_usec = 0;
671181834Sroberto  client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
672285612Sdelphij  if (client == NULL) {
673285612Sdelphij#ifdef HAVE_CLNT_SPCREATEERROR
674285612Sdelphij    plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
675285612Sdelphij	 host, clnt_spcreateerror(""));
676285612Sdelphij#else /* not HAVE_CLNT_SPCREATEERROR */
677285612Sdelphij    plog(XLOG_ERROR, "get_mount_client failed for %s", host);
678285612Sdelphij#endif /* not HAVE_CLNT_SPCREATEERROR */
679285612Sdelphij    goto out;
680285612Sdelphij  }
681285612Sdelphij
682285612Sdelphij  if (!nfs_auth) {
683285612Sdelphij    if (make_nfs_auth())
684285612Sdelphij      goto out;
685285612Sdelphij  }
686285612Sdelphij  client->cl_auth = nfs_auth;
687285612Sdelphij
688285612Sdelphij  dlog("Unmounting all from %s", host);
689285612Sdelphij
690285612Sdelphij  clnt_stat = clnt_call(client,
691285612Sdelphij			MOUNTPROC_UMNTALL,
692285612Sdelphij			(XDRPROC_T_TYPE) xdr_void,
693285612Sdelphij			0,
694285612Sdelphij			(XDRPROC_T_TYPE) xdr_void,
695285612Sdelphij			0,
696285612Sdelphij			tv);
697181834Sroberto  if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_SYSTEMERROR) {
698181834Sroberto    /* RPC_SYSTEMERROR seems to be returned for no good reason ... */
699181834Sroberto    const char *msg = clnt_sperrno(clnt_stat);
700285612Sdelphij    plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg);
701285612Sdelphij    goto out;
702181834Sroberto  }
703181834Sroberto
704181834Srobertoout:
705181834Sroberto  if (sock != RPC_ANYSOCK)
706181834Sroberto    (void) amu_close(sock);
707181834Sroberto  if (client)
708181834Sroberto    clnt_destroy(client);
709181834Sroberto}
710181834Sroberto