ops_nfs.c revision 51292
138494Sobrien/*
251292Sobrien * Copyright (c) 1997-1999 Erez Zadok
338494Sobrien * Copyright (c) 1990 Jan-Simon Pendry
438494Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
538494Sobrien * Copyright (c) 1990 The Regents of the University of California.
638494Sobrien * All rights reserved.
738494Sobrien *
838494Sobrien * This code is derived from software contributed to Berkeley by
938494Sobrien * Jan-Simon Pendry at Imperial College, London.
1038494Sobrien *
1138494Sobrien * Redistribution and use in source and binary forms, with or without
1238494Sobrien * modification, are permitted provided that the following conditions
1338494Sobrien * are met:
1438494Sobrien * 1. Redistributions of source code must retain the above copyright
1538494Sobrien *    notice, this list of conditions and the following disclaimer.
1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1738494Sobrien *    notice, this list of conditions and the following disclaimer in the
1838494Sobrien *    documentation and/or other materials provided with the distribution.
1938494Sobrien * 3. All advertising materials mentioning features or use of this software
2042629Sobrien *    must display the following acknowledgment:
2138494Sobrien *      This product includes software developed by the University of
2238494Sobrien *      California, Berkeley and its contributors.
2338494Sobrien * 4. Neither the name of the University nor the names of its contributors
2438494Sobrien *    may be used to endorse or promote products derived from this software
2538494Sobrien *    without specific prior written permission.
2638494Sobrien *
2738494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2838494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2938494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3038494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3138494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3238494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3338494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3438494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3538494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3638494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3738494Sobrien * SUCH DAMAGE.
3838494Sobrien *
3938494Sobrien *      %W% (Berkeley) %G%
4038494Sobrien *
4151292Sobrien * $Id: ops_nfs.c,v 1.5 1999/03/13 17:03:28 ezk Exp $
4238494Sobrien *
4338494Sobrien */
4438494Sobrien
4538494Sobrien/*
4638494Sobrien * Network file system
4738494Sobrien */
4838494Sobrien
4938494Sobrien#ifdef HAVE_CONFIG_H
5038494Sobrien# include <config.h>
5138494Sobrien#endif /* HAVE_CONFIG_H */
5238494Sobrien#include <am_defs.h>
5338494Sobrien#include <amd.h>
5438494Sobrien
5538494Sobrien/*
5638494Sobrien * Convert from nfsstat to UN*X error code
5738494Sobrien */
5838494Sobrien#define unx_error(e)	((int)(e))
5938494Sobrien
6038494Sobrien/*
6138494Sobrien * FH_TTL is the time a file handle will remain in the cache since
6238494Sobrien * last being used.  If the file handle becomes invalid, then it
6338494Sobrien * will be flushed anyway.
6438494Sobrien */
6538494Sobrien#define	FH_TTL			(5 * 60) /* five minutes */
6638494Sobrien#define	FH_TTL_ERROR		(30) /* 30 seconds */
6738494Sobrien#define	FHID_ALLOC(struct)	(++fh_id)
6838494Sobrien
6938494Sobrien/*
7038494Sobrien * The NFS layer maintains a cache of file handles.
7138494Sobrien * This is *fundamental* to the implementation and
7238494Sobrien * also allows quick remounting when a filesystem
7338494Sobrien * is accessed soon after timing out.
7438494Sobrien *
7538494Sobrien * The NFS server layer knows to flush this cache
7638494Sobrien * when a server goes down so avoiding stale handles.
7738494Sobrien *
7838494Sobrien * Each cache entry keeps a hard reference to
7938494Sobrien * the corresponding server.  This ensures that
8038494Sobrien * the server keepalive information is maintained.
8138494Sobrien *
8238494Sobrien * The copy of the sockaddr_in here is taken so
8338494Sobrien * that the port can be twiddled to talk to mountd
8438494Sobrien * instead of portmap or the NFS server as used
8538494Sobrien * elsewhere.
8638494Sobrien * The port# is flushed if a server goes down.
8738494Sobrien * The IP address is never flushed - we assume
8838494Sobrien * that the address of a mounted machine never
8938494Sobrien * changes.  If it does, then you have other
9038494Sobrien * problems...
9138494Sobrien */
9238494Sobrientypedef struct fh_cache fh_cache;
9338494Sobrienstruct fh_cache {
9438494Sobrien  qelem			fh_q;		/* List header */
9538494Sobrien  voidp			fh_wchan;	/* Wait channel */
9638494Sobrien  int			fh_error;	/* Valid data? */
9738494Sobrien  int			fh_id;		/* Unique id */
9838494Sobrien  int			fh_cid;		/* Callout id */
9938494Sobrien  u_long		fh_nfs_version;	/* highest NFS version on host */
10038494Sobrien  am_nfs_handle_t	fh_nfs_handle;	/* Handle on filesystem */
10138494Sobrien  struct sockaddr_in	fh_sin;		/* Address of mountd */
10238494Sobrien  fserver		*fh_fs;		/* Server holding filesystem */
10338494Sobrien  char			*fh_path;	/* Filesystem on host */
10438494Sobrien};
10538494Sobrien
10638494Sobrien/* forward definitions */
10738494Sobrienstatic int call_mountd(fh_cache *fp, u_long proc, fwd_fun f, voidp wchan);
10838494Sobrienstatic int fh_id = 0;
10938494Sobrien
11038494Sobrien/* globals */
11138494SobrienAUTH *nfs_auth;
11238494Sobrienqelem fh_head = {&fh_head, &fh_head};
11338494Sobrien
11438494Sobrien/*
11538494Sobrien * Network file system operations
11638494Sobrien */
11738494Sobrienam_ops nfs_ops =
11838494Sobrien{
11938494Sobrien  "nfs",
12038494Sobrien  nfs_match,
12138494Sobrien  nfs_init,
12238494Sobrien  amfs_auto_fmount,
12338494Sobrien  nfs_fmount,
12438494Sobrien  amfs_auto_fumount,
12538494Sobrien  nfs_fumount,
12638494Sobrien  amfs_error_lookuppn,
12738494Sobrien  amfs_error_readdir,
12838494Sobrien  0,				/* nfs_readlink */
12938494Sobrien  0,				/* nfs_mounted */
13038494Sobrien  nfs_umounted,
13138494Sobrien  find_nfs_srvr,
13238494Sobrien  FS_MKMNT | FS_BACKGROUND | FS_AMQINFO
13338494Sobrien};
13438494Sobrien
13538494Sobrien
13638494Sobrienstatic fh_cache *
13738494Sobrienfind_nfs_fhandle_cache(voidp idv, int done)
13838494Sobrien{
13938494Sobrien  fh_cache *fp, *fp2 = 0;
14038494Sobrien  int id = (long) idv;		/* for 64-bit archs */
14138494Sobrien
14238494Sobrien  ITER(fp, fh_cache, &fh_head) {
14338494Sobrien    if (fp->fh_id == id) {
14438494Sobrien      fp2 = fp;
14538494Sobrien      break;
14638494Sobrien    }
14738494Sobrien  }
14838494Sobrien
14938494Sobrien#ifdef DEBUG
15038494Sobrien  if (fp2) {
15151292Sobrien    dlog("fh cache gives fp %#lx, fs %s", (unsigned long) fp2, fp2->fh_path);
15238494Sobrien  } else {
15338494Sobrien    dlog("fh cache search failed");
15438494Sobrien  }
15538494Sobrien#endif /* DEBUG */
15638494Sobrien
15738494Sobrien  if (fp2 && !done) {
15838494Sobrien    fp2->fh_error = ETIMEDOUT;
15938494Sobrien    return 0;
16038494Sobrien  }
16138494Sobrien
16238494Sobrien  return fp2;
16338494Sobrien}
16438494Sobrien
16538494Sobrien
16638494Sobrien/*
16738494Sobrien * Called when a filehandle appears
16838494Sobrien */
16938494Sobrienstatic void
17038494Sobriengot_nfs_fh(voidp pkt, int len, struct sockaddr_in * sa, struct sockaddr_in * ia, voidp idv, int done)
17138494Sobrien{
17238494Sobrien  fh_cache *fp;
17338494Sobrien
17438494Sobrien  fp = find_nfs_fhandle_cache(idv, done);
17538494Sobrien  if (!fp)
17638494Sobrien    return;
17738494Sobrien
17838494Sobrien  /*
17938494Sobrien   * retrieve the correct RPC reply for the file handle, based on the
18038494Sobrien   * NFS protocol version.
18138494Sobrien   */
18238494Sobrien#ifdef HAVE_FS_NFS3
18338494Sobrien  if (fp->fh_nfs_version == NFS_VERSION3)
18438494Sobrien    fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &fp->fh_nfs_handle.v3,
18538494Sobrien				    (XDRPROC_T_TYPE) xdr_mountres3);
18638494Sobrien  else
18738494Sobrien#endif /* HAVE_FS_NFS3 */
18838494Sobrien    fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &fp->fh_nfs_handle.v2,
18938494Sobrien				    (XDRPROC_T_TYPE) xdr_fhstatus);
19038494Sobrien
19138494Sobrien  if (!fp->fh_error) {
19238494Sobrien#ifdef DEBUG
19338494Sobrien    dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
19438494Sobrien#endif /* DEBUG */
19538494Sobrien
19638494Sobrien    /*
19738494Sobrien     * Wakeup anything sleeping on this filehandle
19838494Sobrien     */
19938494Sobrien    if (fp->fh_wchan) {
20038494Sobrien#ifdef DEBUG
20151292Sobrien      dlog("Calling wakeup on %#lx", (unsigned long) fp->fh_wchan);
20238494Sobrien#endif /* DEBUG */
20338494Sobrien      wakeup(fp->fh_wchan);
20438494Sobrien    }
20538494Sobrien  }
20638494Sobrien}
20738494Sobrien
20838494Sobrien
20938494Sobrienvoid
21038494Sobrienflush_nfs_fhandle_cache(fserver *fs)
21138494Sobrien{
21238494Sobrien  fh_cache *fp;
21338494Sobrien
21438494Sobrien  ITER(fp, fh_cache, &fh_head) {
21538494Sobrien    if (fp->fh_fs == fs || fs == 0) {
21638494Sobrien      fp->fh_sin.sin_port = (u_short) 0;
21738494Sobrien      fp->fh_error = -1;
21838494Sobrien    }
21938494Sobrien  }
22038494Sobrien}
22138494Sobrien
22238494Sobrien
22338494Sobrienstatic void
22438494Sobriendiscard_fh(voidp v)
22538494Sobrien{
22638494Sobrien  fh_cache *fp = v;
22738494Sobrien
22838494Sobrien  rem_que(&fp->fh_q);
22938494Sobrien  if (fp->fh_fs) {
23038494Sobrien#ifdef DEBUG
23138494Sobrien    dlog("Discarding filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
23238494Sobrien#endif /* DEBUG */
23338494Sobrien    free_srvr(fp->fh_fs);
23438494Sobrien  }
23538494Sobrien  if (fp->fh_path)
23638494Sobrien    XFREE(fp->fh_path);
23738494Sobrien  XFREE(fp);
23838494Sobrien}
23938494Sobrien
24038494Sobrien
24138494Sobrien/*
24238494Sobrien * Determine the file handle for a node
24338494Sobrien */
24438494Sobrienstatic int
24538494Sobrienprime_nfs_fhandle_cache(char *path, fserver *fs, am_nfs_handle_t *fhbuf, voidp wchan)
24638494Sobrien{
24738494Sobrien  fh_cache *fp, *fp_save = 0;
24838494Sobrien  int error;
24938494Sobrien  int reuse_id = FALSE;
25038494Sobrien
25138494Sobrien#ifdef DEBUG
25238494Sobrien  dlog("Searching cache for %s:%s", fs->fs_host, path);
25338494Sobrien#endif /* DEBUG */
25438494Sobrien
25538494Sobrien  /*
25638494Sobrien   * First search the cache
25738494Sobrien   */
25838494Sobrien  ITER(fp, fh_cache, &fh_head) {
25938494Sobrien    if (fs == fp->fh_fs && STREQ(path, fp->fh_path)) {
26038494Sobrien      switch (fp->fh_error) {
26138494Sobrien      case 0:
26251292Sobrien	plog(XLOG_INFO, "prime_nfs_fhandle_cache: NFS version %d", (int) fp->fh_nfs_version);
26338494Sobrien#ifdef HAVE_FS_NFS3
26438494Sobrien	if (fp->fh_nfs_version == NFS_VERSION3)
26538494Sobrien	  error = fp->fh_error = unx_error(fp->fh_nfs_handle.v3.fhs_status);
26638494Sobrien	else
26738494Sobrien#endif /* HAVE_FS_NFS3 */
26838494Sobrien	  error = fp->fh_error = unx_error(fp->fh_nfs_handle.v2.fhs_status);
26938494Sobrien	if (error == 0) {
27038494Sobrien	  if (fhbuf) {
27138494Sobrien#ifdef HAVE_FS_NFS3
27238494Sobrien	    if (fp->fh_nfs_version == NFS_VERSION3)
27338494Sobrien	      memmove((voidp) &(fhbuf->v3), (voidp) &(fp->fh_nfs_handle.v3),
27438494Sobrien		      sizeof(fp->fh_nfs_handle.v3));
27538494Sobrien	    else
27638494Sobrien#endif /* HAVE_FS_NFS3 */
27738494Sobrien	      memmove((voidp) &(fhbuf->v2), (voidp) &(fp->fh_nfs_handle.v2),
27838494Sobrien		      sizeof(fp->fh_nfs_handle.v2));
27938494Sobrien	  }
28038494Sobrien	  if (fp->fh_cid)
28138494Sobrien	    untimeout(fp->fh_cid);
28238494Sobrien	  fp->fh_cid = timeout(FH_TTL, discard_fh, (voidp) fp);
28338494Sobrien	} else if (error == EACCES) {
28438494Sobrien	  /*
28538494Sobrien	   * Now decode the file handle return code.
28638494Sobrien	   */
28738494Sobrien	  plog(XLOG_INFO, "Filehandle denied for \"%s:%s\"",
28838494Sobrien	       fs->fs_host, path);
28938494Sobrien	} else {
29038494Sobrien	  errno = error;	/* XXX */
29138494Sobrien	  plog(XLOG_INFO, "Filehandle error for \"%s:%s\": %m",
29238494Sobrien	       fs->fs_host, path);
29338494Sobrien	}
29438494Sobrien
29538494Sobrien	/*
29638494Sobrien	 * The error was returned from the remote mount daemon.
29738494Sobrien	 * Policy: this error will be cached for now...
29838494Sobrien	 */
29938494Sobrien	return error;
30038494Sobrien
30138494Sobrien      case -1:
30238494Sobrien	/*
30338494Sobrien	 * Still thinking about it, but we can re-use.
30438494Sobrien	 */
30538494Sobrien	fp_save = fp;
30638494Sobrien	reuse_id = TRUE;
30738494Sobrien	break;
30838494Sobrien
30938494Sobrien      default:
31038494Sobrien	/*
31138494Sobrien	 * Return the error.
31238494Sobrien	 * Policy: make sure we recompute if required again
31338494Sobrien	 * in case this was caused by a network failure.
31438494Sobrien	 * This can thrash mountd's though...  If you find
31538494Sobrien	 * your mountd going slowly then:
31638494Sobrien	 * 1.  Add a fork() loop to main.
31738494Sobrien	 * 2.  Remove the call to innetgr() and don't use
31838494Sobrien	 *     netgroups, especially if you don't use YP.
31938494Sobrien	 */
32038494Sobrien	error = fp->fh_error;
32138494Sobrien	fp->fh_error = -1;
32238494Sobrien	return error;
32338494Sobrien      }
32438494Sobrien      break;
32538494Sobrien    }
32638494Sobrien  }
32738494Sobrien
32838494Sobrien  /*
32938494Sobrien   * Not in cache
33038494Sobrien   */
33138494Sobrien  if (fp_save) {
33238494Sobrien    fp = fp_save;
33338494Sobrien    /*
33438494Sobrien     * Re-use existing slot
33538494Sobrien     */
33638494Sobrien    untimeout(fp->fh_cid);
33738494Sobrien    free_srvr(fp->fh_fs);
33838494Sobrien    XFREE(fp->fh_path);
33938494Sobrien  } else {
34038494Sobrien    fp = ALLOC(struct fh_cache);
34138494Sobrien    memset((voidp) fp, 0, sizeof(struct fh_cache));
34238494Sobrien    ins_que(&fp->fh_q, &fh_head);
34338494Sobrien  }
34438494Sobrien  if (!reuse_id)
34538494Sobrien    fp->fh_id = FHID_ALLOC(struct );
34638494Sobrien  fp->fh_wchan = wchan;
34738494Sobrien  fp->fh_error = -1;
34838494Sobrien  fp->fh_cid = timeout(FH_TTL, discard_fh, (voidp) fp);
34938494Sobrien
35038494Sobrien  /*
35138494Sobrien   * if fs->fs_ip is null, remote server is probably down.
35238494Sobrien   */
35338494Sobrien  if (!fs->fs_ip) {
35438494Sobrien    /* Mark the fileserver down and invalid again */
35538494Sobrien    fs->fs_flags &= ~FSF_VALID;
35638494Sobrien    fs->fs_flags |= FSF_DOWN;
35738494Sobrien    error = AM_ERRNO_HOST_DOWN;
35838494Sobrien    return error;
35938494Sobrien  }
36038494Sobrien
36138494Sobrien  /*
36238494Sobrien   * If the address has changed then don't try to re-use the
36338494Sobrien   * port information
36438494Sobrien   */
36538494Sobrien  if (fp->fh_sin.sin_addr.s_addr != fs->fs_ip->sin_addr.s_addr) {
36638494Sobrien    fp->fh_sin = *fs->fs_ip;
36738494Sobrien    fp->fh_sin.sin_port = 0;
36838494Sobrien    fp->fh_nfs_version = fs->fs_version;
36938494Sobrien  }
37038494Sobrien  fp->fh_fs = dup_srvr(fs);
37138494Sobrien  fp->fh_path = strdup(path);
37238494Sobrien
37338494Sobrien  error = call_mountd(fp, MOUNTPROC_MNT, got_nfs_fh, wchan);
37438494Sobrien  if (error) {
37538494Sobrien    /*
37638494Sobrien     * Local error - cache for a short period
37738494Sobrien     * just to prevent thrashing.
37838494Sobrien     */
37938494Sobrien    untimeout(fp->fh_cid);
38038494Sobrien    fp->fh_cid = timeout(error < 0 ? 2 * ALLOWED_MOUNT_TIME : FH_TTL_ERROR,
38138494Sobrien			 discard_fh, (voidp) fp);
38238494Sobrien    fp->fh_error = error;
38338494Sobrien  } else {
38438494Sobrien    error = fp->fh_error;
38538494Sobrien  }
38638494Sobrien
38738494Sobrien  return error;
38838494Sobrien}
38938494Sobrien
39038494Sobrien
39138494Sobrienint
39238494Sobrienmake_nfs_auth(void)
39338494Sobrien{
39438494Sobrien  AUTH_CREATE_GIDLIST_TYPE group_wheel = 0;
39538494Sobrien
39638494Sobrien  /* Some NFS mounts (particularly cross-domain) require FQDNs to succeed */
39738494Sobrien
39838494Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI
39938494Sobrien  if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) {
40038494Sobrien    plog(XLOG_INFO, "Using NFS auth for fqhn \"%s\"", hostd);
40138494Sobrien    nfs_auth = authsys_create(hostd, 0, 0, 1, &group_wheel);
40238494Sobrien  } else {
40338494Sobrien    nfs_auth = authsys_create_default();
40438494Sobrien  }
40538494Sobrien#else /* not HAVE_TRANSPORT_TYPE_TLI */
40638494Sobrien  if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) {
40738494Sobrien    plog(XLOG_INFO, "Using NFS auth for fqhn \"%s\"", hostd);
40838494Sobrien    nfs_auth = authunix_create(hostd, 0, 0, 1, &group_wheel);
40938494Sobrien  } else {
41038494Sobrien    nfs_auth = authunix_create_default();
41138494Sobrien  }
41238494Sobrien#endif /* not HAVE_TRANSPORT_TYPE_TLI */
41338494Sobrien
41438494Sobrien  if (!nfs_auth)
41538494Sobrien    return ENOBUFS;
41638494Sobrien
41738494Sobrien  return 0;
41838494Sobrien}
41938494Sobrien
42038494Sobrien
42138494Sobrienstatic int
42238494Sobriencall_mountd(fh_cache *fp, u_long proc, fwd_fun f, voidp wchan)
42338494Sobrien{
42438494Sobrien  struct rpc_msg mnt_msg;
42538494Sobrien  int len;
42638494Sobrien  char iobuf[8192];
42738494Sobrien  int error;
42838494Sobrien  u_long mnt_version;
42938494Sobrien
43038494Sobrien  if (!nfs_auth) {
43138494Sobrien    error = make_nfs_auth();
43238494Sobrien    if (error)
43338494Sobrien      return error;
43438494Sobrien  }
43538494Sobrien
43638494Sobrien  if (fp->fh_sin.sin_port == 0) {
43738494Sobrien    u_short port;
43838494Sobrien    error = nfs_srvr_port(fp->fh_fs, &port, wchan);
43938494Sobrien    if (error)
44038494Sobrien      return error;
44138494Sobrien    fp->fh_sin.sin_port = port;
44238494Sobrien  }
44338494Sobrien
44438494Sobrien  /* find the right version of the mount protocol */
44538494Sobrien#ifdef HAVE_FS_NFS3
44638494Sobrien  if (fp->fh_nfs_version == NFS_VERSION3)
44738494Sobrien    mnt_version = MOUNTVERS3;
44838494Sobrien  else
44938494Sobrien#endif /* HAVE_FS_NFS3 */
45038494Sobrien    mnt_version = MOUNTVERS;
45138494Sobrien  plog(XLOG_INFO, "call_mountd: NFS version %d, mount version %d",
45251292Sobrien       (int) fp->fh_nfs_version, (int) mnt_version);
45338494Sobrien
45438494Sobrien  rpc_msg_init(&mnt_msg, MOUNTPROG, mnt_version, MOUNTPROC_NULL);
45538494Sobrien  len = make_rpc_packet(iobuf,
45638494Sobrien			sizeof(iobuf),
45738494Sobrien			proc,
45838494Sobrien			&mnt_msg,
45938494Sobrien			(voidp) &fp->fh_path,
46038494Sobrien			(XDRPROC_T_TYPE) xdr_nfspath,
46138494Sobrien			nfs_auth);
46238494Sobrien
46338494Sobrien  if (len > 0) {
46438494Sobrien    error = fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD, fp->fh_id),
46538494Sobrien		       (voidp) iobuf,
46638494Sobrien		       len,
46738494Sobrien		       &fp->fh_sin,
46838494Sobrien		       &fp->fh_sin,
46938494Sobrien		       (voidp) ((long) fp->fh_id), /* for 64-bit archs */
47038494Sobrien		       f);
47138494Sobrien  } else {
47238494Sobrien    error = -len;
47338494Sobrien  }
47438494Sobrien
47538494Sobrien/*
47638494Sobrien * It may be the case that we're sending to the wrong MOUNTD port.  This
47738494Sobrien * occurs if mountd is restarted on the server after the port has been
47838494Sobrien * looked up and stored in the filehandle cache somewhere.  The correct
47938494Sobrien * solution, if we're going to cache port numbers is to catch the ICMP
48038494Sobrien * port unreachable reply from the server and cause the portmap request
48138494Sobrien * to be redone.  The quick solution here is to invalidate the MOUNTD
48238494Sobrien * port.
48338494Sobrien */
48438494Sobrien  fp->fh_sin.sin_port = 0;
48538494Sobrien
48638494Sobrien  return error;
48738494Sobrien}
48838494Sobrien
48938494Sobrien
49038494Sobrien/*
49138494Sobrien * NFS needs the local filesystem, remote filesystem
49238494Sobrien * remote hostname.
49338494Sobrien * Local filesystem defaults to remote and vice-versa.
49438494Sobrien */
49538494Sobrienchar *
49638494Sobriennfs_match(am_opts *fo)
49738494Sobrien{
49838494Sobrien  char *xmtab;
49938494Sobrien
50038494Sobrien  if (fo->opt_fs && !fo->opt_rfs)
50138494Sobrien    fo->opt_rfs = fo->opt_fs;
50238494Sobrien  if (!fo->opt_rfs) {
50338494Sobrien    plog(XLOG_USER, "nfs: no remote filesystem specified");
50438494Sobrien    return NULL;
50538494Sobrien  }
50638494Sobrien  if (!fo->opt_rhost) {
50738494Sobrien    plog(XLOG_USER, "nfs: no remote host specified");
50838494Sobrien    return NULL;
50938494Sobrien  }
51038494Sobrien
51138494Sobrien  /*
51238494Sobrien   * Determine magic cookie to put in mtab
51338494Sobrien   */
51438494Sobrien  xmtab = (char *) xmalloc(strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2);
51538494Sobrien  sprintf(xmtab, "%s:%s", fo->opt_rhost, fo->opt_rfs);
51638494Sobrien#ifdef DEBUG
51738494Sobrien  dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
51838494Sobrien       fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
51938494Sobrien#endif /* DEBUG */
52038494Sobrien
52138494Sobrien  return xmtab;
52238494Sobrien}
52338494Sobrien
52438494Sobrien
52538494Sobrien/*
52638494Sobrien * Initialize am structure for nfs
52738494Sobrien */
52838494Sobrienint
52938494Sobriennfs_init(mntfs *mf)
53038494Sobrien{
53138494Sobrien  int error;
53238494Sobrien  am_nfs_handle_t fhs;
53338494Sobrien  char *colon;
53438494Sobrien
53538494Sobrien  if (mf->mf_private)
53638494Sobrien    return 0;
53738494Sobrien
53838494Sobrien  colon = strchr(mf->mf_info, ':');
53938494Sobrien  if (colon == 0)
54038494Sobrien    return ENOENT;
54138494Sobrien
54238494Sobrien  error = prime_nfs_fhandle_cache(colon + 1, mf->mf_server, &fhs, (voidp) mf);
54338494Sobrien  if (!error) {
54438494Sobrien    mf->mf_private = (voidp) ALLOC(am_nfs_handle_t);
54538494Sobrien    mf->mf_prfree = (void (*)(voidp)) free;
54638494Sobrien    memmove(mf->mf_private, (voidp) &fhs, sizeof(fhs));
54738494Sobrien  }
54838494Sobrien  return error;
54938494Sobrien}
55038494Sobrien
55138494Sobrien
55238494Sobrienint
55338494Sobrienmount_nfs_fh(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs *mf)
55438494Sobrien{
55538494Sobrien  MTYPE_TYPE type;
55638494Sobrien  char *colon;
55738494Sobrien  char *xopts;
55838494Sobrien  char host[MAXHOSTNAMELEN + MAXPATHLEN + 2];
55938494Sobrien  fserver *fs = mf->mf_server;
56038494Sobrien  u_long nfs_version = fs->fs_version;
56138494Sobrien  char *nfs_proto = fs->fs_proto; /* "tcp" or "udp" */
56238494Sobrien  int error;
56338494Sobrien  int genflags;
56438494Sobrien  int retry;
56538494Sobrien  mntent_t mnt;
56638494Sobrien  nfs_args_t nfs_args;
56738494Sobrien
56838494Sobrien  /*
56938494Sobrien   * Extract HOST name to give to kernel.
57038494Sobrien   * Some systems like osf1/aix3/bsd44 variants may need old code
57138494Sobrien   * for NFS_ARGS_NEEDS_PATH.
57238494Sobrien   */
57338494Sobrien  if (!(colon = strchr(fs_name, ':')))
57438494Sobrien    return ENOENT;
57538494Sobrien#ifdef MOUNT_TABLE_ON_FILE
57638494Sobrien  *colon = '\0';
57738494Sobrien#endif /* MOUNT_TABLE_ON_FILE */
57838494Sobrien  strncpy(host, fs_name, sizeof(host));
57938494Sobrien#ifdef MOUNT_TABLE_ON_FILE
58038494Sobrien  *colon = ':';
58138494Sobrien#endif /* MOUNT_TABLE_ON_FILE */
58238494Sobrien#ifdef MAXHOSTNAMELEN
58338494Sobrien  /* most kernels have a name length restriction */
58438494Sobrien  if (strlen(host) >= MAXHOSTNAMELEN)
58538494Sobrien    strcpy(host + MAXHOSTNAMELEN - 3, "..");
58638494Sobrien#endif /* MAXHOSTNAMELEN */
58738494Sobrien
58851292Sobrien  if (mf->mf_remopts && *mf->mf_remopts &&
58951292Sobrien      !islocalnet(fs->fs_ip->sin_addr.s_addr)) {
59051292Sobrien    plog(XLOG_INFO, "Using remopts=\"%s\"", mf->mf_remopts);
59138494Sobrien    xopts = strdup(mf->mf_remopts);
59251292Sobrien  } else {
59338494Sobrien    xopts = strdup(opts);
59451292Sobrien  }
59538494Sobrien
59638494Sobrien  memset((voidp) &mnt, 0, sizeof(mnt));
59738494Sobrien  mnt.mnt_dir = dir;
59838494Sobrien  mnt.mnt_fsname = fs_name;
59938494Sobrien  mnt.mnt_opts = xopts;
60038494Sobrien
60138494Sobrien  /*
60238494Sobrien   * Set mount types accordingly
60338494Sobrien   */
60438494Sobrien#ifndef HAVE_FS_NFS3
60538494Sobrien  type = MOUNT_TYPE_NFS;
60638494Sobrien  mnt.mnt_type = MNTTAB_TYPE_NFS;
60738494Sobrien#else /* HAVE_FS_NFS3 */
60838494Sobrien  if (nfs_version == NFS_VERSION3) {
60938494Sobrien    type = MOUNT_TYPE_NFS3;
61038494Sobrien    /*
61138494Sobrien     * Systems that include the mount table "vers" option generally do not
61238494Sobrien     * set the mnttab entry to "nfs3", but to "nfs" and then they set
61338494Sobrien     * "vers=3".  Setting it to "nfs3" works, but it may break some things
61438494Sobrien     * like "df -t nfs" and the "quota" program (esp. on Solaris and Irix).
61538494Sobrien     * So on those systems, set it to "nfs".
61638494Sobrien     * Note: MNTTAB_OPT_VERS is always set for NFS3 (see am_compat.h).
61738494Sobrien     */
61838494Sobrien# if defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE)
61938494Sobrien    mnt.mnt_type = MNTTAB_TYPE_NFS;
62038494Sobrien# else /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */
62138494Sobrien    mnt.mnt_type = MNTTAB_TYPE_NFS3;
62238494Sobrien# endif /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */
62338494Sobrien  } else {
62438494Sobrien    type = MOUNT_TYPE_NFS;
62538494Sobrien    mnt.mnt_type = MNTTAB_TYPE_NFS;
62638494Sobrien  }
62738494Sobrien#endif /* HAVE_FS_NFS3 */
62851292Sobrien  plog(XLOG_INFO, "mount_nfs_fh: NFS version %d", (int) nfs_version);
62938494Sobrien#if defined(HAVE_FS_NFS3) || defined(HAVE_TRANSPORT_TYPE_TLI)
63038494Sobrien  plog(XLOG_INFO, "mount_nfs_fh: using NFS transport %s", nfs_proto);
63138494Sobrien#endif /* defined(HAVE_FS_NFS3) || defined(HAVE_TRANSPORT_TYPE_TLI) */
63238494Sobrien
63338494Sobrien  retry = hasmntval(&mnt, MNTTAB_OPT_RETRY);
63438494Sobrien  if (retry <= 0)
63538494Sobrien    retry = 1;			/* XXX */
63638494Sobrien
63738494Sobrien  genflags = compute_mount_flags(&mnt);
63838494Sobrien
63938494Sobrien  /* setup the many fields and flags within nfs_args */
64038494Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI
64138494Sobrien  compute_nfs_args(&nfs_args,
64238494Sobrien		   &mnt,
64338494Sobrien		   genflags,
64438494Sobrien		   NULL,	/* struct netconfig *nfsncp */
64538494Sobrien		   fs->fs_ip,
64638494Sobrien		   nfs_version,
64738494Sobrien		   nfs_proto,
64838494Sobrien		   fhp,
64938494Sobrien		   host,
65038494Sobrien		   fs_name);
65138494Sobrien#else /* not HAVE_TRANSPORT_TYPE_TLI */
65238494Sobrien  compute_nfs_args(&nfs_args,
65338494Sobrien		   &mnt,
65438494Sobrien		   genflags,
65538494Sobrien		   fs->fs_ip,
65638494Sobrien		   nfs_version,
65738494Sobrien		   nfs_proto,
65838494Sobrien		   fhp,
65938494Sobrien		   host,
66038494Sobrien		   fs_name);
66138494Sobrien#endif /* not HAVE_TRANSPORT_TYPE_TLI */
66238494Sobrien
66338494Sobrien  /* finally call the mounting function */
66438494Sobrien#ifdef DEBUG
66551292Sobrien  amuDebug(D_TRACE) {
66638494Sobrien    print_nfs_args(&nfs_args, nfs_version);
66751292Sobrien    plog(XLOG_DEBUG, "Generic mount flags 0x%x", genflags);
66851292Sobrien  }
66938494Sobrien#endif /* DEBUG */
67038494Sobrien  error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type,
67138494Sobrien		   nfs_version, nfs_proto, mnttab_file_name);
67238494Sobrien  XFREE(xopts);
67338494Sobrien
67438494Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI
67538494Sobrien  free_knetconfig(nfs_args.knconf);
67638494Sobrien  if (nfs_args.addr)
67738494Sobrien    XFREE(nfs_args.addr);	/* allocated in compute_nfs_args() */
67838494Sobrien#endif /* HAVE_TRANSPORT_TYPE_TLI */
67938494Sobrien
68038494Sobrien  return error;
68138494Sobrien}
68238494Sobrien
68338494Sobrien
68438494Sobrienstatic int
68538494Sobrienmount_nfs(char *dir, char *fs_name, char *opts, mntfs *mf)
68638494Sobrien{
68738494Sobrien  if (!mf->mf_private) {
68838494Sobrien    plog(XLOG_ERROR, "Missing filehandle for %s", fs_name);
68938494Sobrien    return EINVAL;
69038494Sobrien  }
69138494Sobrien
69238494Sobrien  return mount_nfs_fh((am_nfs_handle_t *) mf->mf_private, dir, fs_name, opts, mf);
69338494Sobrien}
69438494Sobrien
69538494Sobrien
69638494Sobrienint
69738494Sobriennfs_fmount(mntfs *mf)
69838494Sobrien{
69938494Sobrien  int error = 0;
70038494Sobrien
70138494Sobrien  error = mount_nfs(mf->mf_mount, mf->mf_info, mf->mf_mopts, mf);
70238494Sobrien
70338494Sobrien#ifdef DEBUG
70438494Sobrien  if (error) {
70538494Sobrien    errno = error;
70638494Sobrien    dlog("mount_nfs: %m");
70738494Sobrien  }
70838494Sobrien#endif /* DEBUG */
70938494Sobrien
71038494Sobrien  return error;
71138494Sobrien}
71238494Sobrien
71338494Sobrien
71438494Sobrienint
71538494Sobriennfs_fumount(mntfs *mf)
71638494Sobrien{
71738494Sobrien  int error = UMOUNT_FS(mf->mf_mount, mnttab_file_name);
71838494Sobrien
71938494Sobrien  /*
72038494Sobrien   * Here is some code to unmount 'restarted' file systems.
72138494Sobrien   * The restarted file systems are marked as 'nfs', not
72238494Sobrien   * 'host', so we only have the map information for the
72338494Sobrien   * the top-level mount.  The unmount will fail (EBUSY)
72438494Sobrien   * if there are anything else from the NFS server mounted
72538494Sobrien   * below the mount-point.  This code checks to see if there
72638494Sobrien   * is anything mounted with the same prefix as the
72738494Sobrien   * file system to be unmounted ("/a/b/c" when unmounting "/a/b").
72838494Sobrien   * If there is, and it is a 'restarted' file system, we unmount
72938494Sobrien   * it.
73038494Sobrien   * Added by Mike Mitchell, mcm@unx.sas.com, 09/08/93
73138494Sobrien   */
73238494Sobrien  if (error == EBUSY) {
73338494Sobrien    mntfs *new_mf;
73438494Sobrien    int len = strlen(mf->mf_mount);
73538494Sobrien    int didsome = 0;
73638494Sobrien
73738494Sobrien    ITER(new_mf, mntfs, &mfhead) {
73838494Sobrien      if (new_mf->mf_ops != mf->mf_ops ||
73938494Sobrien	  new_mf->mf_refc > 1 ||
74038494Sobrien	  mf == new_mf ||
74138494Sobrien	  ((new_mf->mf_flags & (MFF_MOUNTED | MFF_UNMOUNTING | MFF_RESTART)) == (MFF_MOUNTED | MFF_RESTART)))
74238494Sobrien	continue;
74338494Sobrien
74438494Sobrien      if (NSTREQ(mf->mf_mount, new_mf->mf_mount, len) &&
74538494Sobrien	  new_mf->mf_mount[len] == '/') {
74638494Sobrien	UMOUNT_FS(new_mf->mf_mount, mnttab_file_name);
74738494Sobrien	didsome = 1;
74838494Sobrien      }
74938494Sobrien    }
75038494Sobrien    if (didsome)
75138494Sobrien      error = UMOUNT_FS(mf->mf_mount, mnttab_file_name);
75238494Sobrien  }
75338494Sobrien  if (error)
75438494Sobrien    return error;
75538494Sobrien
75638494Sobrien  return 0;
75738494Sobrien}
75838494Sobrien
75938494Sobrien
76038494Sobrienvoid
76138494Sobriennfs_umounted(am_node *mp)
76238494Sobrien{
76338494Sobrien  /*
76438494Sobrien   * Don't bother to inform remote mountd that we are finished.  Until a
76538494Sobrien   * full track of filehandles is maintained the mountd unmount callback
76638494Sobrien   * cannot be done correctly anyway...
76738494Sobrien   */
76838494Sobrien  mntfs *mf = mp->am_mnt;
76938494Sobrien  fserver *fs;
77038494Sobrien  char *colon, *path;
77138494Sobrien
77238494Sobrien  if (mf->mf_error || mf->mf_refc > 1)
77338494Sobrien    return;
77438494Sobrien
77538494Sobrien  fs = mf->mf_server;
77638494Sobrien
77738494Sobrien  /*
77838494Sobrien   * Call the mount daemon on the server to announce that we are not using
77938494Sobrien   * the fs any more.
78038494Sobrien   *
78138494Sobrien   * This is *wrong*.  The mountd should be called when the fhandle is
78238494Sobrien   * flushed from the cache, and a reference held to the cached entry while
78338494Sobrien   * the fs is mounted...
78438494Sobrien   */
78538494Sobrien  colon = path = strchr(mf->mf_info, ':');
78638494Sobrien  if (fs && colon) {
78738494Sobrien    fh_cache f;
78838494Sobrien
78938494Sobrien#ifdef DEBUG
79038494Sobrien    dlog("calling mountd for %s", mf->mf_info);
79138494Sobrien#endif /* DEBUG */
79238494Sobrien    *path++ = '\0';
79338494Sobrien    f.fh_path = path;
79438494Sobrien    f.fh_sin = *fs->fs_ip;
79538494Sobrien    f.fh_sin.sin_port = (u_short) 0;
79638494Sobrien    f.fh_nfs_version = fs->fs_version;
79738494Sobrien    f.fh_fs = fs;
79838494Sobrien    f.fh_id = 0;
79938494Sobrien    f.fh_error = 0;
80038494Sobrien    prime_nfs_fhandle_cache(colon + 1, mf->mf_server, (am_nfs_handle_t *) 0, (voidp) mf);
80138494Sobrien    call_mountd(&f, MOUNTPROC_UMNT, (fwd_fun) 0, (voidp) 0);
80238494Sobrien    *colon = ':';
80338494Sobrien  }
80438494Sobrien}
805