4 * Copyright (c) 1997-2009 Erez Zadok
5 * Copyright (c) 1990 Jan-Simon Pendry
6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
7 * Copyright (c) 1990 The Regents of the University of California.
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgment:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
42 * File: am-utils/amd/ops_nfs.c
52 #endif /* HAVE_CONFIG_H */
57 * Convert from nfsstat to UN*X error code
59 #define unx_error(e) ((int)(e))
62 * FH_TTL is the time a file handle will remain in the cache since
63 * last being used. If the file handle becomes invalid, then it
64 * will be flushed anyway.
66 #define FH_TTL (5 * 60) /* five minutes */
67 #define FH_TTL_ERROR (30) /* 30 seconds */
68 #define FHID_ALLOC() (++fh_id)
71 * The NFS layer maintains a cache of file handles.
72 * This is *fundamental* to the implementation and
73 * also allows quick remounting when a filesystem
74 * is accessed soon after timing out.
76 * The NFS server layer knows to flush this cache
77 * when a server goes down so avoiding stale handles.
79 * Each cache entry keeps a hard reference to
80 * the corresponding server. This ensures that
81 * the server keepalive information is maintained.
83 * The copy of the sockaddr_in here is taken so
84 * that the port can be twiddled to talk to mountd
85 * instead of portmap or the NFS server as used
87 * The port# is flushed if a server goes down.
88 * The IP address is never flushed - we assume
89 * that the address of a mounted machine never
90 * changes. If it does, then you have other
93 typedef struct fh_cache fh_cache
;
95 qelem fh_q
; /* List header */
96 wchan_t fh_wchan
; /* Wait channel */
97 int fh_error
; /* Valid data? */
98 int fh_id
; /* Unique id */
99 int fh_cid
; /* Callout id */
100 u_long fh_nfs_version
; /* highest NFS version on host */
101 am_nfs_handle_t fh_nfs_handle
; /* Handle on filesystem */
102 int fh_status
; /* Status of last rpc */
103 struct sockaddr_in fh_sin
; /* Address of mountd */
104 fserver
*fh_fs
; /* Server holding filesystem */
105 char *fh_path
; /* Filesystem on host */
108 /* forward definitions */
109 static int nfs_init(mntfs
*mf
);
110 static char *nfs_match(am_opts
*fo
);
111 static int nfs_mount(am_node
*am
, mntfs
*mf
);
112 static int nfs_umount(am_node
*am
, mntfs
*mf
);
113 static void nfs_umounted(mntfs
*mf
);
114 static int call_mountd(fh_cache
*fp
, u_long proc
, fwd_fun f
, wchan_t wchan
);
115 static int webnfs_lookup(fh_cache
*fp
, fwd_fun f
, wchan_t wchan
);
116 static int fh_id
= 0;
120 qelem fh_head
= {&fh_head
, &fh_head
};
123 * Network file system operations
132 amfs_error_lookup_child
,
133 amfs_error_mount_child
,
135 0, /* nfs_readlink */
139 0, /* nfs_get_wchan */
140 FS_MKMNT
| FS_BACKGROUND
| FS_AMQINFO
, /* nfs_fs_flags */
141 #ifdef HAVE_FS_AUTOFS
143 #endif /* HAVE_FS_AUTOFS */
148 find_nfs_fhandle_cache(opaque_t arg
, int done
)
150 fh_cache
*fp
, *fp2
= NULL
;
151 int id
= (long) arg
; /* for 64-bit archs */
153 ITER(fp
, fh_cache
, &fh_head
) {
154 if (fp
->fh_id
== id
) {
161 dlog("fh cache gives fp %#lx, fs %s", (unsigned long) fp2
, fp2
->fh_path
);
163 dlog("fh cache search failed");
167 fp2
->fh_error
= ETIMEDOUT
;
176 * Called when a filehandle appears via the mount protocol
179 got_nfs_fh_mount(voidp pkt
, int len
, struct sockaddr_in
*sa
, struct sockaddr_in
*ia
, opaque_t arg
, int done
)
184 struct am_mountres3 res3
;
185 #endif /* HAVE_FS_NFS3 */
187 fp
= find_nfs_fhandle_cache(arg
, done
);
192 * retrieve the correct RPC reply for the file handle, based on the
193 * NFS protocol version.
196 if (fp
->fh_nfs_version
== NFS_VERSION3
) {
197 memset(&res3
, 0, sizeof(res3
));
198 fp
->fh_error
= pickup_rpc_reply(pkt
, len
, (voidp
) &res3
,
199 (XDRPROC_T_TYPE
) xdr_am_mountres3
);
200 fp
->fh_status
= unx_error(res3
.fhs_status
);
201 memset(&fp
->fh_nfs_handle
.v3
, 0, sizeof(am_nfs_fh3
));
202 fp
->fh_nfs_handle
.v3
.am_fh3_length
= res3
.mountres3_u
.mountinfo
.fhandle
.fhandle3_len
;
203 memmove(fp
->fh_nfs_handle
.v3
.am_fh3_data
,
204 res3
.mountres3_u
.mountinfo
.fhandle
.fhandle3_val
,
205 fp
->fh_nfs_handle
.v3
.am_fh3_length
);
207 #endif /* HAVE_FS_NFS3 */
208 memset(&res
, 0, sizeof(res
));
209 fp
->fh_error
= pickup_rpc_reply(pkt
, len
, (voidp
) &res
,
210 (XDRPROC_T_TYPE
) xdr_fhstatus
);
211 fp
->fh_status
= unx_error(res
.fhs_status
);
212 memmove(&fp
->fh_nfs_handle
.v2
, &res
.fhs_fh
, NFS_FHSIZE
);
215 #endif /* HAVE_FS_NFS3 */
218 dlog("got filehandle for %s:%s", fp
->fh_fs
->fs_host
, fp
->fh_path
);
220 plog(XLOG_USER
, "filehandle denied for %s:%s", fp
->fh_fs
->fs_host
, fp
->fh_path
);
222 * Force the error to be EACCES. It's debatable whether it should be
223 * ENOENT instead, but the server really doesn't give us any clues, and
224 * EACCES is more in line with the "filehandle denied" message.
226 fp
->fh_error
= EACCES
;
230 * Wakeup anything sleeping on this filehandle
233 dlog("Calling wakeup on %#lx", (unsigned long) fp
->fh_wchan
);
234 wakeup(fp
->fh_wchan
);
240 * Called when a filehandle appears via WebNFS
243 got_nfs_fh_webnfs(voidp pkt
, int len
, struct sockaddr_in
*sa
, struct sockaddr_in
*ia
, opaque_t arg
, int done
)
249 #endif /* HAVE_FS_NFS3 */
251 fp
= find_nfs_fhandle_cache(arg
, done
);
256 * retrieve the correct RPC reply for the file handle, based on the
257 * NFS protocol version.
260 if (fp
->fh_nfs_version
== NFS_VERSION3
) {
261 memset(&res3
, 0, sizeof(res3
));
262 fp
->fh_error
= pickup_rpc_reply(pkt
, len
, (voidp
) &res3
,
263 (XDRPROC_T_TYPE
) xdr_am_LOOKUP3res
);
264 fp
->fh_status
= unx_error(res3
.status
);
265 memset(&fp
->fh_nfs_handle
.v3
, 0, sizeof(am_nfs_fh3
));
266 fp
->fh_nfs_handle
.v3
.am_fh3_length
= res3
.res_u
.ok
.object
.am_fh3_length
;
267 memmove(fp
->fh_nfs_handle
.v3
.am_fh3_data
,
268 res3
.res_u
.ok
.object
.am_fh3_data
,
269 fp
->fh_nfs_handle
.v3
.am_fh3_length
);
271 #endif /* HAVE_FS_NFS3 */
272 memset(&res
, 0, sizeof(res
));
273 fp
->fh_error
= pickup_rpc_reply(pkt
, len
, (voidp
) &res
,
274 (XDRPROC_T_TYPE
) xdr_diropres
);
275 fp
->fh_status
= unx_error(res
.dr_status
);
276 memmove(&fp
->fh_nfs_handle
.v2
, &res
.dr_u
.dr_drok_u
.drok_fhandle
, NFS_FHSIZE
);
279 #endif /* HAVE_FS_NFS3 */
282 dlog("got filehandle for %s:%s", fp
->fh_fs
->fs_host
, fp
->fh_path
);
284 plog(XLOG_USER
, "filehandle denied for %s:%s", fp
->fh_fs
->fs_host
, fp
->fh_path
);
286 * Force the error to be EACCES. It's debatable whether it should be
287 * ENOENT instead, but the server really doesn't give us any clues, and
288 * EACCES is more in line with the "filehandle denied" message.
290 fp
->fh_error
= EACCES
;
294 * Wakeup anything sleeping on this filehandle
297 dlog("Calling wakeup on %#lx", (unsigned long) fp
->fh_wchan
);
298 wakeup(fp
->fh_wchan
);
304 flush_nfs_fhandle_cache(fserver
*fs
)
308 ITER(fp
, fh_cache
, &fh_head
) {
309 if (fp
->fh_fs
== fs
|| fs
== NULL
) {
311 * Only invalidate port info for non-WebNFS servers
313 if (!(fp
->fh_fs
->fs_flags
& FSF_WEBNFS
))
314 fp
->fh_sin
.sin_port
= (u_short
) 0;
322 discard_fh(opaque_t arg
)
324 fh_cache
*fp
= (fh_cache
*) arg
;
328 dlog("Discarding filehandle for %s:%s", fp
->fh_fs
->fs_host
, fp
->fh_path
);
329 free_srvr(fp
->fh_fs
);
338 * Determine the file handle for a node
341 prime_nfs_fhandle_cache(char *path
, fserver
*fs
, am_nfs_handle_t
*fhbuf
, mntfs
*mf
)
343 fh_cache
*fp
, *fp_save
= NULL
;
345 int reuse_id
= FALSE
;
347 dlog("Searching cache for %s:%s", fs
->fs_host
, path
);
350 * First search the cache
352 ITER(fp
, fh_cache
, &fh_head
) {
353 if (fs
!= fp
->fh_fs
|| !STREQ(path
, fp
->fh_path
))
354 continue; /* skip to next ITER item */
355 /* else we got a match */
356 switch (fp
->fh_error
) {
358 plog(XLOG_INFO
, "prime_nfs_fhandle_cache: NFS version %d", (int) fp
->fh_nfs_version
);
360 error
= fp
->fh_error
= fp
->fh_status
;
363 if (mf
->mf_flags
& MFF_NFS_SCALEDOWN
) {
365 /* XXX: why reuse the ID? */
372 if (fp
->fh_nfs_version
== NFS_VERSION3
) {
373 memmove((voidp
) &(fhbuf
->v3
), (voidp
) &(fp
->fh_nfs_handle
.v3
),
374 sizeof(fp
->fh_nfs_handle
.v3
));
376 #endif /* HAVE_FS_NFS3 */
378 memmove((voidp
) &(fhbuf
->v2
), (voidp
) &(fp
->fh_nfs_handle
.v2
),
379 sizeof(fp
->fh_nfs_handle
.v2
));
383 untimeout(fp
->fh_cid
);
384 fp
->fh_cid
= timeout(FH_TTL
, discard_fh
, (opaque_t
) fp
);
385 } else if (error
== EACCES
) {
387 * Now decode the file handle return code.
389 plog(XLOG_INFO
, "Filehandle denied for \"%s:%s\"",
392 errno
= error
; /* XXX */
393 plog(XLOG_INFO
, "Filehandle error for \"%s:%s\": %m",
398 * The error was returned from the remote mount daemon.
399 * Policy: this error will be cached for now...
405 * Still thinking about it, but we can re-use.
414 * Policy: make sure we recompute if required again
415 * in case this was caused by a network failure.
416 * This can thrash mountd's though... If you find
417 * your mountd going slowly then:
418 * 1. Add a fork() loop to main.
419 * 2. Remove the call to innetgr() and don't use
420 * netgroups, especially if you don't use YP.
422 error
= fp
->fh_error
;
425 } /* end of switch statement */
426 } /* end of ITER loop */
434 * Re-use existing slot
436 untimeout(fp
->fh_cid
);
437 free_srvr(fp
->fh_fs
);
440 fp
= ALLOC(struct fh_cache
);
441 memset((voidp
) fp
, 0, sizeof(struct fh_cache
));
442 ins_que(&fp
->fh_q
, &fh_head
);
445 fp
->fh_id
= FHID_ALLOC();
446 fp
->fh_wchan
= get_mntfs_wchan(mf
);
448 fp
->fh_cid
= timeout(FH_TTL
, discard_fh
, (opaque_t
) fp
);
451 * If fs->fs_ip is null, remote server is probably down.
454 /* Mark the fileserver down and invalid again */
455 fs
->fs_flags
&= ~FSF_VALID
;
456 fs
->fs_flags
|= FSF_DOWN
;
457 error
= AM_ERRNO_HOST_DOWN
;
462 * Either fp has been freshly allocated or the address has changed.
463 * Initialize address and nfs version. Don't try to re-use the port
464 * information unless using WebNFS where the port is fixed either by
465 * the spec or the "port" mount option.
467 if (fp
->fh_sin
.sin_addr
.s_addr
!= fs
->fs_ip
->sin_addr
.s_addr
) {
468 fp
->fh_sin
= *fs
->fs_ip
;
469 if (!(mf
->mf_flags
& MFF_WEBNFS
))
470 fp
->fh_sin
.sin_port
= 0;
471 fp
->fh_nfs_version
= fs
->fs_version
;
474 fp
->fh_fs
= dup_srvr(fs
);
475 fp
->fh_path
= strdup(path
);
477 if (mf
->mf_flags
& MFF_WEBNFS
)
478 error
= webnfs_lookup(fp
, got_nfs_fh_webnfs
, get_mntfs_wchan(mf
));
480 error
= call_mountd(fp
, MOUNTPROC_MNT
, got_nfs_fh_mount
, get_mntfs_wchan(mf
));
483 * Local error - cache for a short period
484 * just to prevent thrashing.
486 untimeout(fp
->fh_cid
);
487 fp
->fh_cid
= timeout(error
< 0 ? 2 * ALLOWED_MOUNT_TIME
: FH_TTL_ERROR
,
488 discard_fh
, (opaque_t
) fp
);
489 fp
->fh_error
= error
;
491 error
= fp
->fh_error
;
501 AUTH_CREATE_GIDLIST_TYPE group_wheel
= 0;
503 /* Some NFS mounts (particularly cross-domain) require FQDNs to succeed */
505 #ifdef HAVE_TRANSPORT_TYPE_TLI
506 if (gopt
.flags
& CFM_FULLY_QUALIFIED_HOSTS
) {
507 plog(XLOG_INFO
, "Using NFS auth for FQHN \"%s\"", hostd
);
508 nfs_auth
= authsys_create(hostd
, 0, 0, 1, &group_wheel
);
510 nfs_auth
= authsys_create_default();
512 #else /* not HAVE_TRANSPORT_TYPE_TLI */
513 if (gopt
.flags
& CFM_FULLY_QUALIFIED_HOSTS
) {
514 plog(XLOG_INFO
, "Using NFS auth for FQHN \"%s\"", hostd
);
515 nfs_auth
= authunix_create(hostd
, 0, 0, 1, &group_wheel
);
517 nfs_auth
= authunix_create_default();
519 #endif /* not HAVE_TRANSPORT_TYPE_TLI */
529 call_mountd(fh_cache
*fp
, u_long proc
, fwd_fun fun
, wchan_t wchan
)
531 struct rpc_msg mnt_msg
;
533 char iobuf
[UDPMSGSIZE
];
538 error
= make_nfs_auth();
543 if (fp
->fh_sin
.sin_port
== 0) {
545 error
= get_mountd_port(fp
->fh_fs
, &mountd_port
, wchan
);
548 fp
->fh_sin
.sin_port
= mountd_port
;
551 /* find the right version of the mount protocol */
553 if (fp
->fh_nfs_version
== NFS_VERSION3
)
554 mnt_version
= AM_MOUNTVERS3
;
556 #endif /* HAVE_FS_NFS3 */
557 mnt_version
= MOUNTVERS
;
558 plog(XLOG_INFO
, "call_mountd: NFS version %d, mount version %d",
559 (int) fp
->fh_nfs_version
, (int) mnt_version
);
561 rpc_msg_init(&mnt_msg
, MOUNTPROG
, mnt_version
, MOUNTPROC_NULL
);
562 len
= make_rpc_packet(iobuf
,
566 (voidp
) &fp
->fh_path
,
567 (XDRPROC_T_TYPE
) xdr_nfspath
,
571 error
= fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD
, fp
->fh_id
),
576 (opaque_t
) ((long) fp
->fh_id
), /* cast to long needed for 64-bit archs */
583 * It may be the case that we're sending to the wrong MOUNTD port. This
584 * occurs if mountd is restarted on the server after the port has been
585 * looked up and stored in the filehandle cache somewhere. The correct
586 * solution, if we're going to cache port numbers is to catch the ICMP
587 * port unreachable reply from the server and cause the portmap request
588 * to be redone. The quick solution here is to invalidate the MOUNTD
591 fp
->fh_sin
.sin_port
= 0;
598 webnfs_lookup(fh_cache
*fp
, fwd_fun fun
, wchan_t wchan
)
600 struct rpc_msg wnfs_msg
;
602 char iobuf
[UDPMSGSIZE
];
605 XDRPROC_T_TYPE xdr_fn
;
609 am_LOOKUP3args args3
;
610 #endif /* HAVE_FS_NFS3 */
615 error
= make_nfs_auth();
620 if (fp
->fh_sin
.sin_port
== 0) {
621 /* FIXME: wrong, don't discard sin_port in the first place for WebNFS. */
622 plog(XLOG_WARNING
, "webnfs_lookup: port == 0 for nfs on %s, fixed",
624 fp
->fh_sin
.sin_port
= htons(NFS_PORT
);
628 * Use native path like the rest of amd (cf. RFC 2054, 6.1).
630 l
= strlen(fp
->fh_path
) + 2;
631 wnfs_path
= (char *) xmalloc(l
);
633 xstrlcpy(wnfs_path
+ 1, fp
->fh_path
, l
- 1);
635 /* find the right program and lookup procedure */
637 if (fp
->fh_nfs_version
== NFS_VERSION3
) {
638 proc
= AM_NFSPROC3_LOOKUP
;
639 xdr_fn
= (XDRPROC_T_TYPE
) xdr_am_LOOKUP3args
;
641 /* WebNFS public file handle */
642 args3
.what
.dir
.am_fh3_length
= 0;
643 args3
.what
.name
= wnfs_path
;
645 #endif /* HAVE_FS_NFS3 */
646 proc
= NFSPROC_LOOKUP
;
647 xdr_fn
= (XDRPROC_T_TYPE
) xdr_diropargs
;
649 /* WebNFS public file handle */
650 memset(&args
.da_fhandle
, 0, NFS_FHSIZE
);
651 args
.da_name
= wnfs_path
;
654 #endif /* HAVE_FS_NFS3 */
656 plog(XLOG_INFO
, "webnfs_lookup: NFS version %d", (int) fp
->fh_nfs_version
);
658 rpc_msg_init(&wnfs_msg
, NFS_PROGRAM
, fp
->fh_nfs_version
, proc
);
659 len
= make_rpc_packet(iobuf
,
664 (XDRPROC_T_TYPE
) xdr_fn
,
668 error
= fwd_packet(MK_RPC_XID(RPC_XID_WEBNFS
, fp
->fh_id
),
673 (opaque_t
) ((long) fp
->fh_id
), /* cast to long needed for 64-bit archs */
685 * NFS needs the local filesystem, remote filesystem
687 * Local filesystem defaults to remote and vice-versa.
690 nfs_match(am_opts
*fo
)
695 if (fo
->opt_fs
&& !fo
->opt_rfs
)
696 fo
->opt_rfs
= fo
->opt_fs
;
698 plog(XLOG_USER
, "nfs: no remote filesystem specified");
701 if (!fo
->opt_rhost
) {
702 plog(XLOG_USER
, "nfs: no remote host specified");
707 * Determine magic cookie to put in mtab
709 l
= strlen(fo
->opt_rhost
) + strlen(fo
->opt_rfs
) + 2;
710 xmtab
= (char *) xmalloc(l
);
711 xsnprintf(xmtab
, l
, "%s:%s", fo
->opt_rhost
, fo
->opt_rfs
);
712 dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
713 fo
->opt_rhost
, fo
->opt_rfs
, fo
->opt_fs
);
720 * Initialize am structure for nfs
729 if (mf
->mf_private
) {
730 if (mf
->mf_flags
& MFF_NFS_SCALEDOWN
) {
733 /* tell remote mountd that we're done with this filehandle */
734 mf
->mf_ops
->umounted(mf
);
736 mf
->mf_prfree(mf
->mf_private
);
737 fs
= mf
->mf_ops
->ffserver(mf
);
738 free_srvr(mf
->mf_server
);
744 colon
= strchr(mf
->mf_info
, ':');
748 error
= prime_nfs_fhandle_cache(colon
+ 1, mf
->mf_server
, &fhs
, mf
);
750 mf
->mf_private
= (opaque_t
) ALLOC(am_nfs_handle_t
);
751 mf
->mf_prfree
= (void (*)(opaque_t
)) free
;
752 memmove(mf
->mf_private
, (voidp
) &fhs
, sizeof(fhs
));
759 mount_nfs_fh(am_nfs_handle_t
*fhp
, char *mntdir
, char *fs_name
, mntfs
*mf
)
763 char *xopts
=NULL
, transp_timeo_opts
[40], transp_retrans_opts
[40];
764 char host
[MAXHOSTNAMELEN
+ MAXPATHLEN
+ 2];
765 fserver
*fs
= mf
->mf_server
;
766 u_long nfs_version
= fs
->fs_version
;
767 char *nfs_proto
= fs
->fs_proto
; /* "tcp" or "udp" */
768 int on_autofs
= mf
->mf_flags
& MFF_ON_AUTOFS
;
772 int proto
= AMU_TYPE_NONE
;
777 * Extract HOST name to give to kernel.
778 * Some systems like osf1/aix3/bsd44 variants may need old code
779 * for NFS_ARGS_NEEDS_PATH.
781 if (!(colon
= strchr(fs_name
, ':')))
783 #ifdef MOUNT_TABLE_ON_FILE
785 #endif /* MOUNT_TABLE_ON_FILE */
786 xstrlcpy(host
, fs_name
, sizeof(host
));
787 #ifdef MOUNT_TABLE_ON_FILE
789 #endif /* MOUNT_TABLE_ON_FILE */
790 #ifdef MAXHOSTNAMELEN
791 /* most kernels have a name length restriction */
792 if (strlen(host
) >= MAXHOSTNAMELEN
)
793 xstrlcpy(host
+ MAXHOSTNAMELEN
- 3, "..",
794 sizeof(host
) - MAXHOSTNAMELEN
+ 3);
795 #endif /* MAXHOSTNAMELEN */
798 * Create option=VAL for udp/tcp specific timeouts and retrans values, but
799 * only if these options were specified.
802 transp_timeo_opts
[0] = transp_retrans_opts
[0] = '\0'; /* initialize */
803 if (STREQ(nfs_proto
, "udp"))
804 proto
= AMU_TYPE_UDP
;
805 else if (STREQ(nfs_proto
, "tcp"))
806 proto
= AMU_TYPE_TCP
;
807 if (proto
!= AMU_TYPE_NONE
) {
808 if (gopt
.amfs_auto_timeo
[proto
] > 0)
809 xsnprintf(transp_timeo_opts
, sizeof(transp_timeo_opts
), "%s=%d,",
810 MNTTAB_OPT_TIMEO
, gopt
.amfs_auto_timeo
[proto
]);
811 if (gopt
.amfs_auto_retrans
[proto
] > 0)
812 xsnprintf(transp_retrans_opts
, sizeof(transp_retrans_opts
), "%s=%d,",
813 MNTTAB_OPT_RETRANS
, gopt
.amfs_auto_retrans
[proto
]);
816 if (mf
->mf_remopts
&& *mf
->mf_remopts
&&
817 !islocalnet(fs
->fs_ip
->sin_addr
.s_addr
)) {
818 plog(XLOG_INFO
, "Using remopts=\"%s\"", mf
->mf_remopts
);
819 /* use transp_opts first, so map-specific opts will override */
820 xopts
= str3cat(xopts
, transp_timeo_opts
, transp_retrans_opts
, mf
->mf_remopts
);
822 /* use transp_opts first, so map-specific opts will override */
823 xopts
= str3cat(xopts
, transp_timeo_opts
, transp_retrans_opts
, mf
->mf_mopts
);
826 memset((voidp
) &mnt
, 0, sizeof(mnt
));
827 mnt
.mnt_dir
= mntdir
;
828 mnt
.mnt_fsname
= fs_name
;
829 mnt
.mnt_opts
= xopts
;
832 * Set mount types accordingly
835 type
= MOUNT_TYPE_NFS
;
836 mnt
.mnt_type
= MNTTAB_TYPE_NFS
;
837 #else /* HAVE_FS_NFS3 */
838 if (nfs_version
== NFS_VERSION3
) {
839 type
= MOUNT_TYPE_NFS3
;
841 * Systems that include the mount table "vers" option generally do not
842 * set the mnttab entry to "nfs3", but to "nfs" and then they set
843 * "vers=3". Setting it to "nfs3" works, but it may break some things
844 * like "df -t nfs" and the "quota" program (esp. on Solaris and Irix).
845 * So on those systems, set it to "nfs".
846 * Note: MNTTAB_OPT_VERS is always set for NFS3 (see am_compat.h).
848 # if defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE)
849 mnt
.mnt_type
= MNTTAB_TYPE_NFS
;
850 # else /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */
851 mnt
.mnt_type
= MNTTAB_TYPE_NFS3
;
852 # endif /* defined(MNTTAB_OPT_VERS) && defined(MOUNT_TABLE_ON_FILE) */
854 type
= MOUNT_TYPE_NFS
;
855 mnt
.mnt_type
= MNTTAB_TYPE_NFS
;
857 #endif /* HAVE_FS_NFS3 */
858 plog(XLOG_INFO
, "mount_nfs_fh: NFS version %d", (int) nfs_version
);
859 plog(XLOG_INFO
, "mount_nfs_fh: using NFS transport %s", nfs_proto
);
861 retry
= hasmntval(&mnt
, MNTTAB_OPT_RETRY
);
865 genflags
= compute_mount_flags(&mnt
);
866 #ifdef HAVE_FS_AUTOFS
868 genflags
|= autofs_compute_mount_flags(&mnt
);
869 #endif /* HAVE_FS_AUTOFS */
871 /* setup the many fields and flags within nfs_args */
872 compute_nfs_args(&nfs_args
,
875 NULL
, /* struct netconfig *nfsncp */
883 /* finally call the mounting function */
884 if (amuDebug(D_TRACE
)) {
885 print_nfs_args(&nfs_args
, nfs_version
);
886 plog(XLOG_DEBUG
, "Generic mount flags 0x%x used for NFS mount", genflags
);
888 error
= mount_fs(&mnt
, genflags
, (caddr_t
) &nfs_args
, retry
, type
,
889 nfs_version
, nfs_proto
, mnttab_file_name
, on_autofs
);
892 #ifdef HAVE_TRANSPORT_TYPE_TLI
893 free_knetconfig(nfs_args
.knconf
);
895 XFREE(nfs_args
.addr
); /* allocated in compute_nfs_args() */
896 #endif /* HAVE_TRANSPORT_TYPE_TLI */
903 nfs_mount(am_node
*am
, mntfs
*mf
)
908 if (!mf
->mf_private
) {
909 plog(XLOG_ERROR
, "Missing filehandle for %s", mf
->mf_info
);
913 mnt
.mnt_opts
= mf
->mf_mopts
;
914 if (amu_hasmntopt(&mnt
, "softlookup") ||
915 (amu_hasmntopt(&mnt
, "soft") && !amu_hasmntopt(&mnt
, "nosoftlookup")))
916 am
->am_flags
|= AMF_SOFTLOOKUP
;
918 error
= mount_nfs_fh((am_nfs_handle_t
*) mf
->mf_private
,
925 dlog("mount_nfs: %m");
933 nfs_umount(am_node
*am
, mntfs
*mf
)
935 int unmount_flags
, new_unmount_flags
, error
;
937 unmount_flags
= (mf
->mf_flags
& MFF_ON_AUTOFS
) ? AMU_UMOUNT_AUTOFS
: 0;
938 error
= UMOUNT_FS(mf
->mf_mount
, mnttab_file_name
, unmount_flags
);
940 #if defined(HAVE_UMOUNT2) && (defined(MNT2_GEN_OPT_FORCE) || defined(MNT2_GEN_OPT_DETACH))
942 * If the attempt to unmount failed with EBUSY, and this fserver was
943 * marked for forced unmounts, then use forced/lazy unmounts.
945 if (error
== EBUSY
&&
946 gopt
.flags
& CFM_FORCED_UNMOUNTS
&&
947 mf
->mf_server
->fs_flags
& FSF_FORCE_UNMOUNT
) {
948 plog(XLOG_INFO
, "EZK: nfs_umount: trying forced/lazy unmounts");
950 * XXX: turning off the FSF_FORCE_UNMOUNT may not be perfectly
951 * incorrect. Multiple nodes may need to be timed out and restarted for
952 * a single hung fserver.
954 mf
->mf_server
->fs_flags
&= ~FSF_FORCE_UNMOUNT
;
955 new_unmount_flags
= unmount_flags
| AMU_UMOUNT_FORCE
| AMU_UMOUNT_DETACH
;
956 error
= UMOUNT_FS(mf
->mf_mount
, mnttab_file_name
, new_unmount_flags
);
958 #endif /* HAVE_UMOUNT2 && (MNT2_GEN_OPT_FORCE || MNT2_GEN_OPT_DETACH) */
961 * Here is some code to unmount 'restarted' file systems.
962 * The restarted file systems are marked as 'nfs', not
963 * 'host', so we only have the map information for the
964 * the top-level mount. The unmount will fail (EBUSY)
965 * if there are anything else from the NFS server mounted
966 * below the mount-point. This code checks to see if there
967 * is anything mounted with the same prefix as the
968 * file system to be unmounted ("/a/b/c" when unmounting "/a/b").
969 * If there is, and it is a 'restarted' file system, we unmount
971 * Added by Mike Mitchell, mcm@unx.sas.com, 09/08/93
973 if (error
== EBUSY
) {
975 int len
= strlen(mf
->mf_mount
);
978 ITER(new_mf
, mntfs
, &mfhead
) {
979 if (new_mf
->mf_ops
!= mf
->mf_ops
||
980 new_mf
->mf_refc
> 1 ||
982 ((new_mf
->mf_flags
& (MFF_MOUNTED
| MFF_UNMOUNTING
| MFF_RESTART
)) == (MFF_MOUNTED
| MFF_RESTART
)))
985 if (NSTREQ(mf
->mf_mount
, new_mf
->mf_mount
, len
) &&
986 new_mf
->mf_mount
[len
] == '/') {
988 (new_mf
->mf_flags
& MFF_ON_AUTOFS
) ? AMU_UMOUNT_AUTOFS
: 0;
989 UMOUNT_FS(new_mf
->mf_mount
, mnttab_file_name
, new_unmount_flags
);
994 error
= UMOUNT_FS(mf
->mf_mount
, mnttab_file_name
, unmount_flags
);
1004 nfs_umounted(mntfs
*mf
)
1009 if (mf
->mf_error
|| mf
->mf_refc
> 1)
1013 * No need to inform mountd when WebNFS is in use.
1015 if (mf
->mf_flags
& MFF_WEBNFS
)
1019 * Call the mount daemon on the server to announce that we are not using
1022 * XXX: This is *wrong*. The mountd should be called when the fhandle is
1023 * flushed from the cache, and a reference held to the cached entry while
1024 * the fs is mounted...
1027 colon
= path
= strchr(mf
->mf_info
, ':');
1031 dlog("calling mountd for %s", mf
->mf_info
);
1034 f
.fh_sin
= *fs
->fs_ip
;
1035 f
.fh_sin
.sin_port
= (u_short
) 0;
1036 f
.fh_nfs_version
= fs
->fs_version
;
1040 prime_nfs_fhandle_cache(colon
+ 1, mf
->mf_server
, (am_nfs_handle_t
*) NULL
, mf
);
1041 call_mountd(&f
, MOUNTPROC_UMNT
, (fwd_fun
*) NULL
, (wchan_t
) NULL
);