amfs_host.c revision 131702
1118611Snjl/*
2118611Snjl * Copyright (c) 1997-2004 Erez Zadok
3118611Snjl * Copyright (c) 1990 Jan-Simon Pendry
4118611Snjl * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5118611Snjl * Copyright (c) 1990 The Regents of the University of California.
6118611Snjl * All rights reserved.
7118611Snjl *
8118611Snjl * This code is derived from software contributed to Berkeley by
9118611Snjl * Jan-Simon Pendry at Imperial College, London.
10118611Snjl *
11118611Snjl * Redistribution and use in source and binary forms, with or without
12118611Snjl * modification, are permitted provided that the following conditions
13118611Snjl * are met:
14118611Snjl * 1. Redistributions of source code must retain the above copyright
15118611Snjl *    notice, this list of conditions and the following disclaimer.
16118611Snjl * 2. Redistributions in binary form must reproduce the above copyright
17118611Snjl *    notice, this list of conditions and the following disclaimer in the
18118611Snjl *    documentation and/or other materials provided with the distribution.
19118611Snjl * 3. All advertising materials mentioning features or use of this software
20118611Snjl *    must display the following acknowledgment:
21118611Snjl *      This product includes software developed by the University of
22118611Snjl *      California, Berkeley and its contributors.
23118611Snjl * 4. Neither the name of the University nor the names of its contributors
24118611Snjl *    may be used to endorse or promote products derived from this software
25118611Snjl *    without specific prior written permission.
26118611Snjl *
27118611Snjl * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28118611Snjl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29118611Snjl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30118611Snjl * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31118611Snjl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32118611Snjl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33118611Snjl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34118611Snjl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35118611Snjl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36118611Snjl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37118611Snjl * SUCH DAMAGE.
38118611Snjl *
39118611Snjl *      %W% (Berkeley) %G%
40118611Snjl *
41118611Snjl * $Id: amfs_host.c,v 1.4.2.7 2004/01/06 03:15:16 ezk Exp $
42118611Snjl *
43118611Snjl */
44118611Snjl
45118611Snjl/*
46118611Snjl * NFS host file system.
47118611Snjl * Mounts all exported filesystems from a given host.
48118611Snjl * This has now degenerated into a mess but will not
49118611Snjl * be rewritten.  Amd 6 will support the abstractions
50118611Snjl * needed to make this work correctly.
51118611Snjl */
52118611Snjl
53118611Snjl#ifdef HAVE_CONFIG_H
54118611Snjl# include <config.h>
55118611Snjl#endif /* HAVE_CONFIG_H */
56118611Snjl#include <am_defs.h>
57118611Snjl#include <amd.h>
58118611Snjl
59118611Snjlstatic char *amfs_host_match(am_opts *fo);
60118611Snjlstatic int amfs_host_fmount(mntfs *mf);
61118611Snjlstatic int amfs_host_fumount(mntfs *mf);
62118611Snjlstatic int amfs_host_init(mntfs *mf);
63118611Snjlstatic void amfs_host_umounted(am_node *mp);
64118611Snjl
65118611Snjl/*
66118611Snjl * Ops structure
67118611Snjl */
68118611Snjlam_ops amfs_host_ops =
69118611Snjl{
70118611Snjl  "host",
71118611Snjl  amfs_host_match,
72118611Snjl  amfs_host_init,
73118611Snjl  amfs_auto_fmount,
74118611Snjl  amfs_host_fmount,
75118611Snjl  amfs_auto_fumount,
76118611Snjl  amfs_host_fumount,
77118611Snjl  amfs_error_lookuppn,
78118611Snjl  amfs_error_readdir,
79118611Snjl  0,				/* amfs_host_readlink */
80118611Snjl  0,				/* amfs_host_mounted */
81118611Snjl  amfs_host_umounted,
82118611Snjl  find_nfs_srvr,
83118611Snjl  FS_MKMNT | FS_BACKGROUND | FS_AMQINFO
84118611Snjl};
85118611Snjl
86118611Snjl
87118611Snjl/*
88118611Snjl * Determine the mount point:
89118611Snjl *
90118611Snjl * The next change we put in to better handle PCs.  This is a bit
91118611Snjl * disgusting, so you'd better sit down.  We change the make_mntpt function
92118611Snjl * to look for exported file systems without a leading '/'.  If they don't
93118611Snjl * have a leading '/', we add one.  If the export is 'a:' through 'z:'
94118611Snjl * (without a leading slash), we change it to 'a%' (or b% or z%).  This
95118611Snjl * allows the entire PC disk to be mounted.
96118611Snjl */
97118611Snjlstatic void
98118611Snjlmake_mntpt(char *mntpt, const exports ex, const mntfs *mf)
99118611Snjl{
100118611Snjl  if (ex->ex_dir[0] == '/') {
101118611Snjl    if (ex->ex_dir[1] == 0)
102118611Snjl      strcpy(mntpt, (mf)->mf_mount);
103118611Snjl    else
104118611Snjl      sprintf(mntpt, "%s%s", mf->mf_mount, ex->ex_dir);
105118611Snjl  } else if (ex->ex_dir[0] >= 'a' &&
106118611Snjl	     ex->ex_dir[0] <= 'z' &&
107118611Snjl	     ex->ex_dir[1] == ':' &&
108118611Snjl	     ex->ex_dir[2] == '/' &&
109118611Snjl	     ex->ex_dir[3] == 0)
110118611Snjl    sprintf(mntpt, "%s/%c%%", mf->mf_mount, ex->ex_dir[0]);
111118611Snjl  else
112118611Snjl    sprintf(mntpt, "%s/%s", mf->mf_mount, ex->ex_dir);
113118611Snjl}
114118611Snjl
115118611Snjl
116118611Snjl/*
117118611Snjl * Execute needs the same as NFS plus a helper command
118118611Snjl */
119118611Snjlstatic char *
120118611Snjlamfs_host_match(am_opts *fo)
121118611Snjl{
122118611Snjl  extern am_ops nfs_ops;
123118611Snjl
124118611Snjl  /*
125118611Snjl   * Make sure rfs is specified to keep nfs_match happy...
126118611Snjl   */
127118611Snjl  if (!fo->opt_rfs)
128118611Snjl    fo->opt_rfs = "/";
129118611Snjl
130118611Snjl  return (*nfs_ops.fs_match) (fo);
131118611Snjl}
132118611Snjl
133118611Snjl
134118611Snjlstatic int
135118611Snjlamfs_host_init(mntfs *mf)
136118611Snjl{
137118611Snjl  fserver *fs;
138118611Snjl  u_short port;
139118611Snjl
140118611Snjl  if (strchr(mf->mf_info, ':') == 0)
141118611Snjl    return ENOENT;
142118611Snjl
143118611Snjl  /*
144118611Snjl   * This is primarily to schedule a wakeup so that as soon
145118611Snjl   * as our fileserver is ready, we can continue setting up
146118611Snjl   * the host filesystem.  If we don't do this, the standard
147118611Snjl   * amfs_auto code will set up a fileserver structure, but it will
148118611Snjl   * have to wait for another nfs request from the client to come
149118611Snjl   * in before finishing.  Our way is faster since we don't have
150118611Snjl   * to wait for the client to resend its request (which could
151118611Snjl   * take a second or two).
152118611Snjl   */
153118611Snjl  /*
154118611Snjl   * First, we find the fileserver for this mntfs and then call
155118611Snjl   * nfs_srvr_port with our mntfs passed as the wait channel.
156118611Snjl   * nfs_srvr_port will check some things and then schedule
157118611Snjl   * it so that when the fileserver is ready, a wakeup is done
158118611Snjl   * on this mntfs.   amfs_auto_cont() is already sleeping on this mntfs
159118611Snjl   * so as soon as that wakeup happens amfs_auto_cont() is called and
160118611Snjl   * this mount is retried.
161118611Snjl   */
162118611Snjl  if ((fs = mf->mf_server))
163118611Snjl    /*
164118611Snjl     * We don't really care if there's an error returned.
165118611Snjl     * Since this is just to help speed things along, the
166118611Snjl     * error will get handled properly elsewhere.
167118611Snjl     */
168118611Snjl    (void) nfs_srvr_port(fs, &port, (voidp) mf);
169118611Snjl
170118611Snjl  return 0;
171118611Snjl}
172118611Snjl
173118611Snjl
174118611Snjlstatic int
175118611Snjldo_mount(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs *mf)
176118611Snjl{
177118611Snjl  struct stat stb;
178118611Snjl
179118611Snjl#ifdef DEBUG
180118611Snjl  dlog("amfs_host: mounting fs %s on %s\n", fs_name, dir);
181118611Snjl#endif /* DEBUG */
182118611Snjl
183118611Snjl  (void) mkdirs(dir, 0555);
184118611Snjl  if (stat(dir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) {
185118611Snjl    plog(XLOG_ERROR, "No mount point for %s - skipping", dir);
186118611Snjl    return ENOENT;
187118611Snjl  }
188118611Snjl
189118611Snjl  return mount_nfs_fh(fhp, dir, fs_name, opts, mf);
190118611Snjl}
191118611Snjl
192118611Snjl
193118611Snjlstatic int
194118611Snjlsortfun(const voidp x, const voidp y)
195118611Snjl{
196118611Snjl  exports *a = (exports *) x;
197118611Snjl  exports *b = (exports *) y;
198118611Snjl
199118611Snjl  return strcmp((*a)->ex_dir, (*b)->ex_dir);
200118611Snjl}
201118611Snjl
202118611Snjl
203118611Snjl/*
204118611Snjl * Get filehandle
205118611Snjl */
206118611Snjlstatic int
207118611Snjlfetch_fhandle(CLIENT *client, char *dir, am_nfs_handle_t *fhp, u_long nfs_version)
208118611Snjl{
209118611Snjl  struct timeval tv;
210118611Snjl  enum clnt_stat clnt_stat;
211118611Snjl
212118611Snjl  /*
213118611Snjl   * Pick a number, any number...
214118611Snjl   */
215118611Snjl  tv.tv_sec = 20;
216118611Snjl  tv.tv_usec = 0;
217118611Snjl
218118611Snjl#ifdef DEBUG
219118611Snjl  dlog("Fetching fhandle for %s", dir);
220118611Snjl#endif /* DEBUG */
221118611Snjl
222118611Snjl  /*
223118611Snjl   * Call the mount daemon on the remote host to
224118611Snjl   * get the filehandle.  Use NFS version specific call.
225118611Snjl   */
226118611Snjl
227118611Snjl  plog(XLOG_INFO, "fetch_fhandle: NFS version %d", (int) nfs_version);
228118611Snjl#ifdef HAVE_FS_NFS3
229118611Snjl  if (nfs_version == NFS_VERSION3) {
230118611Snjl    memset((char *) &fhp->v3, 0, sizeof(fhp->v3));
231118611Snjl    clnt_stat = clnt_call(client,
232118611Snjl			  MOUNTPROC_MNT,
233118611Snjl			  (XDRPROC_T_TYPE) xdr_dirpath,
234118611Snjl			  (SVC_IN_ARG_TYPE) &dir,
235118611Snjl			  (XDRPROC_T_TYPE) xdr_mountres3,
236118611Snjl			  (SVC_IN_ARG_TYPE) &fhp->v3,
237118611Snjl			  tv);
238118611Snjl    if (clnt_stat != RPC_SUCCESS) {
239118611Snjl      plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
240118611Snjl      return EIO;
241118611Snjl    }
242118611Snjl    /* Check the status of the filehandle */
243118611Snjl    if ((errno = fhp->v3.fhs_status)) {
244118611Snjl#ifdef DEBUG
245118611Snjl      dlog("fhandle fetch for mount version 3 failed: %m");
246118611Snjl#endif /* DEBUG */
247118611Snjl      return errno;
248118611Snjl    }
249118611Snjl  } else {			/* not NFS_VERSION3 mount */
250118611Snjl#endif /* HAVE_FS_NFS3 */
251118611Snjl    clnt_stat = clnt_call(client,
252118611Snjl			  MOUNTPROC_MNT,
253118611Snjl			  (XDRPROC_T_TYPE) xdr_dirpath,
254118611Snjl			  (SVC_IN_ARG_TYPE) &dir,
255118611Snjl			  (XDRPROC_T_TYPE) xdr_fhstatus,
256118611Snjl			  (SVC_IN_ARG_TYPE) &fhp->v2,
257118611Snjl			  tv);
258118611Snjl    if (clnt_stat != RPC_SUCCESS) {
259118611Snjl      plog(XLOG_ERROR, "mountd rpc failed: %s", clnt_sperrno(clnt_stat));
260118611Snjl      return EIO;
261118611Snjl    }
262118611Snjl    /* Check status of filehandle */
263118611Snjl    if (fhp->v2.fhs_status) {
264118611Snjl      errno = fhp->v2.fhs_status;
265118611Snjl#ifdef DEBUG
266118611Snjl      dlog("fhandle fetch for mount version 1 failed: %m");
267118611Snjl#endif /* DEBUG */
268118611Snjl      return errno;
269118611Snjl    }
270118611Snjl#ifdef HAVE_FS_NFS3
271118611Snjl  } /* end of "if (nfs_version == NFS_VERSION3)" statement */
272118611Snjl#endif /* HAVE_FS_NFS3 */
273118611Snjl
274118611Snjl  /* all is well */
275118611Snjl  return 0;
276118611Snjl}
277118611Snjl
278118611Snjl
279118611Snjl/*
280118611Snjl * Scan mount table to see if something already mounted
281118611Snjl */
282118611Snjlstatic int
283118611Snjlalready_mounted(mntlist *mlist, char *dir)
284118611Snjl{
285118611Snjl  mntlist *ml;
286118611Snjl
287118611Snjl  for (ml = mlist; ml; ml = ml->mnext)
288118611Snjl    if (STREQ(ml->mnt->mnt_dir, dir))
289118611Snjl      return 1;
290118611Snjl  return 0;
291118611Snjl}
292118611Snjl
293118611Snjl
294118611Snjl/*
295118611Snjl * Mount the export tree from a host
296118611Snjl */
297118611Snjlstatic int
298118611Snjlamfs_host_fmount(mntfs *mf)
299118611Snjl{
300118611Snjl  struct timeval tv2;
301118611Snjl  CLIENT *client;
302118611Snjl  enum clnt_stat clnt_stat;
303118611Snjl  int n_export;
304118611Snjl  int j, k;
305118611Snjl  exports exlist = 0, ex;
306118611Snjl  exports *ep = 0;
307118611Snjl  am_nfs_handle_t *fp = 0;
308118611Snjl  char *host;
309118611Snjl  int error = 0;
310118611Snjl  struct sockaddr_in sin;
311118611Snjl  int sock = RPC_ANYSOCK;
312118611Snjl  int ok = FALSE;
313118611Snjl  mntlist *mlist;
314118611Snjl  char fs_name[MAXPATHLEN], *rfs_dir;
315118611Snjl  char mntpt[MAXPATHLEN];
316118611Snjl  struct timeval tv;
317118611Snjl  u_long mnt_version;
318118611Snjl
319118611Snjl  /*
320118611Snjl   * Read the mount list
321118611Snjl   */
322118611Snjl  mlist = read_mtab(mf->mf_mount, mnttab_file_name);
323118611Snjl
324118611Snjl#ifdef MOUNT_TABLE_ON_FILE
325118611Snjl  /*
326118611Snjl   * Unlock the mount list
327118611Snjl   */
328118611Snjl  unlock_mntlist();
329118611Snjl#endif /* MOUNT_TABLE_ON_FILE */
330118611Snjl
331118611Snjl  /*
332118611Snjl   * Take a copy of the server hostname, address, and nfs version
333118611Snjl   * to mount version conversion.
334118611Snjl   */
335118611Snjl  host = mf->mf_server->fs_host;
336118611Snjl  sin = *mf->mf_server->fs_ip;
337118611Snjl  plog(XLOG_INFO, "amfs_host_fmount: NFS version %d", (int) mf->mf_server->fs_version);
338118611Snjl#ifdef HAVE_FS_NFS3
339118611Snjl  if (mf->mf_server->fs_version == NFS_VERSION3)
340118611Snjl    mnt_version = MOUNTVERS3;
341118611Snjl  else
342118611Snjl#endif /* HAVE_FS_NFS3 */
343118611Snjl    mnt_version = MOUNTVERS;
344118611Snjl
345118611Snjl  /*
346118611Snjl   * The original 10 second per try timeout is WAY too large, especially
347118611Snjl   * if we're only waiting 10 or 20 seconds max for the response.
348118611Snjl   * That would mean we'd try only once in 10 seconds, and we could
349118611Snjl   * lose the transmit or receive packet, and never try again.
350118611Snjl   * A 2-second per try timeout here is much more reasonable.
351118611Snjl   * 09/28/92 Mike Mitchell, mcm@unx.sas.com
352118611Snjl   */
353118611Snjl  tv.tv_sec = 2;
354118611Snjl  tv.tv_usec = 0;
355118611Snjl
356118611Snjl  /*
357118611Snjl   * Create a client attached to mountd
358118611Snjl   */
359118611Snjl  client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
360118611Snjl  if (client == NULL) {
361118611Snjl#ifdef HAVE_CLNT_SPCREATEERROR
362118611Snjl    plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
363118611Snjl	 host, clnt_spcreateerror(""));
364118611Snjl#else /* not HAVE_CLNT_SPCREATEERROR */
365118611Snjl    plog(XLOG_ERROR, "get_mount_client failed for %s", host);
366118611Snjl#endif /* not HAVE_CLNT_SPCREATEERROR */
367118611Snjl    error = EIO;
368118611Snjl    goto out;
369118611Snjl  }
370118611Snjl  if (!nfs_auth) {
371118611Snjl    error = make_nfs_auth();
372118611Snjl    if (error)
373118611Snjl      goto out;
374118611Snjl  }
375118611Snjl  client->cl_auth = nfs_auth;
376118611Snjl
377118611Snjl#ifdef DEBUG
378118611Snjl  dlog("Fetching export list from %s", host);
379118611Snjl#endif /* DEBUG */
380118611Snjl
381118611Snjl  /*
382118611Snjl   * Fetch the export list
383118611Snjl   */
384118611Snjl  tv2.tv_sec = 10;
385118611Snjl  tv2.tv_usec = 0;
386118611Snjl  clnt_stat = clnt_call(client,
387118611Snjl			MOUNTPROC_EXPORT,
388118611Snjl			(XDRPROC_T_TYPE) xdr_void,
389118611Snjl			0,
390118611Snjl			(XDRPROC_T_TYPE) xdr_exports,
391118611Snjl			(SVC_IN_ARG_TYPE) & exlist,
392118611Snjl			tv2);
393118611Snjl  if (clnt_stat != RPC_SUCCESS) {
394118611Snjl    const char *msg = clnt_sperrno(clnt_stat);
395118611Snjl    plog(XLOG_ERROR, "host_fmount rpc failed: %s", msg);
396118611Snjl    /* clnt_perror(client, "rpc"); */
397118611Snjl    error = EIO;
398118611Snjl    goto out;
399118611Snjl  }
400118611Snjl
401118611Snjl  /*
402118611Snjl   * Figure out how many exports were returned
403118611Snjl   */
404118611Snjl  for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) {
405118611Snjl    n_export++;
406118611Snjl  }
407118611Snjl
408118611Snjl  /*
409118611Snjl   * Allocate an array of pointers into the list
410118611Snjl   * so that they can be sorted.  If the filesystem
411118611Snjl   * is already mounted then ignore it.
412118611Snjl   */
413118611Snjl  ep = (exports *) xmalloc(n_export * sizeof(exports));
414118611Snjl  for (j = 0, ex = exlist; ex; ex = ex->ex_next) {
415118611Snjl    make_mntpt(mntpt, ex, mf);
416118611Snjl    if (already_mounted(mlist, mntpt))
417118611Snjl      /* we have at least one mounted f/s, so don't fail the mount */
418118611Snjl      ok = TRUE;
419118611Snjl    else
420118611Snjl      ep[j++] = ex;
421118611Snjl  }
422118611Snjl  n_export = j;
423118611Snjl
424118611Snjl  /*
425118611Snjl   * Sort into order.
426118611Snjl   * This way the mounts are done in order down the tree,
427118611Snjl   * instead of any random order returned by the mount
428118611Snjl   * daemon (the protocol doesn't specify...).
429118611Snjl   */
430118611Snjl  qsort(ep, n_export, sizeof(exports), sortfun);
431118611Snjl
432118611Snjl  /*
433118611Snjl   * Allocate an array of filehandles
434118611Snjl   */
435118611Snjl  fp = (am_nfs_handle_t *) xmalloc(n_export * sizeof(am_nfs_handle_t));
436118611Snjl
437118611Snjl  /*
438118611Snjl   * Try to obtain filehandles for each directory.
439118611Snjl   * If a fetch fails then just zero out the array
440118611Snjl   * reference but discard the error.
441118611Snjl   */
442118611Snjl  for (j = k = 0; j < n_export; j++) {
443118611Snjl    /* Check and avoid a duplicated export entry */
444118611Snjl    if (j > k && ep[k] && STREQ(ep[j]->ex_dir, ep[k]->ex_dir)) {
445118611Snjl#ifdef DEBUG
446118611Snjl      dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir);
447118611Snjl#endif /* DEBUG */
448118611Snjl      ep[j] = 0;
449118611Snjl    } else {
450118611Snjl      k = j;
451118611Snjl      error = fetch_fhandle(client, ep[j]->ex_dir, &fp[j],
452118611Snjl			    mf->mf_server->fs_version);
453118611Snjl      if (error)
454118611Snjl	ep[j] = 0;
455118611Snjl    }
456118611Snjl  }
457118611Snjl
458118611Snjl  /*
459118611Snjl   * Mount each filesystem for which we have a filehandle.
460118611Snjl   * If any of the mounts succeed then mark "ok" and return
461118611Snjl   * error code 0 at the end.  If they all fail then return
462118611Snjl   * the last error code.
463118611Snjl   */
464118611Snjl  strncpy(fs_name, mf->mf_info, sizeof(fs_name));
465118611Snjl  if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) {
466118611Snjl    plog(XLOG_FATAL, "amfs_host_fmount: mf_info has no colon");
467118611Snjl    error = EINVAL;
468118611Snjl    goto out;
469118611Snjl  }
470118611Snjl  ++rfs_dir;
471118611Snjl  for (j = 0; j < n_export; j++) {
472118611Snjl    ex = ep[j];
473118611Snjl    if (ex) {
474118611Snjl      strcpy(rfs_dir, ex->ex_dir);
475118611Snjl      make_mntpt(mntpt, ex, mf);
476118611Snjl      if (do_mount(&fp[j], mntpt, fs_name, mf->mf_mopts, mf) == 0)
477118611Snjl	ok = TRUE;
478118611Snjl    }
479118611Snjl  }
480118611Snjl
481118611Snjl  /*
482118611Snjl   * Clean up and exit
483118611Snjl   */
484118611Snjlout:
485118611Snjl  discard_mntlist(mlist);
486118611Snjl  if (ep)
487118611Snjl    XFREE(ep);
488118611Snjl  if (fp)
489118611Snjl    XFREE(fp);
490118611Snjl  if (sock != RPC_ANYSOCK)
491118611Snjl    (void) amu_close(sock);
492118611Snjl  if (client)
493118611Snjl    clnt_destroy(client);
494118611Snjl  if (exlist)
495118611Snjl    xdr_pri_free((XDRPROC_T_TYPE) xdr_exports, (caddr_t) &exlist);
496118611Snjl  if (ok)
497118611Snjl    return 0;
498118611Snjl  return error;
499118611Snjl}
500118611Snjl
501118611Snjl
502118611Snjl/*
503118611Snjl * Return true if pref is a directory prefix of dir.
504118611Snjl *
505118611Snjl * XXX TODO:
506118611Snjl * Does not work if pref is "/".
507118611Snjl */
508118611Snjlstatic int
509118611Snjldirectory_prefix(char *pref, char *dir)
510118611Snjl{
511118611Snjl  int len = strlen(pref);
512118611Snjl
513118611Snjl  if (!NSTREQ(pref, dir, len))
514118611Snjl    return FALSE;
515118611Snjl  if (dir[len] == '/' || dir[len] == '\0')
516118611Snjl    return TRUE;
517118611Snjl  return FALSE;
518118611Snjl}
519118611Snjl
520118611Snjl
521118611Snjl/*
522118611Snjl * Unmount a mount tree
523118611Snjl */
524118611Snjlstatic int
525118611Snjlamfs_host_fumount(mntfs *mf)
526118611Snjl{
527118611Snjl  mntlist *ml, *mprev;
528118611Snjl  int xerror = 0;
529118611Snjl
530118611Snjl  /*
531118611Snjl   * Read the mount list
532118611Snjl   */
533118611Snjl  mntlist *mlist = read_mtab(mf->mf_mount, mnttab_file_name);
534118611Snjl
535118611Snjl#ifdef MOUNT_TABLE_ON_FILE
536118611Snjl  /*
537118611Snjl   * Unlock the mount list
538118611Snjl   */
539118611Snjl  unlock_mntlist();
540118611Snjl#endif /* MOUNT_TABLE_ON_FILE */
541118611Snjl
542118611Snjl  /*
543118611Snjl   * Reverse list...
544118611Snjl   */
545118611Snjl  ml = mlist;
546118611Snjl  mprev = 0;
547118611Snjl  while (ml) {
548118611Snjl    mntlist *ml2 = ml->mnext;
549118611Snjl    ml->mnext = mprev;
550118611Snjl    mprev = ml;
551118611Snjl    ml = ml2;
552118611Snjl  }
553118611Snjl  mlist = mprev;
554118611Snjl
555118611Snjl  /*
556118611Snjl   * Unmount all filesystems...
557118611Snjl   */
558118611Snjl  for (ml = mlist; ml && !xerror; ml = ml->mnext) {
559118611Snjl    char *dir = ml->mnt->mnt_dir;
560118611Snjl    if (directory_prefix(mf->mf_mount, dir)) {
561118611Snjl      int error;
562118611Snjl#ifdef DEBUG
563118611Snjl      dlog("amfs_host: unmounts %s", dir);
564118611Snjl#endif /* DEBUG */
565118611Snjl      /*
566118611Snjl       * Unmount "dir"
567118611Snjl       */
568118611Snjl      error = UMOUNT_FS(dir, mnttab_file_name);
569118611Snjl      /*
570118611Snjl       * Keep track of errors
571118611Snjl       */
572118611Snjl      if (error) {
573118611Snjl	if (!xerror)
574118611Snjl	  xerror = error;
575118611Snjl	if (error != EBUSY) {
576118611Snjl	  errno = error;
577118611Snjl	  plog(XLOG_ERROR, "Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
578118611Snjl	}
579118611Snjl      } else {
580118611Snjl	(void) rmdirs(dir);
581118611Snjl      }
582118611Snjl    }
583118611Snjl  }
584118611Snjl
585118611Snjl  /*
586118611Snjl   * Throw away mount list
587118611Snjl   */
588118611Snjl  discard_mntlist(mlist);
589118611Snjl
590118611Snjl  /*
591118611Snjl   * Try to remount, except when we are shutting down.
592118611Snjl   */
593118611Snjl  if (xerror && amd_state != Finishing) {
594118611Snjl    xerror = amfs_host_fmount(mf);
595118611Snjl    if (!xerror) {
596118611Snjl      /*
597118611Snjl       * Don't log this - it's usually too verbose
598118611Snjl       plog(XLOG_INFO, "Remounted host %s", mf->mf_info);
599118611Snjl       */
600118611Snjl      xerror = EBUSY;
601118611Snjl    }
602118611Snjl  }
603118611Snjl  return xerror;
604118611Snjl}
605118611Snjl
606118611Snjl
607118611Snjl/*
608118611Snjl * Tell mountd we're done.
609118611Snjl * This is not quite right, because we may still
610118611Snjl * have other filesystems mounted, but the existing
611118611Snjl * mountd protocol is badly broken anyway.
612118611Snjl */
613118611Snjlstatic void
614118611Snjlamfs_host_umounted(am_node *mp)
615118611Snjl{
616118611Snjl  mntfs *mf = mp->am_mnt;
617118611Snjl  char *host;
618118611Snjl  CLIENT *client;
619118611Snjl  enum clnt_stat clnt_stat;
620118611Snjl  struct sockaddr_in sin;
621118611Snjl  int sock = RPC_ANYSOCK;
622118611Snjl  struct timeval tv;
623118611Snjl  u_long mnt_version;
624118611Snjl
625118611Snjl  if (mf->mf_error || mf->mf_refc > 1 || !mf->mf_server)
626118611Snjl    return;
627118611Snjl
628118611Snjl  /*
629118611Snjl   * Take a copy of the server hostname, address, and NFS version
630118611Snjl   * to mount version conversion.
631118611Snjl   */
632118611Snjl  host = mf->mf_server->fs_host;
633118611Snjl  sin = *mf->mf_server->fs_ip;
634118611Snjl  plog(XLOG_INFO, "amfs_host_umounted: NFS version %d", (int) mf->mf_server->fs_version);
635118611Snjl#ifdef HAVE_FS_NFS3
636118611Snjl  if (mf->mf_server->fs_version == NFS_VERSION3)
637118611Snjl    mnt_version = MOUNTVERS3;
638118611Snjl  else
639118611Snjl#endif /* HAVE_FS_NFS3 */
640118611Snjl    mnt_version = MOUNTVERS;
641118611Snjl
642118611Snjl  /*
643118611Snjl   * Create a client attached to mountd
644118611Snjl   */
645118611Snjl  tv.tv_sec = 10;
646118611Snjl  tv.tv_usec = 0;
647118611Snjl  client = get_mount_client(host, &sin, &tv, &sock, mnt_version);
648118611Snjl  if (client == NULL) {
649118611Snjl#ifdef HAVE_CLNT_SPCREATEERROR
650118611Snjl    plog(XLOG_ERROR, "get_mount_client failed for %s: %s",
651118611Snjl	 host, clnt_spcreateerror(""));
652118611Snjl#else /* not HAVE_CLNT_SPCREATEERROR */
653118611Snjl    plog(XLOG_ERROR, "get_mount_client failed for %s", host);
654118611Snjl#endif /* not HAVE_CLNT_SPCREATEERROR */
655118611Snjl    goto out;
656118611Snjl  }
657118611Snjl
658118611Snjl  if (!nfs_auth) {
659118611Snjl    if (make_nfs_auth())
660118611Snjl      goto out;
661118611Snjl  }
662118611Snjl  client->cl_auth = nfs_auth;
663118611Snjl
664118611Snjl#ifdef DEBUG
665118611Snjl  dlog("Unmounting all from %s", host);
666118611Snjl#endif /* DEBUG */
667118611Snjl
668118611Snjl  clnt_stat = clnt_call(client,
669118611Snjl			MOUNTPROC_UMNTALL,
670118611Snjl			(XDRPROC_T_TYPE) xdr_void,
671118611Snjl			0,
672118611Snjl			(XDRPROC_T_TYPE) xdr_void,
673118611Snjl			0,
674118611Snjl			tv);
675118611Snjl  if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_SYSTEMERROR) {
676118611Snjl    /* RPC_SYSTEMERROR seems to be returned for no good reason ... */
677118611Snjl    const char *msg = clnt_sperrno(clnt_stat);
678118611Snjl    plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg);
679118611Snjl    goto out;
680118611Snjl  }
681118611Snjl
682118611Snjlout:
683118611Snjl  if (sock != RPC_ANYSOCK)
684118611Snjl    (void) amu_close(sock);
685118611Snjl  if (client)
686118611Snjl    clnt_destroy(client);
687118611Snjl}
688118611Snjl