138494Sobrien/*
2174294Sobrien * Copyright (c) 1997-2006 Erez Zadok
338494Sobrien * Copyright (c) 1990 Jan-Simon Pendry
438494Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
538494Sobrien * Copyright (c) 1990 The Regents of the University of California.
638494Sobrien * All rights reserved.
738494Sobrien *
838494Sobrien * This code is derived from software contributed to Berkeley by
938494Sobrien * Jan-Simon Pendry at Imperial College, London.
1038494Sobrien *
1138494Sobrien * Redistribution and use in source and binary forms, with or without
1238494Sobrien * modification, are permitted provided that the following conditions
1338494Sobrien * are met:
1438494Sobrien * 1. Redistributions of source code must retain the above copyright
1538494Sobrien *    notice, this list of conditions and the following disclaimer.
1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1738494Sobrien *    notice, this list of conditions and the following disclaimer in the
1838494Sobrien *    documentation and/or other materials provided with the distribution.
1938494Sobrien * 3. All advertising materials mentioning features or use of this software
2042629Sobrien *    must display the following acknowledgment:
2138494Sobrien *      This product includes software developed by the University of
2238494Sobrien *      California, Berkeley and its contributors.
2338494Sobrien * 4. Neither the name of the University nor the names of its contributors
2438494Sobrien *    may be used to endorse or promote products derived from this software
2538494Sobrien *    without specific prior written permission.
2638494Sobrien *
2738494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2838494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2938494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3038494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3138494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3238494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3338494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3438494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3538494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3638494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3738494Sobrien * SUCH DAMAGE.
3838494Sobrien *
3938494Sobrien *
40174294Sobrien * File: am-utils/amd/ops_nfs.c
4138494Sobrien *
4238494Sobrien */
4338494Sobrien
4438494Sobrien/*
4538494Sobrien * Network file system
4638494Sobrien */
4738494Sobrien
4838494Sobrien#ifdef HAVE_CONFIG_H
4938494Sobrien# include <config.h>
5038494Sobrien#endif /* HAVE_CONFIG_H */
5138494Sobrien#include <am_defs.h>
5238494Sobrien#include <amd.h>
5338494Sobrien
5438494Sobrien/*
5538494Sobrien * Convert from nfsstat to UN*X error code
5638494Sobrien */
5738494Sobrien#define unx_error(e)	((int)(e))
5838494Sobrien
5938494Sobrien/*
6038494Sobrien * FH_TTL is the time a file handle will remain in the cache since
6138494Sobrien * last being used.  If the file handle becomes invalid, then it
6238494Sobrien * will be flushed anyway.
6338494Sobrien */
6438494Sobrien#define	FH_TTL			(5 * 60) /* five minutes */
6538494Sobrien#define	FH_TTL_ERROR		(30) /* 30 seconds */
66174294Sobrien#define	FHID_ALLOC()		(++fh_id)
6738494Sobrien
6838494Sobrien/*
6938494Sobrien * The NFS layer maintains a cache of file handles.
7038494Sobrien * This is *fundamental* to the implementation and
7138494Sobrien * also allows quick remounting when a filesystem
7238494Sobrien * is accessed soon after timing out.
7338494Sobrien *
7438494Sobrien * The NFS server layer knows to flush this cache
7538494Sobrien * when a server goes down so avoiding stale handles.
7638494Sobrien *
7738494Sobrien * Each cache entry keeps a hard reference to
7838494Sobrien * the corresponding server.  This ensures that
7938494Sobrien * the server keepalive information is maintained.
8038494Sobrien *
8138494Sobrien * The copy of the sockaddr_in here is taken so
8238494Sobrien * that the port can be twiddled to talk to mountd
8338494Sobrien * instead of portmap or the NFS server as used
8438494Sobrien * elsewhere.
8538494Sobrien * The port# is flushed if a server goes down.
8638494Sobrien * The IP address is never flushed - we assume
8738494Sobrien * that the address of a mounted machine never
8838494Sobrien * changes.  If it does, then you have other
8938494Sobrien * problems...
9038494Sobrien */
9138494Sobrientypedef struct fh_cache fh_cache;
9238494Sobrienstruct fh_cache {
9338494Sobrien  qelem			fh_q;		/* List header */
94174294Sobrien  wchan_t		fh_wchan;	/* Wait channel */
9538494Sobrien  int			fh_error;	/* Valid data? */
9638494Sobrien  int			fh_id;		/* Unique id */
9738494Sobrien  int			fh_cid;		/* Callout id */
9838494Sobrien  u_long		fh_nfs_version;	/* highest NFS version on host */
9938494Sobrien  am_nfs_handle_t	fh_nfs_handle;	/* Handle on filesystem */
100174294Sobrien  int			fh_status;	/* Status of last rpc */
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 */
107174294Sobrienstatic int nfs_init(mntfs *mf);
108174294Sobrienstatic char *nfs_match(am_opts *fo);
109174294Sobrienstatic int nfs_mount(am_node *am, mntfs *mf);
110174294Sobrienstatic int nfs_umount(am_node *am, mntfs *mf);
111174294Sobrienstatic void nfs_umounted(mntfs *mf);
112174294Sobrienstatic int call_mountd(fh_cache *fp, u_long proc, fwd_fun f, wchan_t wchan);
113174294Sobrienstatic int webnfs_lookup(fh_cache *fp, fwd_fun f, wchan_t wchan);
11438494Sobrienstatic int fh_id = 0;
11538494Sobrien
11638494Sobrien/* globals */
11738494SobrienAUTH *nfs_auth;
11838494Sobrienqelem fh_head = {&fh_head, &fh_head};
11938494Sobrien
12038494Sobrien/*
12138494Sobrien * Network file system operations
12238494Sobrien */
12338494Sobrienam_ops nfs_ops =
12438494Sobrien{
12538494Sobrien  "nfs",
12638494Sobrien  nfs_match,
12738494Sobrien  nfs_init,
128174294Sobrien  nfs_mount,
129174294Sobrien  nfs_umount,
130174294Sobrien  amfs_error_lookup_child,
131174294Sobrien  amfs_error_mount_child,
13238494Sobrien  amfs_error_readdir,
13338494Sobrien  0,				/* nfs_readlink */
13438494Sobrien  0,				/* nfs_mounted */
13538494Sobrien  nfs_umounted,
13638494Sobrien  find_nfs_srvr,
137174294Sobrien  0,				/* nfs_get_wchan */
138174294Sobrien  FS_MKMNT | FS_BACKGROUND | FS_AMQINFO,	/* nfs_fs_flags */
139174294Sobrien#ifdef HAVE_FS_AUTOFS
140174294Sobrien  AUTOFS_NFS_FS_FLAGS,
141174294Sobrien#endif /* HAVE_FS_AUTOFS */
14238494Sobrien};
14338494Sobrien
14438494Sobrien
14538494Sobrienstatic fh_cache *
146174294Sobrienfind_nfs_fhandle_cache(opaque_t arg, int done)
14738494Sobrien{
14838494Sobrien  fh_cache *fp, *fp2 = 0;
149174294Sobrien  int id = (long) arg;		/* for 64-bit archs */
15038494Sobrien
15138494Sobrien  ITER(fp, fh_cache, &fh_head) {
15238494Sobrien    if (fp->fh_id == id) {
15338494Sobrien      fp2 = fp;
15438494Sobrien      break;
15538494Sobrien    }
15638494Sobrien  }
15738494Sobrien
15838494Sobrien  if (fp2) {
15951292Sobrien    dlog("fh cache gives fp %#lx, fs %s", (unsigned long) fp2, fp2->fh_path);
16038494Sobrien  } else {
16138494Sobrien    dlog("fh cache search failed");
16238494Sobrien  }
16338494Sobrien
16438494Sobrien  if (fp2 && !done) {
16538494Sobrien    fp2->fh_error = ETIMEDOUT;
16638494Sobrien    return 0;
16738494Sobrien  }
16838494Sobrien
16938494Sobrien  return fp2;
17038494Sobrien}
17138494Sobrien
17238494Sobrien
17338494Sobrien/*
174174294Sobrien * Called when a filehandle appears via the mount protocol
17538494Sobrien */
17638494Sobrienstatic void
177174294Sobriengot_nfs_fh_mount(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, opaque_t arg, int done)
17838494Sobrien{
17938494Sobrien  fh_cache *fp;
180174294Sobrien  struct fhstatus res;
181174294Sobrien#ifdef HAVE_FS_NFS3
182174294Sobrien  struct am_mountres3 res3;
183174294Sobrien#endif /* HAVE_FS_NFS3 */
18438494Sobrien
185174294Sobrien  fp = find_nfs_fhandle_cache(arg, done);
18638494Sobrien  if (!fp)
18738494Sobrien    return;
18838494Sobrien
18938494Sobrien  /*
19038494Sobrien   * retrieve the correct RPC reply for the file handle, based on the
19138494Sobrien   * NFS protocol version.
19238494Sobrien   */
19338494Sobrien#ifdef HAVE_FS_NFS3
194174294Sobrien  if (fp->fh_nfs_version == NFS_VERSION3) {
195174294Sobrien    memset(&res3, 0, sizeof(res3));
196174294Sobrien    fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res3,
197174294Sobrien				    (XDRPROC_T_TYPE) xdr_am_mountres3);
198174294Sobrien    fp->fh_status = unx_error(res3.fhs_status);
199174294Sobrien    memset(&fp->fh_nfs_handle.v3, 0, sizeof(am_nfs_fh3));
200174294Sobrien    fp->fh_nfs_handle.v3.am_fh3_length = res3.mountres3_u.mountinfo.fhandle.fhandle3_len;
201174294Sobrien    memmove(fp->fh_nfs_handle.v3.am_fh3_data,
202174294Sobrien	    res3.mountres3_u.mountinfo.fhandle.fhandle3_val,
203174294Sobrien	    fp->fh_nfs_handle.v3.am_fh3_length);
204174294Sobrien  } else {
20538494Sobrien#endif /* HAVE_FS_NFS3 */
206174294Sobrien    memset(&res, 0, sizeof(res));
207174294Sobrien    fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res,
20838494Sobrien				    (XDRPROC_T_TYPE) xdr_fhstatus);
209174294Sobrien    fp->fh_status = unx_error(res.fhs_status);
210174294Sobrien    memmove(&fp->fh_nfs_handle.v2, &res.fhs_fh, NFS_FHSIZE);
211174294Sobrien#ifdef HAVE_FS_NFS3
212174294Sobrien  }
213174294Sobrien#endif /* HAVE_FS_NFS3 */
21438494Sobrien
21538494Sobrien  if (!fp->fh_error) {
21638494Sobrien    dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
217174294Sobrien  } else {
218174294Sobrien    plog(XLOG_USER, "filehandle denied for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
219174294Sobrien    /*
220174294Sobrien     * Force the error to be EACCES. It's debatable whether it should be
221174294Sobrien     * ENOENT instead, but the server really doesn't give us any clues, and
222174294Sobrien     * EACCES is more in line with the "filehandle denied" message.
223174294Sobrien     */
224174294Sobrien    fp->fh_error = EACCES;
225174294Sobrien  }
22638494Sobrien
227174294Sobrien  /*
228174294Sobrien   * Wakeup anything sleeping on this filehandle
229174294Sobrien   */
230174294Sobrien  if (fp->fh_wchan) {
231174294Sobrien    dlog("Calling wakeup on %#lx", (unsigned long) fp->fh_wchan);
232174294Sobrien    wakeup(fp->fh_wchan);
233174294Sobrien  }
234174294Sobrien}
235174294Sobrien
236174294Sobrien
237174294Sobrien/*
238174294Sobrien * Called when a filehandle appears via WebNFS
239174294Sobrien */
240174294Sobrienstatic void
241174294Sobriengot_nfs_fh_webnfs(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, opaque_t arg, int done)
242174294Sobrien{
243174294Sobrien  fh_cache *fp;
244174294Sobrien  nfsdiropres res;
245174294Sobrien#ifdef HAVE_FS_NFS3
246174294Sobrien  am_LOOKUP3res res3;
247174294Sobrien#endif /* HAVE_FS_NFS3 */
248174294Sobrien
249174294Sobrien  fp = find_nfs_fhandle_cache(arg, done);
250174294Sobrien  if (!fp)
251174294Sobrien    return;
252174294Sobrien
253174294Sobrien  /*
254174294Sobrien   * retrieve the correct RPC reply for the file handle, based on the
255174294Sobrien   * NFS protocol version.
256174294Sobrien   */
257174294Sobrien#ifdef HAVE_FS_NFS3
258174294Sobrien  if (fp->fh_nfs_version == NFS_VERSION3) {
259174294Sobrien    memset(&res3, 0, sizeof(res3));
260174294Sobrien    fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res3,
261174294Sobrien				    (XDRPROC_T_TYPE) xdr_am_LOOKUP3res);
262174294Sobrien    fp->fh_status = unx_error(res3.status);
263174294Sobrien    memset(&fp->fh_nfs_handle.v3, 0, sizeof(am_nfs_fh3));
264174294Sobrien    fp->fh_nfs_handle.v3.am_fh3_length = res3.res_u.ok.object.am_fh3_length;
265174294Sobrien    memmove(fp->fh_nfs_handle.v3.am_fh3_data,
266174294Sobrien	    res3.res_u.ok.object.am_fh3_data,
267174294Sobrien	    fp->fh_nfs_handle.v3.am_fh3_length);
268174294Sobrien  } else {
269174294Sobrien#endif /* HAVE_FS_NFS3 */
270174294Sobrien    memset(&res, 0, sizeof(res));
271174294Sobrien    fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &res,
272174294Sobrien				    (XDRPROC_T_TYPE) xdr_diropres);
273174294Sobrien    fp->fh_status = unx_error(res.dr_status);
274174294Sobrien    memmove(&fp->fh_nfs_handle.v2, &res.dr_u.dr_drok_u.drok_fhandle, NFS_FHSIZE);
275174294Sobrien#ifdef HAVE_FS_NFS3
276174294Sobrien  }
277174294Sobrien#endif /* HAVE_FS_NFS3 */
278174294Sobrien
279174294Sobrien  if (!fp->fh_error) {
280174294Sobrien    dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
281174294Sobrien  } else {
282174294Sobrien    plog(XLOG_USER, "filehandle denied for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
28338494Sobrien    /*
284174294Sobrien     * Force the error to be EACCES. It's debatable whether it should be
285174294Sobrien     * ENOENT instead, but the server really doesn't give us any clues, and
286174294Sobrien     * EACCES is more in line with the "filehandle denied" message.
28738494Sobrien     */
288174294Sobrien    fp->fh_error = EACCES;
28938494Sobrien  }
290174294Sobrien
291174294Sobrien  /*
292174294Sobrien   * Wakeup anything sleeping on this filehandle
293174294Sobrien   */
294174294Sobrien  if (fp->fh_wchan) {
295174294Sobrien    dlog("Calling wakeup on %#lx", (unsigned long) fp->fh_wchan);
296174294Sobrien    wakeup(fp->fh_wchan);
297174294Sobrien  }
29838494Sobrien}
29938494Sobrien
30038494Sobrien
30138494Sobrienvoid
30238494Sobrienflush_nfs_fhandle_cache(fserver *fs)
30338494Sobrien{
30438494Sobrien  fh_cache *fp;
30538494Sobrien
30638494Sobrien  ITER(fp, fh_cache, &fh_head) {
307174294Sobrien    if (fp->fh_fs == fs || fs == NULL) {
308174294Sobrien      /*
309174294Sobrien       * Only invalidate port info for non-WebNFS servers
310174294Sobrien       */
311174294Sobrien      if (!(fp->fh_fs->fs_flags & FSF_WEBNFS))
312174294Sobrien	fp->fh_sin.sin_port = (u_short) 0;
31338494Sobrien      fp->fh_error = -1;
31438494Sobrien    }
31538494Sobrien  }
31638494Sobrien}
31738494Sobrien
31838494Sobrien
31938494Sobrienstatic void
320174294Sobriendiscard_fh(opaque_t arg)
32138494Sobrien{
322174294Sobrien  fh_cache *fp = (fh_cache *) arg;
32338494Sobrien
32438494Sobrien  rem_que(&fp->fh_q);
32538494Sobrien  if (fp->fh_fs) {
32638494Sobrien    dlog("Discarding filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
32738494Sobrien    free_srvr(fp->fh_fs);
32838494Sobrien  }
32938494Sobrien  if (fp->fh_path)
33038494Sobrien    XFREE(fp->fh_path);
33138494Sobrien  XFREE(fp);
33238494Sobrien}
33338494Sobrien
33438494Sobrien
33538494Sobrien/*
33638494Sobrien * Determine the file handle for a node
33738494Sobrien */
33838494Sobrienstatic int
339174294Sobrienprime_nfs_fhandle_cache(char *path, fserver *fs, am_nfs_handle_t *fhbuf, mntfs *mf)
34038494Sobrien{
34138494Sobrien  fh_cache *fp, *fp_save = 0;
34238494Sobrien  int error;
34338494Sobrien  int reuse_id = FALSE;
34438494Sobrien
34538494Sobrien  dlog("Searching cache for %s:%s", fs->fs_host, path);
34638494Sobrien
34738494Sobrien  /*
34838494Sobrien   * First search the cache
34938494Sobrien   */
35038494Sobrien  ITER(fp, fh_cache, &fh_head) {
351174294Sobrien    if (fs != fp->fh_fs  ||  !STREQ(path, fp->fh_path))
352174294Sobrien      continue;			/* skip to next ITER item */
353174294Sobrien    /* else we got a match */
354174294Sobrien    switch (fp->fh_error) {
355174294Sobrien    case 0:
356174294Sobrien      plog(XLOG_INFO, "prime_nfs_fhandle_cache: NFS version %d", (int) fp->fh_nfs_version);
357174294Sobrien
358174294Sobrien      error = fp->fh_error = fp->fh_status;
359174294Sobrien
360174294Sobrien      if (error == 0) {
361174294Sobrien	if (mf->mf_flags & MFF_NFS_SCALEDOWN) {
362174294Sobrien	  fp_save = fp;
363174294Sobrien	  /* XXX: why reuse the ID? */
364174294Sobrien	  reuse_id = TRUE;
365174294Sobrien	  break;
366174294Sobrien	}
367174294Sobrien
368174294Sobrien	if (fhbuf) {
36938494Sobrien#ifdef HAVE_FS_NFS3
370174294Sobrien	  if (fp->fh_nfs_version == NFS_VERSION3) {
371174294Sobrien	    memmove((voidp) &(fhbuf->v3), (voidp) &(fp->fh_nfs_handle.v3),
372174294Sobrien		    sizeof(fp->fh_nfs_handle.v3));
373174294Sobrien	  } else
37438494Sobrien#endif /* HAVE_FS_NFS3 */
375174294Sobrien	    {
37638494Sobrien	      memmove((voidp) &(fhbuf->v2), (voidp) &(fp->fh_nfs_handle.v2),
37738494Sobrien		      sizeof(fp->fh_nfs_handle.v2));
378174294Sobrien	    }
37938494Sobrien	}
380174294Sobrien	if (fp->fh_cid)
381174294Sobrien	  untimeout(fp->fh_cid);
382174294Sobrien	fp->fh_cid = timeout(FH_TTL, discard_fh, (opaque_t) fp);
383174294Sobrien      } else if (error == EACCES) {
38438494Sobrien	/*
385174294Sobrien	 * Now decode the file handle return code.
38638494Sobrien	 */
387174294Sobrien	plog(XLOG_INFO, "Filehandle denied for \"%s:%s\"",
388174294Sobrien	     fs->fs_host, path);
389174294Sobrien      } else {
390174294Sobrien	errno = error;	/* XXX */
391174294Sobrien	plog(XLOG_INFO, "Filehandle error for \"%s:%s\": %m",
392174294Sobrien	     fs->fs_host, path);
393174294Sobrien      }
39438494Sobrien
395174294Sobrien      /*
396174294Sobrien       * The error was returned from the remote mount daemon.
397174294Sobrien       * Policy: this error will be cached for now...
398174294Sobrien       */
399174294Sobrien      return error;
40038494Sobrien
401174294Sobrien    case -1:
402174294Sobrien      /*
403174294Sobrien       * Still thinking about it, but we can re-use.
404174294Sobrien       */
405174294Sobrien      fp_save = fp;
406174294Sobrien      reuse_id = TRUE;
40738494Sobrien      break;
40838494Sobrien
409174294Sobrien    default:
410174294Sobrien      /*
411174294Sobrien       * Return the error.
412174294Sobrien       * Policy: make sure we recompute if required again
413174294Sobrien       * in case this was caused by a network failure.
414174294Sobrien       * This can thrash mountd's though...  If you find
415174294Sobrien       * your mountd going slowly then:
416174294Sobrien       * 1.  Add a fork() loop to main.
417174294Sobrien       * 2.  Remove the call to innetgr() and don't use
418174294Sobrien       *     netgroups, especially if you don't use YP.
419174294Sobrien       */
420174294Sobrien      error = fp->fh_error;
421174294Sobrien      fp->fh_error = -1;
422174294Sobrien      return error;
423174294Sobrien    }	/* end of switch statement */
424174294Sobrien  } /* end of ITER loop */
425174294Sobrien
42638494Sobrien  /*
42738494Sobrien   * Not in cache
42838494Sobrien   */
42938494Sobrien  if (fp_save) {
43038494Sobrien    fp = fp_save;
43138494Sobrien    /*
43238494Sobrien     * Re-use existing slot
43338494Sobrien     */
43438494Sobrien    untimeout(fp->fh_cid);
43538494Sobrien    free_srvr(fp->fh_fs);
43638494Sobrien    XFREE(fp->fh_path);
43738494Sobrien  } else {
43838494Sobrien    fp = ALLOC(struct fh_cache);
43938494Sobrien    memset((voidp) fp, 0, sizeof(struct fh_cache));
44038494Sobrien    ins_que(&fp->fh_q, &fh_head);
44138494Sobrien  }
44238494Sobrien  if (!reuse_id)
443174294Sobrien    fp->fh_id = FHID_ALLOC();
444174294Sobrien  fp->fh_wchan = get_mntfs_wchan(mf);
44538494Sobrien  fp->fh_error = -1;
446174294Sobrien  fp->fh_cid = timeout(FH_TTL, discard_fh, (opaque_t) fp);
44738494Sobrien
44838494Sobrien  /*
449174294Sobrien   * If fs->fs_ip is null, remote server is probably down.
45038494Sobrien   */
45138494Sobrien  if (!fs->fs_ip) {
45238494Sobrien    /* Mark the fileserver down and invalid again */
45338494Sobrien    fs->fs_flags &= ~FSF_VALID;
45438494Sobrien    fs->fs_flags |= FSF_DOWN;
45538494Sobrien    error = AM_ERRNO_HOST_DOWN;
45638494Sobrien    return error;
45738494Sobrien  }
45838494Sobrien
45938494Sobrien  /*
460174294Sobrien   * Either fp has been freshly allocated or the address has changed.
461174294Sobrien   * Initialize address and nfs version.  Don't try to re-use the port
462174294Sobrien   * information unless using WebNFS where the port is fixed either by
463174294Sobrien   * the spec or the "port" mount option.
46438494Sobrien   */
46538494Sobrien  if (fp->fh_sin.sin_addr.s_addr != fs->fs_ip->sin_addr.s_addr) {
46638494Sobrien    fp->fh_sin = *fs->fs_ip;
467174294Sobrien    if (!(mf->mf_flags & MFF_WEBNFS))
468174294Sobrien	fp->fh_sin.sin_port = 0;
46938494Sobrien    fp->fh_nfs_version = fs->fs_version;
47038494Sobrien  }
471174294Sobrien
47238494Sobrien  fp->fh_fs = dup_srvr(fs);
47338494Sobrien  fp->fh_path = strdup(path);
47438494Sobrien
475174294Sobrien  if (mf->mf_flags & MFF_WEBNFS)
476174294Sobrien    error = webnfs_lookup(fp, got_nfs_fh_webnfs, get_mntfs_wchan(mf));
477174294Sobrien  else
478174294Sobrien    error = call_mountd(fp, MOUNTPROC_MNT, got_nfs_fh_mount, get_mntfs_wchan(mf));
47938494Sobrien  if (error) {
48038494Sobrien    /*
48138494Sobrien     * Local error - cache for a short period
48238494Sobrien     * just to prevent thrashing.
48338494Sobrien     */
48438494Sobrien    untimeout(fp->fh_cid);
48538494Sobrien    fp->fh_cid = timeout(error < 0 ? 2 * ALLOWED_MOUNT_TIME : FH_TTL_ERROR,
486174294Sobrien			 discard_fh, (opaque_t) fp);
48738494Sobrien    fp->fh_error = error;
48838494Sobrien  } else {
48938494Sobrien    error = fp->fh_error;
49038494Sobrien  }
49138494Sobrien
49238494Sobrien  return error;
49338494Sobrien}
49438494Sobrien
49538494Sobrien
49638494Sobrienint
49738494Sobrienmake_nfs_auth(void)
49838494Sobrien{
49938494Sobrien  AUTH_CREATE_GIDLIST_TYPE group_wheel = 0;
50038494Sobrien
50138494Sobrien  /* Some NFS mounts (particularly cross-domain) require FQDNs to succeed */
50238494Sobrien
50338494Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI
50438494Sobrien  if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) {
50582794Sobrien    plog(XLOG_INFO, "Using NFS auth for FQHN \"%s\"", hostd);
50638494Sobrien    nfs_auth = authsys_create(hostd, 0, 0, 1, &group_wheel);
50738494Sobrien  } else {
50838494Sobrien    nfs_auth = authsys_create_default();
50938494Sobrien  }
51038494Sobrien#else /* not HAVE_TRANSPORT_TYPE_TLI */
51138494Sobrien  if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) {
51282794Sobrien    plog(XLOG_INFO, "Using NFS auth for FQHN \"%s\"", hostd);
51338494Sobrien    nfs_auth = authunix_create(hostd, 0, 0, 1, &group_wheel);
51438494Sobrien  } else {
51538494Sobrien    nfs_auth = authunix_create_default();
51638494Sobrien  }
51738494Sobrien#endif /* not HAVE_TRANSPORT_TYPE_TLI */
51838494Sobrien
51938494Sobrien  if (!nfs_auth)
52038494Sobrien    return ENOBUFS;
52138494Sobrien
52238494Sobrien  return 0;
52338494Sobrien}
52438494Sobrien
52538494Sobrien
52638494Sobrienstatic int
527174294Sobriencall_mountd(fh_cache *fp, u_long proc, fwd_fun fun, wchan_t wchan)
52838494Sobrien{
52938494Sobrien  struct rpc_msg mnt_msg;
53038494Sobrien  int len;
531174294Sobrien  char iobuf[UDPMSGSIZE];
53238494Sobrien  int error;
53338494Sobrien  u_long mnt_version;
53438494Sobrien
53538494Sobrien  if (!nfs_auth) {
53638494Sobrien    error = make_nfs_auth();
53738494Sobrien    if (error)
53838494Sobrien      return error;
53938494Sobrien  }
54038494Sobrien
54138494Sobrien  if (fp->fh_sin.sin_port == 0) {
542174294Sobrien    u_short mountd_port;
543174294Sobrien    error = get_mountd_port(fp->fh_fs, &mountd_port, wchan);
54438494Sobrien    if (error)
54538494Sobrien      return error;
546174294Sobrien    fp->fh_sin.sin_port = mountd_port;
54738494Sobrien  }
54838494Sobrien
54938494Sobrien  /* find the right version of the mount protocol */
55038494Sobrien#ifdef HAVE_FS_NFS3
55138494Sobrien  if (fp->fh_nfs_version == NFS_VERSION3)
552174294Sobrien    mnt_version = AM_MOUNTVERS3;
55338494Sobrien  else
55438494Sobrien#endif /* HAVE_FS_NFS3 */
55538494Sobrien    mnt_version = MOUNTVERS;
55638494Sobrien  plog(XLOG_INFO, "call_mountd: NFS version %d, mount version %d",
55751292Sobrien       (int) fp->fh_nfs_version, (int) mnt_version);
55838494Sobrien
55938494Sobrien  rpc_msg_init(&mnt_msg, MOUNTPROG, mnt_version, MOUNTPROC_NULL);
56038494Sobrien  len = make_rpc_packet(iobuf,
56138494Sobrien			sizeof(iobuf),
56238494Sobrien			proc,
56338494Sobrien			&mnt_msg,
56438494Sobrien			(voidp) &fp->fh_path,
56538494Sobrien			(XDRPROC_T_TYPE) xdr_nfspath,
56638494Sobrien			nfs_auth);
56738494Sobrien
56838494Sobrien  if (len > 0) {
56938494Sobrien    error = fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD, fp->fh_id),
570174294Sobrien		       iobuf,
57138494Sobrien		       len,
57238494Sobrien		       &fp->fh_sin,
57338494Sobrien		       &fp->fh_sin,
574174294Sobrien		       (opaque_t) ((long) fp->fh_id), /* cast to long needed for 64-bit archs */
575174294Sobrien		       fun);
57638494Sobrien  } else {
57738494Sobrien    error = -len;
57838494Sobrien  }
57938494Sobrien
580174294Sobrien  /*
581174294Sobrien   * It may be the case that we're sending to the wrong MOUNTD port.  This
582174294Sobrien   * occurs if mountd is restarted on the server after the port has been
583174294Sobrien   * looked up and stored in the filehandle cache somewhere.  The correct
584174294Sobrien   * solution, if we're going to cache port numbers is to catch the ICMP
585174294Sobrien   * port unreachable reply from the server and cause the portmap request
586174294Sobrien   * to be redone.  The quick solution here is to invalidate the MOUNTD
587174294Sobrien   * port.
588174294Sobrien   */
58938494Sobrien  fp->fh_sin.sin_port = 0;
59038494Sobrien
59138494Sobrien  return error;
59238494Sobrien}
59338494Sobrien
59438494Sobrien
595174294Sobrienstatic int
596174294Sobrienwebnfs_lookup(fh_cache *fp, fwd_fun fun, wchan_t wchan)
597174294Sobrien{
598174294Sobrien  struct rpc_msg wnfs_msg;
599174294Sobrien  int len;
600174294Sobrien  char iobuf[UDPMSGSIZE];
601174294Sobrien  int error;
602174294Sobrien  u_long proc;
603174294Sobrien  XDRPROC_T_TYPE xdr_fn;
604174294Sobrien  voidp argp;
605174294Sobrien  nfsdiropargs args;
606174294Sobrien#ifdef HAVE_FS_NFS3
607174294Sobrien  am_LOOKUP3args args3;
608174294Sobrien#endif
609174294Sobrien  char *wnfs_path;
610174294Sobrien  size_t l;
611174294Sobrien
612174294Sobrien  if (!nfs_auth) {
613174294Sobrien    error = make_nfs_auth();
614174294Sobrien    if (error)
615174294Sobrien      return error;
616174294Sobrien  }
617174294Sobrien
618174294Sobrien  if (fp->fh_sin.sin_port == 0) {
619174294Sobrien    /* FIXME: wrong, don't discard sin_port in the first place for WebNFS. */
620174294Sobrien    plog(XLOG_WARNING, "webnfs_lookup: port == 0 for nfs on %s, fixed",
621174294Sobrien	 fp->fh_fs->fs_host);
622174294Sobrien    fp->fh_sin.sin_port = htons(NFS_PORT);
623174294Sobrien  }
624174294Sobrien
625174294Sobrien  /*
626174294Sobrien   * Use native path like the rest of amd (cf. RFC 2054, 6.1).
627174294Sobrien   */
628174294Sobrien  l = strlen(fp->fh_path) + 2;
629174294Sobrien  wnfs_path = (char *) xmalloc(l);
630174294Sobrien  wnfs_path[0] = 0x80;
631174294Sobrien  xstrlcpy(wnfs_path + 1, fp->fh_path, l - 1);
632174294Sobrien
633174294Sobrien  /* find the right program and lookup procedure */
634174294Sobrien#ifdef HAVE_FS_NFS3
635174294Sobrien  if (fp->fh_nfs_version == NFS_VERSION3) {
636174294Sobrien    proc = AM_NFSPROC3_LOOKUP;
637174294Sobrien    xdr_fn = (XDRPROC_T_TYPE) xdr_am_LOOKUP3args;
638174294Sobrien    argp = &args3;
639174294Sobrien    /* WebNFS public file handle */
640174294Sobrien    args3.what.dir.am_fh3_length = 0;
641174294Sobrien    args3.what.name = wnfs_path;
642174294Sobrien  } else {
643174294Sobrien#endif /* HAVE_FS_NFS3 */
644174294Sobrien    proc = NFSPROC_LOOKUP;
645174294Sobrien    xdr_fn = (XDRPROC_T_TYPE) xdr_diropargs;
646174294Sobrien    argp = &args;
647174294Sobrien    /* WebNFS public file handle */
648174294Sobrien    memset(&args.da_fhandle, 0, NFS_FHSIZE);
649174294Sobrien    args.da_name = wnfs_path;
650174294Sobrien#ifdef HAVE_FS_NFS3
651174294Sobrien  }
652174294Sobrien#endif /* HAVE_FS_NFS3 */
653174294Sobrien
654174294Sobrien  plog(XLOG_INFO, "webnfs_lookup: NFS version %d", (int) fp->fh_nfs_version);
655174294Sobrien
656174294Sobrien  rpc_msg_init(&wnfs_msg, NFS_PROGRAM, fp->fh_nfs_version, proc);
657174294Sobrien  len = make_rpc_packet(iobuf,
658174294Sobrien			sizeof(iobuf),
659174294Sobrien			proc,
660174294Sobrien			&wnfs_msg,
661174294Sobrien			argp,
662174294Sobrien			(XDRPROC_T_TYPE) xdr_fn,
663174294Sobrien			nfs_auth);
664174294Sobrien
665174294Sobrien  if (len > 0) {
666174294Sobrien    error = fwd_packet(MK_RPC_XID(RPC_XID_WEBNFS, fp->fh_id),
667174294Sobrien		       iobuf,
668174294Sobrien		       len,
669174294Sobrien		       &fp->fh_sin,
670174294Sobrien		       &fp->fh_sin,
671174294Sobrien		       (opaque_t) ((long) fp->fh_id), /* cast to long needed for 64-bit archs */
672174294Sobrien		       fun);
673174294Sobrien  } else {
674174294Sobrien    error = -len;
675174294Sobrien  }
676174294Sobrien
677174294Sobrien  XFREE(wnfs_path);
678174294Sobrien  return error;
679174294Sobrien}
680174294Sobrien
681174294Sobrien
68238494Sobrien/*
68338494Sobrien * NFS needs the local filesystem, remote filesystem
68438494Sobrien * remote hostname.
68538494Sobrien * Local filesystem defaults to remote and vice-versa.
68638494Sobrien */
687174294Sobrienstatic char *
68838494Sobriennfs_match(am_opts *fo)
68938494Sobrien{
69038494Sobrien  char *xmtab;
691174294Sobrien  size_t l;
69238494Sobrien
69338494Sobrien  if (fo->opt_fs && !fo->opt_rfs)
69438494Sobrien    fo->opt_rfs = fo->opt_fs;
69538494Sobrien  if (!fo->opt_rfs) {
69638494Sobrien    plog(XLOG_USER, "nfs: no remote filesystem specified");
69738494Sobrien    return NULL;
69838494Sobrien  }
69938494Sobrien  if (!fo->opt_rhost) {
70038494Sobrien    plog(XLOG_USER, "nfs: no remote host specified");
70138494Sobrien    return NULL;
70238494Sobrien  }
70338494Sobrien
70438494Sobrien  /*
70538494Sobrien   * Determine magic cookie to put in mtab
70638494Sobrien   */
707174294Sobrien  l = strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2;
708174294Sobrien  xmtab = (char *) xmalloc(l);
709174294Sobrien  xsnprintf(xmtab, l, "%s:%s", fo->opt_rhost, fo->opt_rfs);
71038494Sobrien  dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
71138494Sobrien       fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
71238494Sobrien
71338494Sobrien  return xmtab;
71438494Sobrien}
71538494Sobrien
71638494Sobrien
71738494Sobrien/*
71838494Sobrien * Initialize am structure for nfs
71938494Sobrien */
720174294Sobrienstatic int
72138494Sobriennfs_init(mntfs *mf)
72238494Sobrien{
72338494Sobrien  int error;
72438494Sobrien  am_nfs_handle_t fhs;
72538494Sobrien  char *colon;
72638494Sobrien
727174294Sobrien  if (mf->mf_private) {
728174294Sobrien    if (mf->mf_flags & MFF_NFS_SCALEDOWN) {
729174294Sobrien      fserver *fs;
73038494Sobrien
731174294Sobrien      /* tell remote mountd that we're done with this filehandle */
732174294Sobrien      mf->mf_ops->umounted(mf);
733174294Sobrien
734174294Sobrien      mf->mf_prfree(mf->mf_private);
735174294Sobrien      fs = mf->mf_ops->ffserver(mf);
736174294Sobrien      free_srvr(mf->mf_server);
737174294Sobrien      mf->mf_server = fs;
738174294Sobrien    } else
739174294Sobrien      return 0;
740174294Sobrien  }
741174294Sobrien
74238494Sobrien  colon = strchr(mf->mf_info, ':');
74338494Sobrien  if (colon == 0)
74438494Sobrien    return ENOENT;
74538494Sobrien
746174294Sobrien  error = prime_nfs_fhandle_cache(colon + 1, mf->mf_server, &fhs, mf);
74738494Sobrien  if (!error) {
748174294Sobrien    mf->mf_private = (opaque_t) ALLOC(am_nfs_handle_t);
749174294Sobrien    mf->mf_prfree = (void (*)(opaque_t)) free;
75038494Sobrien    memmove(mf->mf_private, (voidp) &fhs, sizeof(fhs));
75138494Sobrien  }
75238494Sobrien  return error;
75338494Sobrien}
75438494Sobrien
75538494Sobrien
75638494Sobrienint
757174294Sobrienmount_nfs_fh(am_nfs_handle_t *fhp, char *mntdir, char *fs_name, mntfs *mf)
75838494Sobrien{
75938494Sobrien  MTYPE_TYPE type;
76038494Sobrien  char *colon;
761174294Sobrien  char *xopts=NULL, transp_timeo_opts[40], transp_retrans_opts[40];
76238494Sobrien  char host[MAXHOSTNAMELEN + MAXPATHLEN + 2];
76338494Sobrien  fserver *fs = mf->mf_server;
76438494Sobrien  u_long nfs_version = fs->fs_version;
76538494Sobrien  char *nfs_proto = fs->fs_proto; /* "tcp" or "udp" */
766174294Sobrien  int on_autofs = mf->mf_flags & MFF_ON_AUTOFS;
76738494Sobrien  int error;
76838494Sobrien  int genflags;
76938494Sobrien  int retry;
770174294Sobrien  int proto = AMU_TYPE_NONE;
77138494Sobrien  mntent_t mnt;
77238494Sobrien  nfs_args_t nfs_args;
77338494Sobrien
77438494Sobrien  /*
77538494Sobrien   * Extract HOST name to give to kernel.
77638494Sobrien   * Some systems like osf1/aix3/bsd44 variants may need old code
77738494Sobrien   * for NFS_ARGS_NEEDS_PATH.
77838494Sobrien   */
77938494Sobrien  if (!(colon = strchr(fs_name, ':')))
78038494Sobrien    return ENOENT;
78138494Sobrien#ifdef MOUNT_TABLE_ON_FILE
78238494Sobrien  *colon = '\0';
78338494Sobrien#endif /* MOUNT_TABLE_ON_FILE */
784174294Sobrien  xstrlcpy(host, fs_name, sizeof(host));
78538494Sobrien#ifdef MOUNT_TABLE_ON_FILE
78638494Sobrien  *colon = ':';
78738494Sobrien#endif /* MOUNT_TABLE_ON_FILE */
78838494Sobrien#ifdef MAXHOSTNAMELEN
78938494Sobrien  /* most kernels have a name length restriction */
79038494Sobrien  if (strlen(host) >= MAXHOSTNAMELEN)
791174294Sobrien    xstrlcpy(host + MAXHOSTNAMELEN - 3, "..",
792174294Sobrien	     sizeof(host) - MAXHOSTNAMELEN + 3);
79338494Sobrien#endif /* MAXHOSTNAMELEN */
79438494Sobrien
795174294Sobrien  /*
796174294Sobrien   * Create option=VAL for udp/tcp specific timeouts and retrans values, but
797174294Sobrien   * only if these options were specified.
798174294Sobrien   */
799174294Sobrien
800174294Sobrien  transp_timeo_opts[0] = transp_retrans_opts[0] = '\0';	/* initialize */
801174294Sobrien  if (STREQ(nfs_proto, "udp"))
802174294Sobrien    proto = AMU_TYPE_UDP;
803174294Sobrien  else if (STREQ(nfs_proto, "tcp"))
804174294Sobrien    proto = AMU_TYPE_TCP;
805174294Sobrien  if (proto != AMU_TYPE_NONE) {
806174294Sobrien    if (gopt.amfs_auto_timeo[proto] > 0)
807174294Sobrien      xsnprintf(transp_timeo_opts, sizeof(transp_timeo_opts), "%s=%d,",
808174294Sobrien		MNTTAB_OPT_TIMEO, gopt.amfs_auto_timeo[proto]);
809174294Sobrien    if (gopt.amfs_auto_retrans[proto] > 0)
810174294Sobrien      xsnprintf(transp_retrans_opts, sizeof(transp_retrans_opts), "%s=%d,",
811174294Sobrien		MNTTAB_OPT_RETRANS, gopt.amfs_auto_retrans[proto]);
812174294Sobrien  }
813174294Sobrien
81451292Sobrien  if (mf->mf_remopts && *mf->mf_remopts &&
81551292Sobrien      !islocalnet(fs->fs_ip->sin_addr.s_addr)) {
81651292Sobrien    plog(XLOG_INFO, "Using remopts=\"%s\"", mf->mf_remopts);
817174294Sobrien    /* use transp_opts first, so map-specific opts will override */
818174294Sobrien    xopts = str3cat(xopts, transp_timeo_opts, transp_retrans_opts, mf->mf_remopts);
81951292Sobrien  } else {
820174294Sobrien    /* use transp_opts first, so map-specific opts will override */
821174294Sobrien    xopts = str3cat(xopts, transp_timeo_opts, transp_retrans_opts, mf->mf_mopts);
82251292Sobrien  }
82338494Sobrien
82438494Sobrien  memset((voidp) &mnt, 0, sizeof(mnt));
825174294Sobrien  mnt.mnt_dir = mntdir;
82638494Sobrien  mnt.mnt_fsname = fs_name;
82738494Sobrien  mnt.mnt_opts = xopts;
82838494Sobrien
82938494Sobrien  /*
83038494Sobrien   * Set mount types accordingly
83138494Sobrien   */
83238494Sobrien#ifndef HAVE_FS_NFS3
83338494Sobrien  type = MOUNT_TYPE_NFS;
83438494Sobrien  mnt.mnt_type = MNTTAB_TYPE_NFS;
83538494Sobrien#else /* HAVE_FS_NFS3 */
83638494Sobrien  if (nfs_version == NFS_VERSION3) {
83738494Sobrien    type = MOUNT_TYPE_NFS3;
83838494Sobrien    /*
83938494Sobrien     * Systems that include the mount table "vers" option generally do not
84038494Sobrien     * set the mnttab entry to "nfs3", but to "nfs" and then they set
84138494Sobrien     * "vers=3".  Setting it to "nfs3" works, but it may break some things
84238494Sobrien     * like "df -t nfs" and the "quota" program (esp. on Solaris and Irix).
84338494Sobrien     * So on those systems, set it to "nfs".
84438494Sobrien     * Note: MNTTAB_OPT_VERS is always set for NFS3 (see am_compat.h).
84538494Sobrien     */
84638494Sobrien# if defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE)
84738494Sobrien    mnt.mnt_type = MNTTAB_TYPE_NFS;
84838494Sobrien# else /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */
84938494Sobrien    mnt.mnt_type = MNTTAB_TYPE_NFS3;
85038494Sobrien# endif /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */
85138494Sobrien  } else {
85238494Sobrien    type = MOUNT_TYPE_NFS;
85338494Sobrien    mnt.mnt_type = MNTTAB_TYPE_NFS;
85438494Sobrien  }
85538494Sobrien#endif /* HAVE_FS_NFS3 */
85651292Sobrien  plog(XLOG_INFO, "mount_nfs_fh: NFS version %d", (int) nfs_version);
85738494Sobrien  plog(XLOG_INFO, "mount_nfs_fh: using NFS transport %s", nfs_proto);
85838494Sobrien
85938494Sobrien  retry = hasmntval(&mnt, MNTTAB_OPT_RETRY);
86038494Sobrien  if (retry <= 0)
86138494Sobrien    retry = 1;			/* XXX */
86238494Sobrien
86338494Sobrien  genflags = compute_mount_flags(&mnt);
864174294Sobrien#ifdef HAVE_FS_AUTOFS
865174294Sobrien  if (on_autofs)
866174294Sobrien    genflags |= autofs_compute_mount_flags(&mnt);
867174294Sobrien#endif /* HAVE_FS_AUTOFS */
86838494Sobrien
86938494Sobrien  /* setup the many fields and flags within nfs_args */
87038494Sobrien  compute_nfs_args(&nfs_args,
87138494Sobrien		   &mnt,
87238494Sobrien		   genflags,
87338494Sobrien		   NULL,	/* struct netconfig *nfsncp */
87438494Sobrien		   fs->fs_ip,
87538494Sobrien		   nfs_version,
87638494Sobrien		   nfs_proto,
87738494Sobrien		   fhp,
87838494Sobrien		   host,
87938494Sobrien		   fs_name);
88038494Sobrien
88138494Sobrien  /* finally call the mounting function */
882174294Sobrien  if (amuDebug(D_TRACE)) {
88338494Sobrien    print_nfs_args(&nfs_args, nfs_version);
88482794Sobrien    plog(XLOG_DEBUG, "Generic mount flags 0x%x used for NFS mount", genflags);
88551292Sobrien  }
88638494Sobrien  error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type,
887174294Sobrien		    nfs_version, nfs_proto, mnttab_file_name, on_autofs);
88838494Sobrien  XFREE(xopts);
88938494Sobrien
89038494Sobrien#ifdef HAVE_TRANSPORT_TYPE_TLI
89138494Sobrien  free_knetconfig(nfs_args.knconf);
89238494Sobrien  if (nfs_args.addr)
89338494Sobrien    XFREE(nfs_args.addr);	/* allocated in compute_nfs_args() */
89438494Sobrien#endif /* HAVE_TRANSPORT_TYPE_TLI */
89538494Sobrien
89638494Sobrien  return error;
89738494Sobrien}
89838494Sobrien
89938494Sobrien
90038494Sobrienstatic int
901174294Sobriennfs_mount(am_node *am, mntfs *mf)
90238494Sobrien{
903174294Sobrien  int error = 0;
904174294Sobrien  mntent_t mnt;
905174294Sobrien
90638494Sobrien  if (!mf->mf_private) {
907174294Sobrien    plog(XLOG_ERROR, "Missing filehandle for %s", mf->mf_info);
90838494Sobrien    return EINVAL;
90938494Sobrien  }
91038494Sobrien
911174294Sobrien  mnt.mnt_opts = mf->mf_mopts;
912174294Sobrien  if (amu_hasmntopt(&mnt, "softlookup") ||
913174294Sobrien      (amu_hasmntopt(&mnt, "soft") && !amu_hasmntopt(&mnt, "nosoftlookup")))
914174294Sobrien    am->am_flags |= AMF_SOFTLOOKUP;
91538494Sobrien
916174294Sobrien  error = mount_nfs_fh((am_nfs_handle_t *) mf->mf_private,
917174294Sobrien		       mf->mf_mount,
918174294Sobrien		       mf->mf_info,
919174294Sobrien		       mf);
92038494Sobrien
92138494Sobrien  if (error) {
92238494Sobrien    errno = error;
92338494Sobrien    dlog("mount_nfs: %m");
92438494Sobrien  }
92538494Sobrien
92638494Sobrien  return error;
92738494Sobrien}
92838494Sobrien
92938494Sobrien
930174294Sobrienstatic int
931174294Sobriennfs_umount(am_node *am, mntfs *mf)
93238494Sobrien{
933174294Sobrien  int unmount_flags, new_unmount_flags, error;
93438494Sobrien
935174294Sobrien  unmount_flags = (mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
936174294Sobrien  error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags);
937174294Sobrien
938174294Sobrien#if defined(HAVE_UMOUNT2) && (defined(MNT2_GEN_OPT_FORCE) || defined(MNT2_GEN_OPT_DETACH))
93938494Sobrien  /*
940174294Sobrien   * If the attempt to unmount failed with EBUSY, and this fserver was
941174294Sobrien   * marked for forced unmounts, then use forced/lazy unmounts.
942174294Sobrien   */
943174294Sobrien  if (error == EBUSY &&
944174294Sobrien      gopt.flags & CFM_FORCED_UNMOUNTS &&
945174294Sobrien      mf->mf_server->fs_flags & FSF_FORCE_UNMOUNT) {
946174294Sobrien    plog(XLOG_INFO, "EZK: nfs_umount: trying forced/lazy unmounts");
947174294Sobrien    /*
948174294Sobrien     * XXX: turning off the FSF_FORCE_UNMOUNT may not be perfectly
949174294Sobrien     * incorrect.  Multiple nodes may need to be timed out and restarted for
950174294Sobrien     * a single hung fserver.
951174294Sobrien     */
952174294Sobrien    mf->mf_server->fs_flags &= ~FSF_FORCE_UNMOUNT;
953174294Sobrien    new_unmount_flags = unmount_flags | AMU_UMOUNT_FORCE | AMU_UMOUNT_DETACH;
954174294Sobrien    error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, new_unmount_flags);
955174294Sobrien  }
956174294Sobrien#endif /* HAVE_UMOUNT2 && (MNT2_GEN_OPT_FORCE || MNT2_GEN_OPT_DETACH) */
957174294Sobrien
958174294Sobrien  /*
95938494Sobrien   * Here is some code to unmount 'restarted' file systems.
96038494Sobrien   * The restarted file systems are marked as 'nfs', not
96138494Sobrien   * 'host', so we only have the map information for the
96238494Sobrien   * the top-level mount.  The unmount will fail (EBUSY)
96338494Sobrien   * if there are anything else from the NFS server mounted
96438494Sobrien   * below the mount-point.  This code checks to see if there
96538494Sobrien   * is anything mounted with the same prefix as the
96638494Sobrien   * file system to be unmounted ("/a/b/c" when unmounting "/a/b").
96738494Sobrien   * If there is, and it is a 'restarted' file system, we unmount
96838494Sobrien   * it.
96938494Sobrien   * Added by Mike Mitchell, mcm@unx.sas.com, 09/08/93
97038494Sobrien   */
97138494Sobrien  if (error == EBUSY) {
97238494Sobrien    mntfs *new_mf;
97338494Sobrien    int len = strlen(mf->mf_mount);
97438494Sobrien    int didsome = 0;
97538494Sobrien
97638494Sobrien    ITER(new_mf, mntfs, &mfhead) {
97738494Sobrien      if (new_mf->mf_ops != mf->mf_ops ||
97838494Sobrien	  new_mf->mf_refc > 1 ||
97938494Sobrien	  mf == new_mf ||
98038494Sobrien	  ((new_mf->mf_flags & (MFF_MOUNTED | MFF_UNMOUNTING | MFF_RESTART)) == (MFF_MOUNTED | MFF_RESTART)))
98138494Sobrien	continue;
98238494Sobrien
98338494Sobrien      if (NSTREQ(mf->mf_mount, new_mf->mf_mount, len) &&
98438494Sobrien	  new_mf->mf_mount[len] == '/') {
985174294Sobrien	new_unmount_flags =
986174294Sobrien	  (new_mf->mf_flags & MFF_ON_AUTOFS) ? AMU_UMOUNT_AUTOFS : 0;
987174294Sobrien	UMOUNT_FS(new_mf->mf_mount, mnttab_file_name, new_unmount_flags);
98838494Sobrien	didsome = 1;
98938494Sobrien      }
99038494Sobrien    }
99138494Sobrien    if (didsome)
992174294Sobrien      error = UMOUNT_FS(mf->mf_mount, mnttab_file_name, unmount_flags);
99338494Sobrien  }
99438494Sobrien  if (error)
99538494Sobrien    return error;
99638494Sobrien
99738494Sobrien  return 0;
99838494Sobrien}
99938494Sobrien
100038494Sobrien
1001174294Sobrienstatic void
1002174294Sobriennfs_umounted(mntfs *mf)
100338494Sobrien{
100438494Sobrien  fserver *fs;
100538494Sobrien  char *colon, *path;
100638494Sobrien
100738494Sobrien  if (mf->mf_error || mf->mf_refc > 1)
100838494Sobrien    return;
100938494Sobrien
1010174294Sobrien  /*
1011174294Sobrien   * No need to inform mountd when WebNFS is in use.
1012174294Sobrien   */
1013174294Sobrien  if (mf->mf_flags & MFF_WEBNFS)
1014174294Sobrien    return;
101538494Sobrien
101638494Sobrien  /*
101738494Sobrien   * Call the mount daemon on the server to announce that we are not using
101838494Sobrien   * the fs any more.
101938494Sobrien   *
1020174294Sobrien   * XXX: This is *wrong*.  The mountd should be called when the fhandle is
102138494Sobrien   * flushed from the cache, and a reference held to the cached entry while
102238494Sobrien   * the fs is mounted...
102338494Sobrien   */
1024174294Sobrien  fs = mf->mf_server;
102538494Sobrien  colon = path = strchr(mf->mf_info, ':');
102638494Sobrien  if (fs && colon) {
102738494Sobrien    fh_cache f;
102838494Sobrien
102938494Sobrien    dlog("calling mountd for %s", mf->mf_info);
103038494Sobrien    *path++ = '\0';
103138494Sobrien    f.fh_path = path;
103238494Sobrien    f.fh_sin = *fs->fs_ip;
103338494Sobrien    f.fh_sin.sin_port = (u_short) 0;
103438494Sobrien    f.fh_nfs_version = fs->fs_version;
103538494Sobrien    f.fh_fs = fs;
103638494Sobrien    f.fh_id = 0;
103738494Sobrien    f.fh_error = 0;
1038174294Sobrien    prime_nfs_fhandle_cache(colon + 1, mf->mf_server, (am_nfs_handle_t *) 0, mf);
1039174294Sobrien    call_mountd(&f, MOUNTPROC_UMNT, (fwd_fun *) 0, (wchan_t) 0);
104038494Sobrien    *colon = ':';
104138494Sobrien  }
104238494Sobrien}
1043