ops_nfs.c revision 82794
1/*
2 * Copyright (c) 1997-2001 Erez Zadok
3 * Copyright (c) 1990 Jan-Simon Pendry
4 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
5 * Copyright (c) 1990 The Regents of the University of California.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 *    must display the following acknowledgment:
21 *      This product includes software developed by the University of
22 *      California, Berkeley and its contributors.
23 * 4. Neither the name of the University nor the names of its contributors
24 *    may be used to endorse or promote products derived from this software
25 *    without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
28 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
29 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
30 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
31 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
32 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
33 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
35 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
36 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * SUCH DAMAGE.
38 *
39 *      %W% (Berkeley) %G%
40 *
41 * $Id: ops_nfs.c,v 1.6.2.3 2001/04/14 21:08:22 ezk Exp $
42 *
43 */
44
45/*
46 * Network file system
47 */
48
49#ifdef HAVE_CONFIG_H
50# include <config.h>
51#endif /* HAVE_CONFIG_H */
52#include <am_defs.h>
53#include <amd.h>
54
55/*
56 * Convert from nfsstat to UN*X error code
57 */
58#define unx_error(e)	((int)(e))
59
60/*
61 * FH_TTL is the time a file handle will remain in the cache since
62 * last being used.  If the file handle becomes invalid, then it
63 * will be flushed anyway.
64 */
65#define	FH_TTL			(5 * 60) /* five minutes */
66#define	FH_TTL_ERROR		(30) /* 30 seconds */
67#define	FHID_ALLOC(struct)	(++fh_id)
68
69/*
70 * The NFS layer maintains a cache of file handles.
71 * This is *fundamental* to the implementation and
72 * also allows quick remounting when a filesystem
73 * is accessed soon after timing out.
74 *
75 * The NFS server layer knows to flush this cache
76 * when a server goes down so avoiding stale handles.
77 *
78 * Each cache entry keeps a hard reference to
79 * the corresponding server.  This ensures that
80 * the server keepalive information is maintained.
81 *
82 * The copy of the sockaddr_in here is taken so
83 * that the port can be twiddled to talk to mountd
84 * instead of portmap or the NFS server as used
85 * elsewhere.
86 * The port# is flushed if a server goes down.
87 * The IP address is never flushed - we assume
88 * that the address of a mounted machine never
89 * changes.  If it does, then you have other
90 * problems...
91 */
92typedef struct fh_cache fh_cache;
93struct fh_cache {
94  qelem			fh_q;		/* List header */
95  voidp			fh_wchan;	/* Wait channel */
96  int			fh_error;	/* Valid data? */
97  int			fh_id;		/* Unique id */
98  int			fh_cid;		/* Callout id */
99  u_long		fh_nfs_version;	/* highest NFS version on host */
100  am_nfs_handle_t	fh_nfs_handle;	/* Handle on filesystem */
101  struct sockaddr_in	fh_sin;		/* Address of mountd */
102  fserver		*fh_fs;		/* Server holding filesystem */
103  char			*fh_path;	/* Filesystem on host */
104};
105
106/* forward definitions */
107static int call_mountd(fh_cache *fp, u_long proc, fwd_fun f, voidp wchan);
108static int fh_id = 0;
109
110/* globals */
111AUTH *nfs_auth;
112qelem fh_head = {&fh_head, &fh_head};
113
114/*
115 * Network file system operations
116 */
117am_ops nfs_ops =
118{
119  "nfs",
120  nfs_match,
121  nfs_init,
122  amfs_auto_fmount,
123  nfs_fmount,
124  amfs_auto_fumount,
125  nfs_fumount,
126  amfs_error_lookuppn,
127  amfs_error_readdir,
128  0,				/* nfs_readlink */
129  0,				/* nfs_mounted */
130  nfs_umounted,
131  find_nfs_srvr,
132  FS_MKMNT | FS_BACKGROUND | FS_AMQINFO
133};
134
135
136static fh_cache *
137find_nfs_fhandle_cache(voidp idv, int done)
138{
139  fh_cache *fp, *fp2 = 0;
140  int id = (long) idv;		/* for 64-bit archs */
141
142  ITER(fp, fh_cache, &fh_head) {
143    if (fp->fh_id == id) {
144      fp2 = fp;
145      break;
146    }
147  }
148
149#ifdef DEBUG
150  if (fp2) {
151    dlog("fh cache gives fp %#lx, fs %s", (unsigned long) fp2, fp2->fh_path);
152  } else {
153    dlog("fh cache search failed");
154  }
155#endif /* DEBUG */
156
157  if (fp2 && !done) {
158    fp2->fh_error = ETIMEDOUT;
159    return 0;
160  }
161
162  return fp2;
163}
164
165
166/*
167 * Called when a filehandle appears
168 */
169static void
170got_nfs_fh(voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, voidp idv, int done)
171{
172  fh_cache *fp;
173
174  fp = find_nfs_fhandle_cache(idv, done);
175  if (!fp)
176    return;
177
178  /*
179   * retrieve the correct RPC reply for the file handle, based on the
180   * NFS protocol version.
181   */
182#ifdef HAVE_FS_NFS3
183  if (fp->fh_nfs_version == NFS_VERSION3)
184    fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &fp->fh_nfs_handle.v3,
185				    (XDRPROC_T_TYPE) xdr_mountres3);
186  else
187#endif /* HAVE_FS_NFS3 */
188    fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &fp->fh_nfs_handle.v2,
189				    (XDRPROC_T_TYPE) xdr_fhstatus);
190
191  if (!fp->fh_error) {
192#ifdef DEBUG
193    dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
194#endif /* DEBUG */
195
196    /*
197     * Wakeup anything sleeping on this filehandle
198     */
199    if (fp->fh_wchan) {
200#ifdef DEBUG
201      dlog("Calling wakeup on %#lx", (unsigned long) fp->fh_wchan);
202#endif /* DEBUG */
203      wakeup(fp->fh_wchan);
204    }
205  }
206}
207
208
209void
210flush_nfs_fhandle_cache(fserver *fs)
211{
212  fh_cache *fp;
213
214  ITER(fp, fh_cache, &fh_head) {
215    if (fp->fh_fs == fs || fs == 0) {
216      fp->fh_sin.sin_port = (u_short) 0;
217      fp->fh_error = -1;
218    }
219  }
220}
221
222
223static void
224discard_fh(voidp v)
225{
226  fh_cache *fp = v;
227
228  rem_que(&fp->fh_q);
229  if (fp->fh_fs) {
230#ifdef DEBUG
231    dlog("Discarding filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
232#endif /* DEBUG */
233    free_srvr(fp->fh_fs);
234  }
235  if (fp->fh_path)
236    XFREE(fp->fh_path);
237  XFREE(fp);
238}
239
240
241/*
242 * Determine the file handle for a node
243 */
244static int
245prime_nfs_fhandle_cache(char *path, fserver *fs, am_nfs_handle_t *fhbuf, voidp wchan)
246{
247  fh_cache *fp, *fp_save = 0;
248  int error;
249  int reuse_id = FALSE;
250
251#ifdef DEBUG
252  dlog("Searching cache for %s:%s", fs->fs_host, path);
253#endif /* DEBUG */
254
255  /*
256   * First search the cache
257   */
258  ITER(fp, fh_cache, &fh_head) {
259    if (fs == fp->fh_fs && STREQ(path, fp->fh_path)) {
260      switch (fp->fh_error) {
261      case 0:
262	plog(XLOG_INFO, "prime_nfs_fhandle_cache: NFS version %d", (int) fp->fh_nfs_version);
263#ifdef HAVE_FS_NFS3
264	if (fp->fh_nfs_version == NFS_VERSION3)
265	  error = fp->fh_error = unx_error(fp->fh_nfs_handle.v3.fhs_status);
266	else
267#endif /* HAVE_FS_NFS3 */
268	  error = fp->fh_error = unx_error(fp->fh_nfs_handle.v2.fhs_status);
269	if (error == 0) {
270	  if (fhbuf) {
271#ifdef HAVE_FS_NFS3
272	    if (fp->fh_nfs_version == NFS_VERSION3)
273	      memmove((voidp) &(fhbuf->v3), (voidp) &(fp->fh_nfs_handle.v3),
274		      sizeof(fp->fh_nfs_handle.v3));
275	    else
276#endif /* HAVE_FS_NFS3 */
277	      memmove((voidp) &(fhbuf->v2), (voidp) &(fp->fh_nfs_handle.v2),
278		      sizeof(fp->fh_nfs_handle.v2));
279	  }
280	  if (fp->fh_cid)
281	    untimeout(fp->fh_cid);
282	  fp->fh_cid = timeout(FH_TTL, discard_fh, (voidp) fp);
283	} else if (error == EACCES) {
284	  /*
285	   * Now decode the file handle return code.
286	   */
287	  plog(XLOG_INFO, "Filehandle denied for \"%s:%s\"",
288	       fs->fs_host, path);
289	} else {
290	  errno = error;	/* XXX */
291	  plog(XLOG_INFO, "Filehandle error for \"%s:%s\": %m",
292	       fs->fs_host, path);
293	}
294
295	/*
296	 * The error was returned from the remote mount daemon.
297	 * Policy: this error will be cached for now...
298	 */
299	return error;
300
301      case -1:
302	/*
303	 * Still thinking about it, but we can re-use.
304	 */
305	fp_save = fp;
306	reuse_id = TRUE;
307	break;
308
309      default:
310	/*
311	 * Return the error.
312	 * Policy: make sure we recompute if required again
313	 * in case this was caused by a network failure.
314	 * This can thrash mountd's though...  If you find
315	 * your mountd going slowly then:
316	 * 1.  Add a fork() loop to main.
317	 * 2.  Remove the call to innetgr() and don't use
318	 *     netgroups, especially if you don't use YP.
319	 */
320	error = fp->fh_error;
321	fp->fh_error = -1;
322	return error;
323      }
324      break;
325    }
326  }
327
328  /*
329   * Not in cache
330   */
331  if (fp_save) {
332    fp = fp_save;
333    /*
334     * Re-use existing slot
335     */
336    untimeout(fp->fh_cid);
337    free_srvr(fp->fh_fs);
338    XFREE(fp->fh_path);
339  } else {
340    fp = ALLOC(struct fh_cache);
341    memset((voidp) fp, 0, sizeof(struct fh_cache));
342    ins_que(&fp->fh_q, &fh_head);
343  }
344  if (!reuse_id)
345    fp->fh_id = FHID_ALLOC(struct );
346  fp->fh_wchan = wchan;
347  fp->fh_error = -1;
348  fp->fh_cid = timeout(FH_TTL, discard_fh, (voidp) fp);
349
350  /*
351   * if fs->fs_ip is null, remote server is probably down.
352   */
353  if (!fs->fs_ip) {
354    /* Mark the fileserver down and invalid again */
355    fs->fs_flags &= ~FSF_VALID;
356    fs->fs_flags |= FSF_DOWN;
357    error = AM_ERRNO_HOST_DOWN;
358    return error;
359  }
360
361  /*
362   * If the address has changed then don't try to re-use the
363   * port information
364   */
365  if (fp->fh_sin.sin_addr.s_addr != fs->fs_ip->sin_addr.s_addr) {
366    fp->fh_sin = *fs->fs_ip;
367    fp->fh_sin.sin_port = 0;
368    fp->fh_nfs_version = fs->fs_version;
369  }
370  fp->fh_fs = dup_srvr(fs);
371  fp->fh_path = strdup(path);
372
373  error = call_mountd(fp, MOUNTPROC_MNT, got_nfs_fh, wchan);
374  if (error) {
375    /*
376     * Local error - cache for a short period
377     * just to prevent thrashing.
378     */
379    untimeout(fp->fh_cid);
380    fp->fh_cid = timeout(error < 0 ? 2 * ALLOWED_MOUNT_TIME : FH_TTL_ERROR,
381			 discard_fh, (voidp) fp);
382    fp->fh_error = error;
383  } else {
384    error = fp->fh_error;
385  }
386
387  return error;
388}
389
390
391int
392make_nfs_auth(void)
393{
394  AUTH_CREATE_GIDLIST_TYPE group_wheel = 0;
395
396  /* Some NFS mounts (particularly cross-domain) require FQDNs to succeed */
397
398#ifdef HAVE_TRANSPORT_TYPE_TLI
399  if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) {
400    plog(XLOG_INFO, "Using NFS auth for FQHN \"%s\"", hostd);
401    nfs_auth = authsys_create(hostd, 0, 0, 1, &group_wheel);
402  } else {
403    nfs_auth = authsys_create_default();
404  }
405#else /* not HAVE_TRANSPORT_TYPE_TLI */
406  if (gopt.flags & CFM_FULLY_QUALIFIED_HOSTS) {
407    plog(XLOG_INFO, "Using NFS auth for FQHN \"%s\"", hostd);
408    nfs_auth = authunix_create(hostd, 0, 0, 1, &group_wheel);
409  } else {
410    nfs_auth = authunix_create_default();
411  }
412#endif /* not HAVE_TRANSPORT_TYPE_TLI */
413
414  if (!nfs_auth)
415    return ENOBUFS;
416
417  return 0;
418}
419
420
421static int
422call_mountd(fh_cache *fp, u_long proc, fwd_fun f, voidp wchan)
423{
424  struct rpc_msg mnt_msg;
425  int len;
426  char iobuf[8192];
427  int error;
428  u_long mnt_version;
429
430  if (!nfs_auth) {
431    error = make_nfs_auth();
432    if (error)
433      return error;
434  }
435
436  if (fp->fh_sin.sin_port == 0) {
437    u_short port;
438    error = nfs_srvr_port(fp->fh_fs, &port, wchan);
439    if (error)
440      return error;
441    fp->fh_sin.sin_port = port;
442  }
443
444  /* find the right version of the mount protocol */
445#ifdef HAVE_FS_NFS3
446  if (fp->fh_nfs_version == NFS_VERSION3)
447    mnt_version = MOUNTVERS3;
448  else
449#endif /* HAVE_FS_NFS3 */
450    mnt_version = MOUNTVERS;
451  plog(XLOG_INFO, "call_mountd: NFS version %d, mount version %d",
452       (int) fp->fh_nfs_version, (int) mnt_version);
453
454  rpc_msg_init(&mnt_msg, MOUNTPROG, mnt_version, MOUNTPROC_NULL);
455  len = make_rpc_packet(iobuf,
456			sizeof(iobuf),
457			proc,
458			&mnt_msg,
459			(voidp) &fp->fh_path,
460			(XDRPROC_T_TYPE) xdr_nfspath,
461			nfs_auth);
462
463  if (len > 0) {
464    error = fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD, fp->fh_id),
465		       (voidp) iobuf,
466		       len,
467		       &fp->fh_sin,
468		       &fp->fh_sin,
469		       (voidp) ((long) fp->fh_id), /* for 64-bit archs */
470		       f);
471  } else {
472    error = -len;
473  }
474
475/*
476 * It may be the case that we're sending to the wrong MOUNTD port.  This
477 * occurs if mountd is restarted on the server after the port has been
478 * looked up and stored in the filehandle cache somewhere.  The correct
479 * solution, if we're going to cache port numbers is to catch the ICMP
480 * port unreachable reply from the server and cause the portmap request
481 * to be redone.  The quick solution here is to invalidate the MOUNTD
482 * port.
483 */
484  fp->fh_sin.sin_port = 0;
485
486  return error;
487}
488
489
490/*
491 * NFS needs the local filesystem, remote filesystem
492 * remote hostname.
493 * Local filesystem defaults to remote and vice-versa.
494 */
495char *
496nfs_match(am_opts *fo)
497{
498  char *xmtab;
499
500  if (fo->opt_fs && !fo->opt_rfs)
501    fo->opt_rfs = fo->opt_fs;
502  if (!fo->opt_rfs) {
503    plog(XLOG_USER, "nfs: no remote filesystem specified");
504    return NULL;
505  }
506  if (!fo->opt_rhost) {
507    plog(XLOG_USER, "nfs: no remote host specified");
508    return NULL;
509  }
510
511  /*
512   * Determine magic cookie to put in mtab
513   */
514  xmtab = (char *) xmalloc(strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2);
515  sprintf(xmtab, "%s:%s", fo->opt_rhost, fo->opt_rfs);
516#ifdef DEBUG
517  dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
518       fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
519#endif /* DEBUG */
520
521  return xmtab;
522}
523
524
525/*
526 * Initialize am structure for nfs
527 */
528int
529nfs_init(mntfs *mf)
530{
531  int error;
532  am_nfs_handle_t fhs;
533  char *colon;
534
535  if (mf->mf_private)
536    return 0;
537
538  colon = strchr(mf->mf_info, ':');
539  if (colon == 0)
540    return ENOENT;
541
542  error = prime_nfs_fhandle_cache(colon + 1, mf->mf_server, &fhs, (voidp) mf);
543  if (!error) {
544    mf->mf_private = (voidp) ALLOC(am_nfs_handle_t);
545    mf->mf_prfree = (void (*)(voidp)) free;
546    memmove(mf->mf_private, (voidp) &fhs, sizeof(fhs));
547  }
548  return error;
549}
550
551
552int
553mount_nfs_fh(am_nfs_handle_t *fhp, char *dir, char *fs_name, char *opts, mntfs *mf)
554{
555  MTYPE_TYPE type;
556  char *colon;
557  char *xopts;
558  char host[MAXHOSTNAMELEN + MAXPATHLEN + 2];
559  fserver *fs = mf->mf_server;
560  u_long nfs_version = fs->fs_version;
561  char *nfs_proto = fs->fs_proto; /* "tcp" or "udp" */
562  int error;
563  int genflags;
564  int retry;
565  mntent_t mnt;
566  nfs_args_t nfs_args;
567
568  /*
569   * Extract HOST name to give to kernel.
570   * Some systems like osf1/aix3/bsd44 variants may need old code
571   * for NFS_ARGS_NEEDS_PATH.
572   */
573  if (!(colon = strchr(fs_name, ':')))
574    return ENOENT;
575#ifdef MOUNT_TABLE_ON_FILE
576  *colon = '\0';
577#endif /* MOUNT_TABLE_ON_FILE */
578  strncpy(host, fs_name, sizeof(host));
579#ifdef MOUNT_TABLE_ON_FILE
580  *colon = ':';
581#endif /* MOUNT_TABLE_ON_FILE */
582#ifdef MAXHOSTNAMELEN
583  /* most kernels have a name length restriction */
584  if (strlen(host) >= MAXHOSTNAMELEN)
585    strcpy(host + MAXHOSTNAMELEN - 3, "..");
586#endif /* MAXHOSTNAMELEN */
587
588  if (mf->mf_remopts && *mf->mf_remopts &&
589      !islocalnet(fs->fs_ip->sin_addr.s_addr)) {
590    plog(XLOG_INFO, "Using remopts=\"%s\"", mf->mf_remopts);
591    xopts = strdup(mf->mf_remopts);
592  } else {
593    xopts = strdup(opts);
594  }
595
596  memset((voidp) &mnt, 0, sizeof(mnt));
597  mnt.mnt_dir = dir;
598  mnt.mnt_fsname = fs_name;
599  mnt.mnt_opts = xopts;
600
601  /*
602   * Set mount types accordingly
603   */
604#ifndef HAVE_FS_NFS3
605  type = MOUNT_TYPE_NFS;
606  mnt.mnt_type = MNTTAB_TYPE_NFS;
607#else /* HAVE_FS_NFS3 */
608  if (nfs_version == NFS_VERSION3) {
609    type = MOUNT_TYPE_NFS3;
610    /*
611     * Systems that include the mount table "vers" option generally do not
612     * set the mnttab entry to "nfs3", but to "nfs" and then they set
613     * "vers=3".  Setting it to "nfs3" works, but it may break some things
614     * like "df -t nfs" and the "quota" program (esp. on Solaris and Irix).
615     * So on those systems, set it to "nfs".
616     * Note: MNTTAB_OPT_VERS is always set for NFS3 (see am_compat.h).
617     */
618# if defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE)
619    mnt.mnt_type = MNTTAB_TYPE_NFS;
620# else /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */
621    mnt.mnt_type = MNTTAB_TYPE_NFS3;
622# endif /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */
623  } else {
624    type = MOUNT_TYPE_NFS;
625    mnt.mnt_type = MNTTAB_TYPE_NFS;
626  }
627#endif /* HAVE_FS_NFS3 */
628  plog(XLOG_INFO, "mount_nfs_fh: NFS version %d", (int) nfs_version);
629#if defined(HAVE_FS_NFS3) || defined(HAVE_TRANSPORT_TYPE_TLI)
630  plog(XLOG_INFO, "mount_nfs_fh: using NFS transport %s", nfs_proto);
631#endif /* defined(HAVE_FS_NFS3) || defined(HAVE_TRANSPORT_TYPE_TLI) */
632
633  retry = hasmntval(&mnt, MNTTAB_OPT_RETRY);
634  if (retry <= 0)
635    retry = 1;			/* XXX */
636
637  genflags = compute_mount_flags(&mnt);
638
639  /* setup the many fields and flags within nfs_args */
640#ifdef HAVE_TRANSPORT_TYPE_TLI
641  compute_nfs_args(&nfs_args,
642		   &mnt,
643		   genflags,
644		   NULL,	/* struct netconfig *nfsncp */
645		   fs->fs_ip,
646		   nfs_version,
647		   nfs_proto,
648		   fhp,
649		   host,
650		   fs_name);
651#else /* not HAVE_TRANSPORT_TYPE_TLI */
652  compute_nfs_args(&nfs_args,
653		   &mnt,
654		   genflags,
655		   fs->fs_ip,
656		   nfs_version,
657		   nfs_proto,
658		   fhp,
659		   host,
660		   fs_name);
661#endif /* not HAVE_TRANSPORT_TYPE_TLI */
662
663  /* finally call the mounting function */
664#ifdef DEBUG
665  amuDebug(D_TRACE) {
666    print_nfs_args(&nfs_args, nfs_version);
667    plog(XLOG_DEBUG, "Generic mount flags 0x%x used for NFS mount", genflags);
668  }
669#endif /* DEBUG */
670  error = mount_fs(&mnt, genflags, (caddr_t) &nfs_args, retry, type,
671		   nfs_version, nfs_proto, mnttab_file_name);
672  XFREE(xopts);
673
674#ifdef HAVE_TRANSPORT_TYPE_TLI
675  free_knetconfig(nfs_args.knconf);
676  if (nfs_args.addr)
677    XFREE(nfs_args.addr);	/* allocated in compute_nfs_args() */
678#endif /* HAVE_TRANSPORT_TYPE_TLI */
679
680  return error;
681}
682
683
684static int
685mount_nfs(char *dir, char *fs_name, char *opts, mntfs *mf)
686{
687  if (!mf->mf_private) {
688    plog(XLOG_ERROR, "Missing filehandle for %s", fs_name);
689    return EINVAL;
690  }
691
692  return mount_nfs_fh((am_nfs_handle_t *) mf->mf_private, dir, fs_name, opts, mf);
693}
694
695
696int
697nfs_fmount(mntfs *mf)
698{
699  int error = 0;
700
701  error = mount_nfs(mf->mf_mount, mf->mf_info, mf->mf_mopts, mf);
702
703#ifdef DEBUG
704  if (error) {
705    errno = error;
706    dlog("mount_nfs: %m");
707  }
708#endif /* DEBUG */
709
710  return error;
711}
712
713
714int
715nfs_fumount(mntfs *mf)
716{
717  int error = UMOUNT_FS(mf->mf_mount, mnttab_file_name);
718
719  /*
720   * Here is some code to unmount 'restarted' file systems.
721   * The restarted file systems are marked as 'nfs', not
722   * 'host', so we only have the map information for the
723   * the top-level mount.  The unmount will fail (EBUSY)
724   * if there are anything else from the NFS server mounted
725   * below the mount-point.  This code checks to see if there
726   * is anything mounted with the same prefix as the
727   * file system to be unmounted ("/a/b/c" when unmounting "/a/b").
728   * If there is, and it is a 'restarted' file system, we unmount
729   * it.
730   * Added by Mike Mitchell, mcm@unx.sas.com, 09/08/93
731   */
732  if (error == EBUSY) {
733    mntfs *new_mf;
734    int len = strlen(mf->mf_mount);
735    int didsome = 0;
736
737    ITER(new_mf, mntfs, &mfhead) {
738      if (new_mf->mf_ops != mf->mf_ops ||
739	  new_mf->mf_refc > 1 ||
740	  mf == new_mf ||
741	  ((new_mf->mf_flags & (MFF_MOUNTED | MFF_UNMOUNTING | MFF_RESTART)) == (MFF_MOUNTED | MFF_RESTART)))
742	continue;
743
744      if (NSTREQ(mf->mf_mount, new_mf->mf_mount, len) &&
745	  new_mf->mf_mount[len] == '/') {
746	UMOUNT_FS(new_mf->mf_mount, mnttab_file_name);
747	didsome = 1;
748      }
749    }
750    if (didsome)
751      error = UMOUNT_FS(mf->mf_mount, mnttab_file_name);
752  }
753  if (error)
754    return error;
755
756  return 0;
757}
758
759
760void
761nfs_umounted(am_node *mp)
762{
763  /*
764   * Don't bother to inform remote mountd that we are finished.  Until a
765   * full track of filehandles is maintained the mountd unmount callback
766   * cannot be done correctly anyway...
767   */
768  mntfs *mf = mp->am_mnt;
769  fserver *fs;
770  char *colon, *path;
771
772  if (mf->mf_error || mf->mf_refc > 1)
773    return;
774
775  fs = mf->mf_server;
776
777  /*
778   * Call the mount daemon on the server to announce that we are not using
779   * the fs any more.
780   *
781   * This is *wrong*.  The mountd should be called when the fhandle is
782   * flushed from the cache, and a reference held to the cached entry while
783   * the fs is mounted...
784   */
785  colon = path = strchr(mf->mf_info, ':');
786  if (fs && colon) {
787    fh_cache f;
788
789#ifdef DEBUG
790    dlog("calling mountd for %s", mf->mf_info);
791#endif /* DEBUG */
792    *path++ = '\0';
793    f.fh_path = path;
794    f.fh_sin = *fs->fs_ip;
795    f.fh_sin.sin_port = (u_short) 0;
796    f.fh_nfs_version = fs->fs_version;
797    f.fh_fs = fs;
798    f.fh_id = 0;
799    f.fh_error = 0;
800    prime_nfs_fhandle_cache(colon + 1, mf->mf_server, (am_nfs_handle_t *) 0, (voidp) mf);
801    call_mountd(&f, MOUNTPROC_UMNT, (fwd_fun) 0, (voidp) 0);
802    *colon = ':';
803  }
804}
805