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.
8 * This code is derived from software contributed to Berkeley by
9 * Jan-Simon Pendry at Imperial College, London.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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
41 * $Id: ops_nfs.c,v 1.5 1999/03/13 17:03:28 ezk Exp $
51 #endif /* HAVE_CONFIG_H */
56 * Convert from nfsstat to UN*X error code
58 #define unx_error(e) ((int)(e))
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.
65 #define FH_TTL (5 * 60) /* five minutes */
66 #define FH_TTL_ERROR (30) /* 30 seconds */
67 #define FHID_ALLOC(struct) (++fh_id)
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.
75 * The NFS server layer knows to flush this cache
76 * when a server goes down so avoiding stale handles.
78 * Each cache entry keeps a hard reference to
79 * the corresponding server. This ensures that
80 * the server keepalive information is maintained.
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
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
92 typedef struct fh_cache 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 */
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;
112 qelem fh_head
= {&fh_head
, &fh_head
};
115 * Network file system operations
128 0, /* nfs_readlink */
132 FS_MKMNT
| FS_BACKGROUND
| FS_AMQINFO
137 find_nfs_fhandle_cache(voidp idv
, int done
)
139 fh_cache
*fp
, *fp2
= 0;
140 int id
= (long) idv
; /* for 64-bit archs */
142 ITER(fp
, fh_cache
, &fh_head
) {
143 if (fp
->fh_id
== id
) {
151 dlog("fh cache gives fp %#lx, fs %s", (unsigned long) fp2
, fp2
->fh_path
);
153 dlog("fh cache search failed");
158 fp2
->fh_error
= ETIMEDOUT
;
167 * Called when a filehandle appears
170 got_nfs_fh(voidp pkt
, int len
, struct sockaddr_in
* sa
, struct sockaddr_in
* ia
, voidp idv
, int done
)
174 fp
= find_nfs_fhandle_cache(idv
, done
);
179 * retrieve the correct RPC reply for the file handle, based on the
180 * NFS protocol version.
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
);
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
);
193 dlog("got filehandle for %s:%s", fp
->fh_fs
->fs_host
, fp
->fh_path
);
197 * Wakeup anything sleeping on this filehandle
201 dlog("Calling wakeup on %#lx", (unsigned long) fp
->fh_wchan
);
203 wakeup(fp
->fh_wchan
);
210 flush_nfs_fhandle_cache(fserver
*fs
)
214 ITER(fp
, fh_cache
, &fh_head
) {
215 if (fp
->fh_fs
== fs
|| fs
== 0) {
216 fp
->fh_sin
.sin_port
= (u_short
) 0;
231 dlog("Discarding filehandle for %s:%s", fp
->fh_fs
->fs_host
, fp
->fh_path
);
233 free_srvr(fp
->fh_fs
);
242 * Determine the file handle for a node
245 prime_nfs_fhandle_cache(char *path
, fserver
*fs
, am_nfs_handle_t
*fhbuf
, voidp wchan
)
247 fh_cache
*fp
, *fp_save
= 0;
249 int reuse_id
= FALSE
;
252 dlog("Searching cache for %s:%s", fs
->fs_host
, path
);
256 * First search the cache
258 ITER(fp
, fh_cache
, &fh_head
) {
259 if (fs
== fp
->fh_fs
&& STREQ(path
, fp
->fh_path
)) {
260 switch (fp
->fh_error
) {
262 plog(XLOG_INFO
, "prime_nfs_fhandle_cache: NFS version %d", (int) fp
->fh_nfs_version
);
264 if (fp
->fh_nfs_version
== NFS_VERSION3
)
265 error
= fp
->fh_error
= unx_error(fp
->fh_nfs_handle
.v3
.fhs_status
);
267 #endif /* HAVE_FS_NFS3 */
268 error
= fp
->fh_error
= unx_error(fp
->fh_nfs_handle
.v2
.fhs_status
);
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
));
276 #endif /* HAVE_FS_NFS3 */
277 memmove((voidp
) &(fhbuf
->v2
), (voidp
) &(fp
->fh_nfs_handle
.v2
),
278 sizeof(fp
->fh_nfs_handle
.v2
));
281 untimeout(fp
->fh_cid
);
282 fp
->fh_cid
= timeout(FH_TTL
, discard_fh
, (voidp
) fp
);
283 } else if (error
== EACCES
) {
285 * Now decode the file handle return code.
287 plog(XLOG_INFO
, "Filehandle denied for \"%s:%s\"",
290 errno
= error
; /* XXX */
291 plog(XLOG_INFO
, "Filehandle error for \"%s:%s\": %m",
296 * The error was returned from the remote mount daemon.
297 * Policy: this error will be cached for now...
303 * Still thinking about it, but we can re-use.
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.
320 error
= fp
->fh_error
;
334 * Re-use existing slot
336 untimeout(fp
->fh_cid
);
337 free_srvr(fp
->fh_fs
);
340 fp
= ALLOC(struct fh_cache
);
341 memset((voidp
) fp
, 0, sizeof(struct fh_cache
));
342 ins_que(&fp
->fh_q
, &fh_head
);
345 fp
->fh_id
= FHID_ALLOC(struct );
346 fp
->fh_wchan
= wchan
;
348 fp
->fh_cid
= timeout(FH_TTL
, discard_fh
, (voidp
) fp
);
351 * if fs->fs_ip is null, remote server is probably down.
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
;
362 * If the address has changed then don't try to re-use the
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
;
370 fp
->fh_fs
= dup_srvr(fs
);
371 fp
->fh_path
= strdup(path
);
373 error
= call_mountd(fp
, MOUNTPROC_MNT
, got_nfs_fh
, wchan
);
376 * Local error - cache for a short period
377 * just to prevent thrashing.
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
;
384 error
= fp
->fh_error
;
394 AUTH_CREATE_GIDLIST_TYPE group_wheel
= 0;
396 /* Some NFS mounts (particularly cross-domain) require FQDNs to succeed */
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
);
403 nfs_auth
= authsys_create_default();
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
);
410 nfs_auth
= authunix_create_default();
412 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
422 call_mountd(fh_cache
*fp
, u_long proc
, fwd_fun f
, voidp wchan
)
424 struct rpc_msg mnt_msg
;
431 error
= make_nfs_auth();
436 if (fp
->fh_sin
.sin_port
== 0) {
438 error
= nfs_srvr_port(fp
->fh_fs
, &port
, wchan
);
441 fp
->fh_sin
.sin_port
= port
;
444 /* find the right version of the mount protocol */
446 if (fp
->fh_nfs_version
== NFS_VERSION3
)
447 mnt_version
= MOUNTVERS3
;
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
);
454 rpc_msg_init(&mnt_msg
, MOUNTPROG
, mnt_version
, MOUNTPROC_NULL
);
455 len
= make_rpc_packet(iobuf
,
459 (voidp
) &fp
->fh_path
,
460 (XDRPROC_T_TYPE
) xdr_nfspath
,
464 error
= fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD
, fp
->fh_id
),
469 (voidp
) ((long) fp
->fh_id
), /* for 64-bit archs */
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
484 fp
->fh_sin
.sin_port
= 0;
491 * NFS needs the local filesystem, remote filesystem
493 * Local filesystem defaults to remote and vice-versa.
496 nfs_match(am_opts
*fo
)
500 if (fo
->opt_fs
&& !fo
->opt_rfs
)
501 fo
->opt_rfs
= fo
->opt_fs
;
503 plog(XLOG_USER
, "nfs: no remote filesystem specified");
506 if (!fo
->opt_rhost
) {
507 plog(XLOG_USER
, "nfs: no remote host specified");
512 * Determine magic cookie to put in mtab
514 xmtab
= (char *) xmalloc(strlen(fo
->opt_rhost
) + strlen(fo
->opt_rfs
) + 2);
515 sprintf(xmtab
, "%s:%s", fo
->opt_rhost
, fo
->opt_rfs
);
517 dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
518 fo
->opt_rhost
, fo
->opt_rfs
, fo
->opt_fs
);
526 * Initialize am structure for nfs
538 colon
= strchr(mf
->mf_info
, ':');
542 error
= prime_nfs_fhandle_cache(colon
+ 1, mf
->mf_server
, &fhs
, (voidp
) mf
);
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
));
553 mount_nfs_fh(am_nfs_handle_t
*fhp
, char *dir
, char *fs_name
, char *opts
, mntfs
*mf
)
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" */
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.
573 if (!(colon
= strchr(fs_name
, ':')))
575 #ifdef MOUNT_TABLE_ON_FILE
577 #endif /* MOUNT_TABLE_ON_FILE */
578 strncpy(host
, fs_name
, sizeof(host
));
579 #ifdef MOUNT_TABLE_ON_FILE
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 */
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
);
593 xopts
= strdup(opts
);
596 memset((voidp
) &mnt
, 0, sizeof(mnt
));
598 mnt
.mnt_fsname
= fs_name
;
599 mnt
.mnt_opts
= xopts
;
602 * Set mount types accordingly
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
;
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).
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) */
624 type
= MOUNT_TYPE_NFS
;
625 mnt
.mnt_type
= MNTTAB_TYPE_NFS
;
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) */
633 retry
= hasmntval(&mnt
, MNTTAB_OPT_RETRY
);
637 genflags
= compute_mount_flags(&mnt
);
639 /* setup the many fields and flags within nfs_args */
640 #ifdef HAVE_TRANSPORT_TYPE_TLI
641 compute_nfs_args(&nfs_args
,
644 NULL
, /* struct netconfig *nfsncp */
651 #else /* not HAVE_TRANSPORT_TYPE_TLI */
652 compute_nfs_args(&nfs_args
,
661 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
663 /* finally call the mounting function */
666 print_nfs_args(&nfs_args
, nfs_version
);
667 plog(XLOG_DEBUG
, "Generic mount flags 0x%x", genflags
);
670 error
= mount_fs(&mnt
, genflags
, (caddr_t
) &nfs_args
, retry
, type
,
671 nfs_version
, nfs_proto
, mnttab_file_name
);
674 #ifdef HAVE_TRANSPORT_TYPE_TLI
675 free_knetconfig(nfs_args
.knconf
);
677 XFREE(nfs_args
.addr
); /* allocated in compute_nfs_args() */
678 #endif /* HAVE_TRANSPORT_TYPE_TLI */
685 mount_nfs(char *dir
, char *fs_name
, char *opts
, mntfs
*mf
)
687 if (!mf
->mf_private
) {
688 plog(XLOG_ERROR
, "Missing filehandle for %s", fs_name
);
692 return mount_nfs_fh((am_nfs_handle_t
*) mf
->mf_private
, dir
, fs_name
, opts
, mf
);
697 nfs_fmount(mntfs
*mf
)
701 error
= mount_nfs(mf
->mf_mount
, mf
->mf_info
, mf
->mf_mopts
, mf
);
706 dlog("mount_nfs: %m");
715 nfs_fumount(mntfs
*mf
)
717 int error
= UMOUNT_FS(mf
->mf_mount
, mnttab_file_name
);
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
730 * Added by Mike Mitchell, mcm@unx.sas.com, 09/08/93
732 if (error
== EBUSY
) {
734 int len
= strlen(mf
->mf_mount
);
737 ITER(new_mf
, mntfs
, &mfhead
) {
738 if (new_mf
->mf_ops
!= mf
->mf_ops
||
739 new_mf
->mf_refc
> 1 ||
741 ((new_mf
->mf_flags
& (MFF_MOUNTED
| MFF_UNMOUNTING
| MFF_RESTART
)) == (MFF_MOUNTED
| MFF_RESTART
)))
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
);
751 error
= UMOUNT_FS(mf
->mf_mount
, mnttab_file_name
);
761 nfs_umounted(am_node
*mp
)
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...
768 mntfs
*mf
= mp
->am_mnt
;
772 if (mf
->mf_error
|| mf
->mf_refc
> 1)
778 * Call the mount daemon on the server to announce that we are not using
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...
785 colon
= path
= strchr(mf
->mf_info
, ':');
790 dlog("calling mountd for %s", mf
->mf_info
);
794 f
.fh_sin
= *fs
->fs_ip
;
795 f
.fh_sin
.sin_port
= (u_short
) 0;
796 f
.fh_nfs_version
= fs
->fs_version
;
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);