Merge from vendor branch LESS:
[dragonfly.git] / contrib / amd / amd / ops_nfs.c
1 /*
2  * Copyright (c) 1997-1999 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.5 1999/03/13 17:03:28 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  */
92 typedef struct fh_cache fh_cache;
93 struct 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 */
107 static int call_mountd(fh_cache *fp, u_long proc, fwd_fun f, voidp wchan);
108 static int fh_id = 0;
109
110 /* globals */
111 AUTH *nfs_auth;
112 qelem fh_head = {&fh_head, &fh_head};
113
114 /*
115  * Network file system operations
116  */
117 am_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
136 static fh_cache *
137 find_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  */
169 static void
170 got_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
209 void
210 flush_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
223 static void
224 discard_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  */
244 static int
245 prime_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
391 int
392 make_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
421 static int
422 call_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  */
495 char *
496 nfs_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  */
528 int
529 nfs_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
552 int
553 mount_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", 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
684 static int
685 mount_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
696 int
697 nfs_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
714 int
715 nfs_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
760 void
761 nfs_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 }