138494Sobrien/*
2310490Scy * Copyright (c) 1997-2014 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.
19310490Scy * 3. Neither the name of the University nor the names of its contributors
2038494Sobrien *    may be used to endorse or promote products derived from this software
2138494Sobrien *    without specific prior written permission.
2238494Sobrien *
2338494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2438494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2538494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2638494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2738494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2838494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2938494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3038494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3138494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3238494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3338494Sobrien * SUCH DAMAGE.
3438494Sobrien *
3538494Sobrien *
36174294Sobrien * File: am-utils/amd/ops_nfs.c
3738494Sobrien *
3838494Sobrien */
3938494Sobrien
4038494Sobrien/*
4138494Sobrien * Network file system
4238494Sobrien */
4338494Sobrien
4438494Sobrien#ifdef HAVE_CONFIG_H
4538494Sobrien# include <config.h>
4638494Sobrien#endif /* HAVE_CONFIG_H */
4738494Sobrien#include <am_defs.h>
4838494Sobrien#include <amd.h>
4938494Sobrien
5038494Sobrien/*
5138494Sobrien * Convert from nfsstat to UN*X error code
5238494Sobrien */
5338494Sobrien#define unx_error(e)	((int)(e))
5438494Sobrien
5538494Sobrien/*
5638494Sobrien * FH_TTL is the time a file handle will remain in the cache since
5738494Sobrien * last being used.  If the file handle becomes invalid, then it
5838494Sobrien * will be flushed anyway.
5938494Sobrien */
6038494Sobrien#define	FH_TTL			(5 * 60) /* five minutes */
6138494Sobrien#define	FH_TTL_ERROR		(30) /* 30 seconds */
62174294Sobrien#define	FHID_ALLOC()		(++fh_id)
6338494Sobrien
6438494Sobrien/*
6538494Sobrien * The NFS layer maintains a cache of file handles.
6638494Sobrien * This is *fundamental* to the implementation and
6738494Sobrien * also allows quick remounting when a filesystem
6838494Sobrien * is accessed soon after timing out.
6938494Sobrien *
7038494Sobrien * The NFS server layer knows to flush this cache
7138494Sobrien * when a server goes down so avoiding stale handles.
7238494Sobrien *
7338494Sobrien * Each cache entry keeps a hard reference to
7438494Sobrien * the corresponding server.  This ensures that
7538494Sobrien * the server keepalive information is maintained.
7638494Sobrien *
7738494Sobrien * The copy of the sockaddr_in here is taken so
7838494Sobrien * that the port can be twiddled to talk to mountd
7938494Sobrien * instead of portmap or the NFS server as used
8038494Sobrien * elsewhere.
8138494Sobrien * The port# is flushed if a server goes down.
8238494Sobrien * The IP address is never flushed - we assume
8338494Sobrien * that the address of a mounted machine never
8438494Sobrien * changes.  If it does, then you have other
8538494Sobrien * problems...
8638494Sobrien */
8738494Sobrientypedef struct fh_cache fh_cache;
8838494Sobrienstruct fh_cache {
8938494Sobrien  qelem			fh_q;		/* List header */
90174294Sobrien  wchan_t		fh_wchan;	/* Wait channel */
9138494Sobrien  int			fh_error;	/* Valid data? */
9238494Sobrien  int			fh_id;		/* Unique id */
9338494Sobrien  int			fh_cid;		/* Callout id */
9438494Sobrien  u_long		fh_nfs_version;	/* highest NFS version on host */
9538494Sobrien  am_nfs_handle_t	fh_nfs_handle;	/* Handle on filesystem */
96174294Sobrien  int			fh_status;	/* Status of last rpc */
9738494Sobrien  struct sockaddr_in	fh_sin;		/* Address of mountd */
9838494Sobrien  fserver		*fh_fs;		/* Server holding filesystem */
9938494Sobrien  char			*fh_path;	/* Filesystem on host */
10038494Sobrien};
10138494Sobrien
10238494Sobrien/* forward definitions */
103174294Sobrienstatic int nfs_init(mntfs *mf);
104174294Sobrienstatic char *nfs_match(am_opts *fo);
105174294Sobrienstatic int nfs_mount(am_node *am, mntfs *mf);
106174294Sobrienstatic int nfs_umount(am_node *am, mntfs *mf);
107174294Sobrienstatic void nfs_umounted(mntfs *mf);
108174294Sobrienstatic int call_mountd(fh_cache *fp, u_long proc, fwd_fun f, wchan_t wchan);
109174294Sobrienstatic int webnfs_lookup(fh_cache *fp, fwd_fun f, wchan_t wchan);
11038494Sobrienstatic int fh_id = 0;
11138494Sobrien
112310490Scy/*
113310490Scy * clamp the filehandle version to 3, so that we can fail back to nfsv3
114310490Scy * since nfsv4 does not have file handles
115310490Scy */
116310490Scy#define SET_FH_VERSION(fs) \
117310490Scy    (fs)->fs_version > NFS_VERSION3 ? NFS_VERSION3 : (fs)->fs_version;
118310490Scy
11938494Sobrien/* globals */
12038494SobrienAUTH *nfs_auth;
12138494Sobrienqelem fh_head = {&fh_head, &fh_head};
12238494Sobrien
12338494Sobrien/*
12438494Sobrien * Network file system operations
12538494Sobrien */
12638494Sobrienam_ops nfs_ops =
12738494Sobrien{
12838494Sobrien  "nfs",
12938494Sobrien  nfs_match,
13038494Sobrien  nfs_init,
131174294Sobrien  nfs_mount,
132174294Sobrien  nfs_umount,
133174294Sobrien  amfs_error_lookup_child,
134174294Sobrien  amfs_error_mount_child,
13538494Sobrien  amfs_error_readdir,
13638494Sobrien  0,				/* nfs_readlink */
13738494Sobrien  0,				/* nfs_mounted */
13838494Sobrien  nfs_umounted,
13938494Sobrien  find_nfs_srvr,
140174294Sobrien  0,				/* nfs_get_wchan */
141174294Sobrien  FS_MKMNT | FS_BACKGROUND | FS_AMQINFO,	/* nfs_fs_flags */
142174294Sobrien#ifdef HAVE_FS_AUTOFS
143174294Sobrien  AUTOFS_NFS_FS_FLAGS,
144174294Sobrien#endif /* HAVE_FS_AUTOFS */
14538494Sobrien};
14638494Sobrien
14738494Sobrien
14838494Sobrienstatic fh_cache *
149174294Sobrienfind_nfs_fhandle_cache(opaque_t arg, int done)
15038494Sobrien{
151310490Scy  fh_cache *fp, *fp2 = NULL;
152174294Sobrien  int id = (long) arg;		/* for 64-bit archs */
15338494Sobrien
15438494Sobrien  ITER(fp, fh_cache, &fh_head) {
15538494Sobrien    if (fp->fh_id == id) {
15638494Sobrien      fp2 = fp;
15738494Sobrien      break;
15838494Sobrien    }
15938494Sobrien  }
16038494Sobrien
16138494Sobrien  if (fp2) {
16251292Sobrien    dlog("fh cache gives fp %#lx, fs %s", (unsigned long) fp2, fp2->fh_path);
16338494Sobrien  } else {
16438494Sobrien    dlog("fh cache search failed");
16538494Sobrien  }
16638494Sobrien
16738494Sobrien  if (fp2 && !done) {
16838494Sobrien    fp2->fh_error = ETIMEDOUT;
16938494Sobrien    return 0;
17038494Sobrien  }
17138494Sobrien
17238494Sobrien  return fp2;
17338494Sobrien}
17438494Sobrien
17538494Sobrien
17638494Sobrien/*
177174294Sobrien * Called when a filehandle appears via the mount protocol
17838494Sobrien */
17938494Sobrienstatic void
180174294Sobriengot_nfs_fh_mount(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, opaque_t arg, int done)
18138494Sobrien{
18238494Sobrien  fh_cache *fp;
183174294Sobrien  struct fhstatus res;
184174294Sobrien#ifdef HAVE_FS_NFS3
185174294Sobrien  struct am_mountres3 res3;
186174294Sobrien#endif /* HAVE_FS_NFS3 */
18738494Sobrien
188174294Sobrien  fp = find_nfs_fhandle_cache(arg, done);
18938494Sobrien  if (!fp)
19038494Sobrien    return;
19138494Sobrien
19238494Sobrien  /*
19338494Sobrien   * retrieve the correct RPC reply for the file handle, based on the
19438494Sobrien   * NFS protocol version.
19538494Sobrien   */
19638494Sobrien#ifdef HAVE_FS_NFS3
197174294Sobrien  if (fp->fh_nfs_version == NFS_VERSION3) {
198174294Sobrien    memset(&res3, 0, sizeof(res3));
199174294Sobrien    fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res3,
200174294Sobrien				    (XDRPROC_T_TYPE) xdr_am_mountres3);
201174294Sobrien    fp->fh_status = unx_error(res3.fhs_status);
202174294Sobrien    memset(&fp->fh_nfs_handle.v3, 0, sizeof(am_nfs_fh3));
203174294Sobrien    fp->fh_nfs_handle.v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len;
204174294Sobrien    memmove(fp->fh_nfs_handle.v3.am_fh3_data,
205174294Sobrien	    res3.mountres3_u.mountinfo.fhandle.fhandle3_val,
206174294Sobrien	    fp->fh_nfs_handle.v3.am_fh3_length);
207310490Scy
208310490Scy    XFREE(res3.mountres3_u.mountinfo.fhandle.fhandle3_val);
209310490Scy    if (res3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val)
210310490Scy      XFREE(res3.mountres3_u.mountinfo.auth_flavors.auth_flavors_val);
211174294Sobrien  } else {
21238494Sobrien#endif /* HAVE_FS_NFS3 */
213174294Sobrien    memset(&res, 0, sizeof(res));
214174294Sobrien    fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res,
21538494Sobrien				    (XDRPROC_T_TYPE) xdr_fhstatus);
216174294Sobrien    fp->fh_status = unx_error(res.fhs_status);
217174294Sobrien    memmove(&fp->fh_nfs_handle.v2, &res.fhs_fh, NFS_FHSIZE);
218174294Sobrien#ifdef HAVE_FS_NFS3
219174294Sobrien  }
220174294Sobrien#endif /* HAVE_FS_NFS3 */
22138494Sobrien
22238494Sobrien  if (!fp->fh_error) {
22338494Sobrien    dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
224174294Sobrien  } else {
225174294Sobrien    plog(XLOG_USER, "filehandle denied for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
226174294Sobrien    /*
227174294Sobrien     * Force the error to be EACCES. It's debatable whether it should be
228174294Sobrien     * ENOENT instead, but the server really doesn't give us any clues, and
229174294Sobrien     * EACCES is more in line with the "filehandle denied" message.
230174294Sobrien     */
231174294Sobrien    fp->fh_error = EACCES;
232174294Sobrien  }
23338494Sobrien
234174294Sobrien  /*
235174294Sobrien   * Wakeup anything sleeping on this filehandle
236174294Sobrien   */
237174294Sobrien  if (fp->fh_wchan) {
238174294Sobrien    dlog("Calling wakeup on %#lx", (unsigned long) fp->fh_wchan);
239174294Sobrien    wakeup(fp->fh_wchan);
240174294Sobrien  }
241174294Sobrien}
242174294Sobrien
243174294Sobrien
244174294Sobrien/*
245174294Sobrien * Called when a filehandle appears via WebNFS
246174294Sobrien */
247174294Sobrienstatic void
248174294Sobriengot_nfs_fh_webnfs(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, opaque_t arg, int done)
249174294Sobrien{
250174294Sobrien  fh_cache *fp;
251174294Sobrien  nfsdiropres res;
252174294Sobrien#ifdef HAVE_FS_NFS3
253174294Sobrien  am_LOOKUP3res res3;
254174294Sobrien#endif /* HAVE_FS_NFS3 */
255174294Sobrien
256174294Sobrien  fp = find_nfs_fhandle_cache(arg, done);
257174294Sobrien  if (!fp)
258174294Sobrien    return;
259174294Sobrien
260174294Sobrien  /*
261174294Sobrien   * retrieve the correct RPC reply for the file handle, based on the
262174294Sobrien   * NFS protocol version.
263174294Sobrien   */
264174294Sobrien#ifdef HAVE_FS_NFS3
265174294Sobrien  if (fp->fh_nfs_version == NFS_VERSION3) {
266174294Sobrien    memset(&res3, 0, sizeof(res3));
267174294Sobrien    fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res3,
268174294Sobrien				    (XDRPROC_T_TYPE) xdr_am_LOOKUP3res);
269174294Sobrien    fp->fh_status = unx_error(res3.status);
270174294Sobrien    memset(&fp->fh_nfs_handle.v3, 0, sizeof(am_nfs_fh3));
271174294Sobrien    fp->fh_nfs_handle.v3.am_fh3_length = res3.res_u.ok.object.am_fh3_length;
272174294Sobrien    memmove(fp->fh_nfs_handle.v3.am_fh3_data,
273174294Sobrien	    res3.res_u.ok.object.am_fh3_data,
274174294Sobrien	    fp->fh_nfs_handle.v3.am_fh3_length);
275174294Sobrien  } else {
276174294Sobrien#endif /* HAVE_FS_NFS3 */
277174294Sobrien    memset(&res, 0, sizeof(res));
278174294Sobrien    fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res,
279174294Sobrien				    (XDRPROC_T_TYPE) xdr_diropres);
280174294Sobrien    fp->fh_status = unx_error(res.dr_status);
281174294Sobrien    memmove(&fp->fh_nfs_handle.v2, &res.dr_u.dr_drok_u.drok_fhandle, NFS_FHSIZE);
282174294Sobrien#ifdef HAVE_FS_NFS3
283174294Sobrien  }
284174294Sobrien#endif /* HAVE_FS_NFS3 */
285174294Sobrien
286174294Sobrien  if (!fp->fh_error) {
287174294Sobrien    dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
288174294Sobrien  } else {
289174294Sobrien    plog(XLOG_USER, "filehandle denied for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
29038494Sobrien    /*
291174294Sobrien     * Force the error to be EACCES. It's debatable whether it should be
292174294Sobrien     * ENOENT instead, but the server really doesn't give us any clues, and
293174294Sobrien     * EACCES is more in line with the "filehandle denied" message.
29438494Sobrien     */
295174294Sobrien    fp->fh_error = EACCES;
29638494Sobrien  }
297174294Sobrien
298174294Sobrien  /*
299174294Sobrien   * Wakeup anything sleeping on this filehandle
300174294Sobrien   */
301174294Sobrien  if (fp->fh_wchan) {
302174294Sobrien    dlog("Calling wakeup on %#lx", (unsigned long) fp->fh_wchan);
303174294Sobrien    wakeup(fp->fh_wchan);
304174294Sobrien  }
30538494Sobrien}
30638494Sobrien
30738494Sobrien
30838494Sobrienvoid
30938494Sobrienflush_nfs_fhandle_cache(fserver *fs)
31038494Sobrien{
31138494Sobrien  fh_cache *fp;
31238494Sobrien
31338494Sobrien  ITER(fp, fh_cache, &fh_head) {
314174294Sobrien    if (fp->fh_fs == fs || fs == NULL) {
315174294Sobrien      /*
316174294Sobrien       * Only invalidate port info for non-WebNFS servers
317174294Sobrien       */
318174294Sobrien      if (!(fp->fh_fs->fs_flags & FSF_WEBNFS))
319174294Sobrien	fp->fh_sin.sin_port = (u_short) 0;
32038494Sobrien      fp->fh_error = -1;
32138494Sobrien    }
32238494Sobrien  }
32338494Sobrien}
32438494Sobrien
32538494Sobrien
32638494Sobrienstatic void
327174294Sobriendiscard_fh(opaque_t arg)
32838494Sobrien{
329174294Sobrien  fh_cache *fp = (fh_cache *) arg;
33038494Sobrien
33138494Sobrien  rem_que(&fp->fh_q);
33238494Sobrien  if (fp->fh_fs) {
33338494Sobrien    dlog("Discarding filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
33438494Sobrien    free_srvr(fp->fh_fs);
33538494Sobrien  }
336310490Scy  XFREE(fp->fh_path);
33738494Sobrien  XFREE(fp);
33838494Sobrien}
33938494Sobrien
34038494Sobrien
34138494Sobrien/*
34238494Sobrien * Determine the file handle for a node
34338494Sobrien */
34438494Sobrienstatic int
345174294Sobrienprime_nfs_fhandle_cache(char *path, fserver *fs, am_nfs_handle_t *fhbuf, mntfs *mf)
34638494Sobrien{
347310490Scy  fh_cache *fp, *fp_save = NULL;
34838494Sobrien  int error;
34938494Sobrien  int reuse_id = FALSE;
35038494Sobrien
35138494Sobrien  dlog("Searching cache for %s:%s", fs->fs_host, path);
35238494Sobrien
35338494Sobrien  /*
35438494Sobrien   * First search the cache
35538494Sobrien   */
35638494Sobrien  ITER(fp, fh_cache, &fh_head) {
357174294Sobrien    if (fs != fp->fh_fs  ||  !STREQ(path, fp->fh_path))
358174294Sobrien      continue;			/* skip to next ITER item */
359174294Sobrien    /* else we got a match */
360174294Sobrien    switch (fp->fh_error) {
361174294Sobrien    case 0:
362174294Sobrien      plog(XLOG_INFO, "prime_nfs_fhandle_cache: NFS version %d", (int) fp->fh_nfs_version);
363174294Sobrien
364174294Sobrien      error = fp->fh_error = fp->fh_status;
365174294Sobrien
366174294Sobrien      if (error == 0) {
367174294Sobrien	if (mf->mf_flags & MFF_NFS_SCALEDOWN) {
368174294Sobrien	  fp_save = fp;
369174294Sobrien	  /* XXX: why reuse the ID? */
370174294Sobrien	  reuse_id = TRUE;
371174294Sobrien	  break;
372174294Sobrien	}
373174294Sobrien
374174294Sobrien	if (fhbuf) {
37538494Sobrien#ifdef HAVE_FS_NFS3
376174294Sobrien	  if (fp->fh_nfs_version == NFS_VERSION3) {
377174294Sobrien	    memmove((voidp) &(fhbuf->v3), (voidp) &(fp->fh_nfs_handle.v3),
378174294Sobrien		    sizeof(fp->fh_nfs_handle.v3));
379174294Sobrien	  } else
38038494Sobrien#endif /* HAVE_FS_NFS3 */
381174294Sobrien	    {
38238494Sobrien	      memmove((voidp) &(fhbuf->v2), (voidp) &(fp->fh_nfs_handle.v2),
38338494Sobrien		      sizeof(fp->fh_nfs_handle.v2));
384174294Sobrien	    }
38538494Sobrien	}
386174294Sobrien	if (fp->fh_cid)
387174294Sobrien	  untimeout(fp->fh_cid);
388174294Sobrien	fp->fh_cid = timeout(FH_TTL, discard_fh, (opaque_t) fp);
389174294Sobrien      } else if (error == EACCES) {
39038494Sobrien	/*
391174294Sobrien	 * Now decode the file handle return code.
39238494Sobrien	 */
393174294Sobrien	plog(XLOG_INFO, "Filehandle denied for \"%s:%s\"",
394174294Sobrien	     fs->fs_host, path);
395174294Sobrien      } else {
396174294Sobrien	errno = error;	/* XXX */
397174294Sobrien	plog(XLOG_INFO, "Filehandle error for \"%s:%s\": %m",
398174294Sobrien	     fs->fs_host, path);
399174294Sobrien      }
40038494Sobrien
401174294Sobrien      /*
402174294Sobrien       * The error was returned from the remote mount daemon.
403174294Sobrien       * Policy: this error will be cached for now...
404174294Sobrien       */
405174294Sobrien      return error;
40638494Sobrien
407174294Sobrien    case -1:
408174294Sobrien      /*
409174294Sobrien       * Still thinking about it, but we can re-use.
410174294Sobrien       */
411174294Sobrien      fp_save = fp;
412174294Sobrien      reuse_id = TRUE;
41338494Sobrien      break;
41438494Sobrien
415174294Sobrien    default:
416174294Sobrien      /*
417174294Sobrien       * Return the error.
418174294Sobrien       * Policy: make sure we recompute if required again
419174294Sobrien       * in case this was caused by a network failure.
420174294Sobrien       * This can thrash mountd's though...  If you find
421174294Sobrien       * your mountd going slowly then:
422174294Sobrien       * 1.  Add a fork() loop to main.
423174294Sobrien       * 2.  Remove the call to innetgr() and don't use
424174294Sobrien       *     netgroups, especially if you don't use YP.
425174294Sobrien       */
426174294Sobrien      error = fp->fh_error;
427174294Sobrien      fp->fh_error = -1;
428174294Sobrien      return error;
429174294Sobrien    }	/* end of switch statement */
430174294Sobrien  } /* end of ITER loop */
431174294Sobrien
43238494Sobrien  /*
43338494Sobrien   * Not in cache
43438494Sobrien   */
43538494Sobrien  if (fp_save) {
43638494Sobrien    fp = fp_save;
43738494Sobrien    /*
43838494Sobrien     * Re-use existing slot
43938494Sobrien     */
44038494Sobrien    untimeout(fp->fh_cid);
44138494Sobrien    free_srvr(fp->fh_fs);
44238494Sobrien    XFREE(fp->fh_path);
44338494Sobrien  } else {
44438494Sobrien    fp = ALLOC(struct fh_cache);
44538494Sobrien    memset((voidp) fp, 0, sizeof(struct fh_cache));
44638494Sobrien    ins_que(&fp->fh_q, &fh_head);
44738494Sobrien  }
44838494Sobrien  if (!reuse_id)
449174294Sobrien    fp->fh_id = FHID_ALLOC();
450174294Sobrien  fp->fh_wchan = get_mntfs_wchan(mf);
45138494Sobrien  fp->fh_error = -1;
452174294Sobrien  fp->fh_cid = timeout(FH_TTL, discard_fh, (opaque_t) fp);
45338494Sobrien
45438494Sobrien  /*
455174294Sobrien   * If fs->fs_ip is null, remote server is probably down.
45638494Sobrien   */
45738494Sobrien  if (!fs->fs_ip) {
45838494Sobrien    /* Mark the fileserver down and invalid again */
45938494Sobrien    fs->fs_flags &= ~FSF_VALID;
46038494Sobrien    fs->fs_flags |= FSF_DOWN;
46138494Sobrien    error = AM_ERRNO_HOST_DOWN;
46238494Sobrien    return error;
46338494Sobrien  }
46438494Sobrien
46538494Sobrien  /*
466174294Sobrien   * Either fp has been freshly allocated or the address has changed.
467174294Sobrien   * Initialize address and nfs version.  Don't try to re-use the port
468174294Sobrien   * information unless using WebNFS where the port is fixed either by
469174294Sobrien   * the spec or the "port" mount option.
47038494Sobrien   */
47138494Sobrien  if (fp->fh_sin.sin_addr.s_addr != fs->fs_ip->sin_addr.s_addr) {
47238494Sobrien    fp->fh_sin = *fs->fs_ip;
473174294Sobrien    if (!(mf->mf_flags & MFF_WEBNFS))
474174294Sobrien	fp->fh_sin.sin_port = 0;
475310490Scy    fp->fh_nfs_version = SET_FH_VERSION(fs);
47638494Sobrien  }
477174294Sobrien
47838494Sobrien  fp->fh_fs = dup_srvr(fs);
479310490Scy  fp->fh_path = xstrdup(path);
48038494Sobrien
481174294Sobrien  if (mf->mf_flags & MFF_WEBNFS)
482174294Sobrien    error = webnfs_lookup(fp, got_nfs_fh_webnfs, get_mntfs_wchan(mf));
483174294Sobrien  else
484174294Sobrien    error = call_mountd(fp, MOUNTPROC_MNT, got_nfs_fh_mount, get_mntfs_wchan(mf));
48538494Sobrien  if (error) {
48638494Sobrien    /*
48738494Sobrien     * Local error - cache for a short period
48838494Sobrien     * just to prevent thrashing.
48938494Sobrien     */
49038494Sobrien    untimeout(fp->fh_cid);
49138494Sobrien    fp->fh_cid = timeout(error < 0 ? 2 * ALLOWED_MOUNT_TIME : FH_TTL_ERROR,
492174294Sobrien			 discard_fh, (opaque_t) fp);
49338494Sobrien    fp->fh_error = error;
49438494Sobrien  } else {
49538494Sobrien    error = fp->fh_error;
49638494Sobrien  }
49738494Sobrien
49838494Sobrien  return error;
49938494Sobrien}
50038494Sobrien
50138494Sobrien
50238494Sobrienint
50338494Sobrienmake_nfs_auth(void)
50438494Sobrien{
50538494Sobrien  AUTH_CREATE_GIDLIST_TYPE group_wheel = 0;
50638494Sobrien
50738494Sobrien  /* Some NFS mounts (particularly cross-domain) require FQDNs to succeed */
50838494Sobrien
50938494Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI
51038494Sobrien  if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) {
51182794Sobrien    plog(XLOG_INFO, "Using NFS auth for FQHN \"%s\"", hostd);
51238494Sobrien    nfs_auth = authsys_create(hostd, 0, 0, 1, &group_wheel);
51338494Sobrien  } else {
51438494Sobrien    nfs_auth = authsys_create_default();
51538494Sobrien  }
51638494Sobrien#else /* not HAVE_TRANSPORT_TYPE_TLI */
51738494Sobrien  if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) {
51882794Sobrien    plog(XLOG_INFO, "Using NFS auth for FQHN \"%s\"", hostd);
51938494Sobrien    nfs_auth = authunix_create(hostd, 0, 0, 1, &group_wheel);
52038494Sobrien  } else {
52138494Sobrien    nfs_auth = authunix_create_default();
52238494Sobrien  }
52338494Sobrien#endif /* not HAVE_TRANSPORT_TYPE_TLI */
52438494Sobrien
52538494Sobrien  if (!nfs_auth)
52638494Sobrien    return ENOBUFS;
52738494Sobrien
52838494Sobrien  return 0;
52938494Sobrien}
53038494Sobrien
53138494Sobrien
53238494Sobrienstatic int
533174294Sobriencall_mountd(fh_cache *fp, u_long proc, fwd_fun fun, wchan_t wchan)
53438494Sobrien{
53538494Sobrien  struct rpc_msg mnt_msg;
53638494Sobrien  int len;
537174294Sobrien  char iobuf[UDPMSGSIZE];
53838494Sobrien  int error;
53938494Sobrien  u_long mnt_version;
54038494Sobrien
54138494Sobrien  if (!nfs_auth) {
54238494Sobrien    error = make_nfs_auth();
54338494Sobrien    if (error)
54438494Sobrien      return error;
54538494Sobrien  }
54638494Sobrien
54738494Sobrien  if (fp->fh_sin.sin_port == 0) {
548174294Sobrien    u_short mountd_port;
549174294Sobrien    error = get_mountd_port(fp->fh_fs, &mountd_port, wchan);
55038494Sobrien    if (error)
55138494Sobrien      return error;
552174294Sobrien    fp->fh_sin.sin_port = mountd_port;
553310490Scy    dlog("%s: New %d mountd port", __func__, fp->fh_sin.sin_port);
554310490Scy  } else
555310490Scy    dlog("%s: Already had %d mountd port", __func__, fp->fh_sin.sin_port);
55638494Sobrien
55738494Sobrien  /* find the right version of the mount protocol */
55838494Sobrien#ifdef HAVE_FS_NFS3
55938494Sobrien  if (fp->fh_nfs_version == NFS_VERSION3)
560174294Sobrien    mnt_version = AM_MOUNTVERS3;
56138494Sobrien  else
56238494Sobrien#endif /* HAVE_FS_NFS3 */
56338494Sobrien    mnt_version = MOUNTVERS;
56438494Sobrien  plog(XLOG_INFO, "call_mountd: NFS version %d, mount version %d",
56551292Sobrien       (int) fp->fh_nfs_version, (int) mnt_version);
56638494Sobrien
56738494Sobrien  rpc_msg_init(&mnt_msg, MOUNTPROG, mnt_version, MOUNTPROC_NULL);
56838494Sobrien  len = make_rpc_packet(iobuf,
56938494Sobrien			sizeof(iobuf),
57038494Sobrien			proc,
57138494Sobrien			&mnt_msg,
57238494Sobrien			(voidp) &fp->fh_path,
57338494Sobrien			(XDRPROC_T_TYPE) xdr_nfspath,
57438494Sobrien			nfs_auth);
57538494Sobrien
57638494Sobrien  if (len > 0) {
57738494Sobrien    error = fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD, fp->fh_id),
578174294Sobrien		       iobuf,
57938494Sobrien		       len,
58038494Sobrien		       &fp->fh_sin,
58138494Sobrien		       &fp->fh_sin,
582174294Sobrien		       (opaque_t) ((long) fp->fh_id), /* cast to long needed for 64-bit archs */
583174294Sobrien		       fun);
58438494Sobrien  } else {
58538494Sobrien    error = -len;
58638494Sobrien  }
58738494Sobrien
588174294Sobrien  /*
589174294Sobrien   * It may be the case that we're sending to the wrong MOUNTD port.  This
590174294Sobrien   * occurs if mountd is restarted on the server after the port has been
591174294Sobrien   * looked up and stored in the filehandle cache somewhere.  The correct
592174294Sobrien   * solution, if we're going to cache port numbers is to catch the ICMP
593174294Sobrien   * port unreachable reply from the server and cause the portmap request
594174294Sobrien   * to be redone.  The quick solution here is to invalidate the MOUNTD
595174294Sobrien   * port.
596174294Sobrien   */
59738494Sobrien  fp->fh_sin.sin_port = 0;
59838494Sobrien
59938494Sobrien  return error;
60038494Sobrien}
60138494Sobrien
60238494Sobrien
603174294Sobrienstatic int
604174294Sobrienwebnfs_lookup(fh_cache *fp, fwd_fun fun, wchan_t wchan)
605174294Sobrien{
606174294Sobrien  struct rpc_msg wnfs_msg;
607174294Sobrien  int len;
608174294Sobrien  char iobuf[UDPMSGSIZE];
609174294Sobrien  int error;
610174294Sobrien  u_long proc;
611174294Sobrien  XDRPROC_T_TYPE xdr_fn;
612174294Sobrien  voidp argp;
613174294Sobrien  nfsdiropargs args;
614174294Sobrien#ifdef HAVE_FS_NFS3
615174294Sobrien  am_LOOKUP3args args3;
616310490Scy#endif /* HAVE_FS_NFS3 */
617174294Sobrien  char *wnfs_path;
618174294Sobrien  size_t l;
619174294Sobrien
620174294Sobrien  if (!nfs_auth) {
621174294Sobrien    error = make_nfs_auth();
622174294Sobrien    if (error)
623174294Sobrien      return error;
624174294Sobrien  }
625174294Sobrien
626174294Sobrien  if (fp->fh_sin.sin_port == 0) {
627174294Sobrien    /* FIXME: wrong, don't discard sin_port in the first place for WebNFS. */
628174294Sobrien    plog(XLOG_WARNING, "webnfs_lookup: port == 0 for nfs on %s, fixed",
629174294Sobrien	 fp->fh_fs->fs_host);
630174294Sobrien    fp->fh_sin.sin_port = htons(NFS_PORT);
631174294Sobrien  }
632174294Sobrien
633174294Sobrien  /*
634174294Sobrien   * Use native path like the rest of amd (cf. RFC 2054, 6.1).
635174294Sobrien   */
636174294Sobrien  l = strlen(fp->fh_path) + 2;
637174294Sobrien  wnfs_path = (char *) xmalloc(l);
638174294Sobrien  wnfs_path[0] = 0x80;
639174294Sobrien  xstrlcpy(wnfs_path + 1, fp->fh_path, l - 1);
640174294Sobrien
641174294Sobrien  /* find the right program and lookup procedure */
642174294Sobrien#ifdef HAVE_FS_NFS3
643174294Sobrien  if (fp->fh_nfs_version == NFS_VERSION3) {
644174294Sobrien    proc = AM_NFSPROC3_LOOKUP;
645174294Sobrien    xdr_fn = (XDRPROC_T_TYPE) xdr_am_LOOKUP3args;
646174294Sobrien    argp = &args3;
647174294Sobrien    /* WebNFS public file handle */
648174294Sobrien    args3.what.dir.am_fh3_length = 0;
649174294Sobrien    args3.what.name = wnfs_path;
650174294Sobrien  } else {
651174294Sobrien#endif /* HAVE_FS_NFS3 */
652174294Sobrien    proc = NFSPROC_LOOKUP;
653174294Sobrien    xdr_fn = (XDRPROC_T_TYPE) xdr_diropargs;
654174294Sobrien    argp = &args;
655174294Sobrien    /* WebNFS public file handle */
656174294Sobrien    memset(&args.da_fhandle, 0, NFS_FHSIZE);
657174294Sobrien    args.da_name = wnfs_path;
658174294Sobrien#ifdef HAVE_FS_NFS3
659174294Sobrien  }
660174294Sobrien#endif /* HAVE_FS_NFS3 */
661174294Sobrien
662174294Sobrien  plog(XLOG_INFO, "webnfs_lookup: NFS version %d", (int) fp->fh_nfs_version);
663174294Sobrien
664174294Sobrien  rpc_msg_init(&wnfs_msg, NFS_PROGRAM, fp->fh_nfs_version, proc);
665174294Sobrien  len = make_rpc_packet(iobuf,
666174294Sobrien			sizeof(iobuf),
667174294Sobrien			proc,
668174294Sobrien			&wnfs_msg,
669174294Sobrien			argp,
670174294Sobrien			(XDRPROC_T_TYPE) xdr_fn,
671174294Sobrien			nfs_auth);
672174294Sobrien
673174294Sobrien  if (len > 0) {
674174294Sobrien    error = fwd_packet(MK_RPC_XID(RPC_XID_WEBNFS, fp->fh_id),
675174294Sobrien		       iobuf,
676174294Sobrien		       len,
677174294Sobrien		       &fp->fh_sin,
678174294Sobrien		       &fp->fh_sin,
679174294Sobrien		       (opaque_t) ((long) fp->fh_id), /* cast to long needed for 64-bit archs */
680174294Sobrien		       fun);
681174294Sobrien  } else {
682174294Sobrien    error = -len;
683174294Sobrien  }
684174294Sobrien
685174294Sobrien  XFREE(wnfs_path);
686174294Sobrien  return error;
687174294Sobrien}
688174294Sobrien
689174294Sobrien
69038494Sobrien/*
69138494Sobrien * NFS needs the local filesystem, remote filesystem
69238494Sobrien * remote hostname.
69338494Sobrien * Local filesystem defaults to remote and vice-versa.
69438494Sobrien */
695174294Sobrienstatic char *
69638494Sobriennfs_match(am_opts *fo)
69738494Sobrien{
69838494Sobrien  char *xmtab;
699174294Sobrien  size_t l;
70038494Sobrien
70138494Sobrien  if (fo->opt_fs && !fo->opt_rfs)
70238494Sobrien    fo->opt_rfs = fo->opt_fs;
70338494Sobrien  if (!fo->opt_rfs) {
70438494Sobrien    plog(XLOG_USER, "nfs: no remote filesystem specified");
70538494Sobrien    return NULL;
70638494Sobrien  }
70738494Sobrien  if (!fo->opt_rhost) {
70838494Sobrien    plog(XLOG_USER, "nfs: no remote host specified");
70938494Sobrien    return NULL;
71038494Sobrien  }
71138494Sobrien
71238494Sobrien  /*
71338494Sobrien   * Determine magic cookie to put in mtab
71438494Sobrien   */
715174294Sobrien  l = strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2;
716174294Sobrien  xmtab = (char *) xmalloc(l);
717174294Sobrien  xsnprintf(xmtab, l, "%s:%s", fo->opt_rhost, fo->opt_rfs);
71838494Sobrien  dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
71938494Sobrien       fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
72038494Sobrien
72138494Sobrien  return xmtab;
72238494Sobrien}
72338494Sobrien
72438494Sobrien
72538494Sobrien/*
72638494Sobrien * Initialize am structure for nfs
72738494Sobrien */
728174294Sobrienstatic int
72938494Sobriennfs_init(mntfs *mf)
73038494Sobrien{
73138494Sobrien  int error;
73238494Sobrien  am_nfs_handle_t fhs;
73338494Sobrien  char *colon;
73438494Sobrien
735310490Scy#ifdef NO_FALLBACK
736310490Scy  /*
737310490Scy   * We don't need file handles for NFS version 4, but we can fall back to
738310490Scy   * version 3, so we allocate anyway
739310490Scy   */
740310490Scy#ifdef HAVE_FS_NFS4
741310490Scy  if (mf->mf_server->fs_version == NFS_VERSION4)
742310490Scy    return 0;
743310490Scy#endif /* HAVE_FS_NFS4 */
744310490Scy#endif /* NO_FALLBACK */
745310490Scy
746174294Sobrien  if (mf->mf_private) {
747174294Sobrien    if (mf->mf_flags & MFF_NFS_SCALEDOWN) {
748174294Sobrien      fserver *fs;
74938494Sobrien
750174294Sobrien      /* tell remote mountd that we're done with this filehandle */
751174294Sobrien      mf->mf_ops->umounted(mf);
752174294Sobrien
753174294Sobrien      mf->mf_prfree(mf->mf_private);
754310490Scy      mf->mf_private = NULL;
755310490Scy      mf->mf_prfree = NULL;
756310490Scy
757174294Sobrien      fs = mf->mf_ops->ffserver(mf);
758174294Sobrien      free_srvr(mf->mf_server);
759174294Sobrien      mf->mf_server = fs;
760174294Sobrien    } else
761174294Sobrien      return 0;
762174294Sobrien  }
763174294Sobrien
76438494Sobrien  colon = strchr(mf->mf_info, ':');
76538494Sobrien  if (colon == 0)
76638494Sobrien    return ENOENT;
76738494Sobrien
768174294Sobrien  error = prime_nfs_fhandle_cache(colon + 1, mf->mf_server, &fhs, mf);
76938494Sobrien  if (!error) {
770174294Sobrien    mf->mf_private = (opaque_t) ALLOC(am_nfs_handle_t);
771174294Sobrien    mf->mf_prfree = (void (*)(opaque_t)) free;
77238494Sobrien    memmove(mf->mf_private, (voidp) &fhs, sizeof(fhs));
77338494Sobrien  }
77438494Sobrien  return error;
77538494Sobrien}
77638494Sobrien
77738494Sobrien
77838494Sobrienint
779174294Sobrienmount_nfs_fh(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf)
78038494Sobrien{
78138494Sobrien  MTYPE_TYPE type;
78238494Sobrien  char *colon;
783174294Sobrien  char *xopts=NULL, transp_timeo_opts[40], transp_retrans_opts[40];
78438494Sobrien  char host[MAXHOSTNAMELEN + MAXPATHLEN + 2];
78538494Sobrien  fserver *fs = mf->mf_server;
78638494Sobrien  u_long nfs_version = fs->fs_version;
78738494Sobrien  char *nfs_proto = fs->fs_proto; /* "tcp" or "udp" */
788174294Sobrien  int on_autofs = mf->mf_flags & MFF_ON_AUTOFS;
78938494Sobrien  int error;
79038494Sobrien  int genflags;
79138494Sobrien  int retry;
792174294Sobrien  int proto = AMU_TYPE_NONE;
79338494Sobrien  mntent_t mnt;
794310490Scy  void *argsp;
79538494Sobrien  nfs_args_t nfs_args;
796310490Scy#ifdef HAVE_FS_NFS4
797310490Scy  nfs4_args_t nfs4_args;
798310490Scy#endif /* HAVE_FS_NFS4 */
79938494Sobrien
80038494Sobrien  /*
80138494Sobrien   * Extract HOST name to give to kernel.
80238494Sobrien   * Some systems like osf1/aix3/bsd44 variants may need old code
80338494Sobrien   * for NFS_ARGS_NEEDS_PATH.
80438494Sobrien   */
80538494Sobrien  if (!(colon = strchr(fs_name, ':')))
80638494Sobrien    return ENOENT;
80738494Sobrien#ifdef MOUNT_TABLE_ON_FILE
80838494Sobrien  *colon = '\0';
80938494Sobrien#endif /* MOUNT_TABLE_ON_FILE */
810174294Sobrien  xstrlcpy(host, fs_name, sizeof(host));
81138494Sobrien#ifdef MOUNT_TABLE_ON_FILE
81238494Sobrien  *colon = ':';
81338494Sobrien#endif /* MOUNT_TABLE_ON_FILE */
81438494Sobrien#ifdef MAXHOSTNAMELEN
81538494Sobrien  /* most kernels have a name length restriction */
81638494Sobrien  if (strlen(host) >= MAXHOSTNAMELEN)
817174294Sobrien    xstrlcpy(host + MAXHOSTNAMELEN - 3, "..",
818174294Sobrien	     sizeof(host) - MAXHOSTNAMELEN + 3);
81938494Sobrien#endif /* MAXHOSTNAMELEN */
82038494Sobrien
821174294Sobrien  /*
822174294Sobrien   * Create option=VAL for udp/tcp specific timeouts and retrans values, but
823174294Sobrien   * only if these options were specified.
824174294Sobrien   */
825174294Sobrien
826174294Sobrien  transp_timeo_opts[0] = transp_retrans_opts[0] = '\0';	/* initialize */
827174294Sobrien  if (STREQ(nfs_proto, "udp"))
828174294Sobrien    proto = AMU_TYPE_UDP;
829174294Sobrien  else if (STREQ(nfs_proto, "tcp"))
830174294Sobrien    proto = AMU_TYPE_TCP;
831174294Sobrien  if (proto != AMU_TYPE_NONE) {
832174294Sobrien    if (gopt.amfs_auto_timeo[proto] > 0)
833174294Sobrien      xsnprintf(transp_timeo_opts, sizeof(transp_timeo_opts), "%s=%d,",
834174294Sobrien		MNTTAB_OPT_TIMEO, gopt.amfs_auto_timeo[proto]);
835174294Sobrien    if (gopt.amfs_auto_retrans[proto] > 0)
836174294Sobrien      xsnprintf(transp_retrans_opts, sizeof(transp_retrans_opts), "%s=%d,",
837174294Sobrien		MNTTAB_OPT_RETRANS, gopt.amfs_auto_retrans[proto]);
838174294Sobrien  }
839174294Sobrien
84051292Sobrien  if (mf->mf_remopts && *mf->mf_remopts &&
84151292Sobrien      !islocalnet(fs->fs_ip->sin_addr.s_addr)) {
84251292Sobrien    plog(XLOG_INFO, "Using remopts=\"%s\"", mf->mf_remopts);
843174294Sobrien    /* use transp_opts first, so map-specific opts will override */
844174294Sobrien    xopts = str3cat(xopts, transp_timeo_opts, transp_retrans_opts, mf->mf_remopts);
84551292Sobrien  } else {
846174294Sobrien    /* use transp_opts first, so map-specific opts will override */
847174294Sobrien    xopts = str3cat(xopts, transp_timeo_opts, transp_retrans_opts, mf->mf_mopts);
84851292Sobrien  }
84938494Sobrien
85038494Sobrien  memset((voidp) &mnt, 0, sizeof(mnt));
851174294Sobrien  mnt.mnt_dir = mntdir;
85238494Sobrien  mnt.mnt_fsname = fs_name;
85338494Sobrien  mnt.mnt_opts = xopts;
85438494Sobrien
85538494Sobrien  /*
85638494Sobrien   * Set mount types accordingly
85738494Sobrien   */
858310490Scy#ifdef HAVE_FS_NFS3
85938494Sobrien  if (nfs_version == NFS_VERSION3) {
86038494Sobrien    type = MOUNT_TYPE_NFS3;
86138494Sobrien    /*
86238494Sobrien     * Systems that include the mount table "vers" option generally do not
86338494Sobrien     * set the mnttab entry to "nfs3", but to "nfs" and then they set
86438494Sobrien     * "vers=3".  Setting it to "nfs3" works, but it may break some things
86538494Sobrien     * like "df -t nfs" and the "quota" program (esp. on Solaris and Irix).
86638494Sobrien     * So on those systems, set it to "nfs".
86738494Sobrien     * Note: MNTTAB_OPT_VERS is always set for NFS3 (see am_compat.h).
86838494Sobrien     */
869310490Scy    argsp = &nfs_args;
87038494Sobrien# if defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE)
87138494Sobrien    mnt.mnt_type = MNTTAB_TYPE_NFS;
87238494Sobrien# else /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */
87338494Sobrien    mnt.mnt_type = MNTTAB_TYPE_NFS3;
87438494Sobrien# endif /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */
875310490Scy# ifdef HAVE_FS_NFS4
876310490Scy  } else if (nfs_version == NFS_VERSION4) {
877310490Scy    argsp = &nfs4_args;
878310490Scy    type = MOUNT_TYPE_NFS4;
879310490Scy    mnt.mnt_type = MNTTAB_TYPE_NFS4;
880310490Scy# endif /* HAVE_FS_NFS4 */
881310490Scy  } else
882310490Scy#endif /* HAVE_FS_NFS3 */
883310490Scy  {
884310490Scy    argsp = &nfs_args;
88538494Sobrien    type = MOUNT_TYPE_NFS;
88638494Sobrien    mnt.mnt_type = MNTTAB_TYPE_NFS;
88738494Sobrien  }
88851292Sobrien  plog(XLOG_INFO, "mount_nfs_fh: NFS version %d", (int) nfs_version);
88938494Sobrien  plog(XLOG_INFO, "mount_nfs_fh: using NFS transport %s", nfs_proto);
89038494Sobrien
89138494Sobrien  retry = hasmntval(&mnt, MNTTAB_OPT_RETRY);
89238494Sobrien  if (retry <= 0)
89338494Sobrien    retry = 1;			/* XXX */
89438494Sobrien
89538494Sobrien  genflags = compute_mount_flags(&mnt);
896174294Sobrien#ifdef HAVE_FS_AUTOFS
897174294Sobrien  if (on_autofs)
898174294Sobrien    genflags |= autofs_compute_mount_flags(&mnt);
899174294Sobrien#endif /* HAVE_FS_AUTOFS */
90038494Sobrien
901310490Scy   /* setup the many fields and flags within nfs_args */
902310490Scy   compute_nfs_args(argsp,
903310490Scy		    &mnt,
904310490Scy		    genflags,
905310490Scy		    NULL,	/* struct netconfig *nfsncp */
906310490Scy		    fs->fs_ip,
907310490Scy		    nfs_version,
908310490Scy		    nfs_proto,
909310490Scy		    fhp,
910310490Scy		    host,
911310490Scy		    fs_name);
91238494Sobrien
91338494Sobrien  /* finally call the mounting function */
914174294Sobrien  if (amuDebug(D_TRACE)) {
915310490Scy    print_nfs_args(argsp, nfs_version);
91682794Sobrien    plog(XLOG_DEBUG, "Generic mount flags 0x%x used for NFS mount", genflags);
91751292Sobrien  }
918310490Scy  error = mount_fs(&mnt, genflags, argsp, retry, type,
919310490Scy		   nfs_version, nfs_proto, mnttab_file_name, on_autofs);
920310490Scy  XFREE(mnt.mnt_opts);
921310490Scy  discard_nfs_args(argsp, nfs_version);
92238494Sobrien
923310490Scy#ifdef HAVE_FS_NFS4
924310490Scy# ifndef NO_FALLBACK
925310490Scy  /*
926310490Scy   * If we are using a v4 file handle, we try a v3 if we get back:
927310490Scy   * 	ENOENT: NFS v4 has a different export list than v3
928310490Scy   * 	EPERM: Kernels <= 2.6.18 return that, instead of ENOENT
929310490Scy   */
930310490Scy  if ((error == ENOENT || error == EPERM) && nfs_version == NFS_VERSION4) {
931310490Scy    plog(XLOG_DEBUG, "Could not find NFS 4 mount, trying again with NFS 3");
932310490Scy    fs->fs_version = NFS_VERSION3;
933310490Scy    error = mount_nfs_fh(fhp, mntdir, fs_name, mf);
934310490Scy    if (error)
935310490Scy      fs->fs_version = NFS_VERSION4;
936310490Scy  }
937310490Scy# endif /* NO_FALLBACK */
938310490Scy#endif /* HAVE_FS_NFS4 */
93938494Sobrien
94038494Sobrien  return error;
94138494Sobrien}
94238494Sobrien
94338494Sobrien
94438494Sobrienstatic int
945174294Sobriennfs_mount(am_node *am, mntfs *mf)
94638494Sobrien{
947174294Sobrien  int error = 0;
948174294Sobrien  mntent_t mnt;
949174294Sobrien
950310490Scy  if (!mf->mf_private && mf->mf_server->fs_version != 4) {
951174294Sobrien    plog(XLOG_ERROR, "Missing filehandle for %s", mf->mf_info);
95238494Sobrien    return EINVAL;
95338494Sobrien  }
95438494Sobrien
955310490Scy  if (mf->mf_mopts == NULL) {
956310490Scy    plog(XLOG_ERROR, "Missing mount options for %s", mf->mf_info);
957310490Scy    return EINVAL;
958310490Scy  }
959310490Scy
960174294Sobrien  mnt.mnt_opts = mf->mf_mopts;
961174294Sobrien  if (amu_hasmntopt(&mnt, "softlookup") ||
962174294Sobrien      (amu_hasmntopt(&mnt, "soft") && !amu_hasmntopt(&mnt, "nosoftlookup")))
963174294Sobrien    am->am_flags |= AMF_SOFTLOOKUP;
96438494Sobrien
965174294Sobrien  error = mount_nfs_fh((am_nfs_handle_t *) mf->mf_private,
966174294Sobrien		       mf->mf_mount,
967174294Sobrien		       mf->mf_info,
968174294Sobrien		       mf);
96938494Sobrien
97038494Sobrien  if (error) {
97138494Sobrien    errno = error;
97238494Sobrien    dlog("mount_nfs: %m");
97338494Sobrien  }
97438494Sobrien
97538494Sobrien  return error;
97638494Sobrien}
97738494Sobrien
97838494Sobrien
979174294Sobrienstatic int
980174294Sobriennfs_umount(am_node *am, mntfs *mf)
98138494Sobrien{
982174294Sobrien  int unmount_flags, new_unmount_flags, error;
98338494Sobrien
984310490Scy  dlog("attempting nfs umount");
985174294Sobrien  unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
986174294Sobrien  error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags);
987174294Sobrien
988174294Sobrien#if defined(HAVE_UMOUNT2) && (defined(MNT2_GEN_OPT_FORCE) || defined(MNT2_GEN_OPT_DETACH))
98938494Sobrien  /*
990174294Sobrien   * If the attempt to unmount failed with EBUSY, and this fserver was
991174294Sobrien   * marked for forced unmounts, then use forced/lazy unmounts.
992174294Sobrien   */
993174294Sobrien  if (error == EBUSY &&
994174294Sobrien      gopt.flags & CFM_FORCED_UNMOUNTS &&
995174294Sobrien      mf->mf_server->fs_flags & FSF_FORCE_UNMOUNT) {
996174294Sobrien    plog(XLOG_INFO, "EZK: nfs_umount: trying forced/lazy unmounts");
997174294Sobrien    /*
998174294Sobrien     * XXX: turning off the FSF_FORCE_UNMOUNT may not be perfectly
999174294Sobrien     * incorrect.  Multiple nodes may need to be timed out and restarted for
1000174294Sobrien     * a single hung fserver.
1001174294Sobrien     */
1002174294Sobrien    mf->mf_server->fs_flags &= ~FSF_FORCE_UNMOUNT;
1003174294Sobrien    new_unmount_flags = unmount_flags | AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH;
1004174294Sobrien    error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, new_unmount_flags);
1005174294Sobrien  }
1006174294Sobrien#endif /* HAVE_UMOUNT2 && (MNT2_GEN_OPT_FORCE || MNT2_GEN_OPT_DETACH) */
1007174294Sobrien
1008174294Sobrien  /*
100938494Sobrien   * Here is some code to unmount 'restarted' file systems.
101038494Sobrien   * The restarted file systems are marked as 'nfs', not
101138494Sobrien   * 'host', so we only have the map information for the
101238494Sobrien   * the top-level mount.  The unmount will fail (EBUSY)
101338494Sobrien   * if there are anything else from the NFS server mounted
101438494Sobrien   * below the mount-point.  This code checks to see if there
101538494Sobrien   * is anything mounted with the same prefix as the
101638494Sobrien   * file system to be unmounted ("/a/b/c" when unmounting "/a/b").
101738494Sobrien   * If there is, and it is a 'restarted' file system, we unmount
101838494Sobrien   * it.
101938494Sobrien   * Added by Mike Mitchell, mcm@unx.sas.com, 09/08/93
102038494Sobrien   */
102138494Sobrien  if (error == EBUSY) {
102238494Sobrien    mntfs *new_mf;
102338494Sobrien    int len = strlen(mf->mf_mount);
102438494Sobrien    int didsome = 0;
102538494Sobrien
102638494Sobrien    ITER(new_mf, mntfs, &mfhead) {
102738494Sobrien      if (new_mf->mf_ops != mf->mf_ops ||
102838494Sobrien	  new_mf->mf_refc > 1 ||
102938494Sobrien	  mf == new_mf ||
103038494Sobrien	  ((new_mf->mf_flags & (MFF_MOUNTED | MFF_UNMOUNTING | MFF_RESTART)) == (MFF_MOUNTED | MFF_RESTART)))
103138494Sobrien	continue;
103238494Sobrien
103338494Sobrien      if (NSTREQ(mf->mf_mount, new_mf->mf_mount, len) &&
103438494Sobrien	  new_mf->mf_mount[len] == '/') {
1035174294Sobrien	new_unmount_flags =
1036174294Sobrien	  (new_mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
1037174294Sobrien	UMOUNT_FS(new_mf->mf_mount, mnttab_file_name, new_unmount_flags);
103838494Sobrien	didsome = 1;
103938494Sobrien      }
104038494Sobrien    }
104138494Sobrien    if (didsome)
1042174294Sobrien      error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags);
104338494Sobrien  }
104438494Sobrien  if (error)
104538494Sobrien    return error;
104638494Sobrien
104738494Sobrien  return 0;
104838494Sobrien}
104938494Sobrien
105038494Sobrien
1051174294Sobrienstatic void
1052174294Sobriennfs_umounted(mntfs *mf)
105338494Sobrien{
105438494Sobrien  fserver *fs;
105538494Sobrien  char *colon, *path;
105638494Sobrien
105738494Sobrien  if (mf->mf_error || mf->mf_refc > 1)
105838494Sobrien    return;
105938494Sobrien
1060174294Sobrien  /*
1061174294Sobrien   * No need to inform mountd when WebNFS is in use.
1062174294Sobrien   */
1063174294Sobrien  if (mf->mf_flags & MFF_WEBNFS)
1064174294Sobrien    return;
106538494Sobrien
106638494Sobrien  /*
106738494Sobrien   * Call the mount daemon on the server to announce that we are not using
106838494Sobrien   * the fs any more.
106938494Sobrien   *
1070174294Sobrien   * XXX: This is *wrong*.  The mountd should be called when the fhandle is
107138494Sobrien   * flushed from the cache, and a reference held to the cached entry while
107238494Sobrien   * the fs is mounted...
107338494Sobrien   */
1074174294Sobrien  fs = mf->mf_server;
107538494Sobrien  colon = path = strchr(mf->mf_info, ':');
107638494Sobrien  if (fs && colon) {
107738494Sobrien    fh_cache f;
107838494Sobrien
107938494Sobrien    dlog("calling mountd for %s", mf->mf_info);
108038494Sobrien    *path++ = '\0';
108138494Sobrien    f.fh_path = path;
108238494Sobrien    f.fh_sin = *fs->fs_ip;
108338494Sobrien    f.fh_sin.sin_port = (u_short) 0;
1084310490Scy    f.fh_nfs_version = SET_FH_VERSION(fs);
108538494Sobrien    f.fh_fs = fs;
108638494Sobrien    f.fh_id = 0;
108738494Sobrien    f.fh_error = 0;
1088310490Scy    prime_nfs_fhandle_cache(colon + 1, mf->mf_server, (am_nfs_handle_t *) NULL, mf);
1089310490Scy    call_mountd(&f, MOUNTPROC_UMNT, (fwd_fun *) NULL, (wchan_t) NULL);
109038494Sobrien    *colon = ':';
109138494Sobrien  }
109238494Sobrien}
1093