* $Id: nfs_ops.c,v 5.2 90/06/23 22:19:45 jsp Rel $
* Copyright (c) 1990 Jan-Simon Pendry
* Copyright (c) 1990 Imperial College of Science, Technology & Medicine
* Copyright (c) 1990 The Regents of the University of California.
* This code is derived from software contributed to Berkeley by
* Jan-Simon Pendry at Imperial College, London.
* %sccs.include.redist.c%
* @(#)nfs_ops.c 5.1 (Berkeley) %G%
typedef nfs_fh fhandle_t
;
* Convert from nfsstat to UN*X error code
#define unx_error(e) ((int)(e))
* The NFS layer maintains a cache of file handles.
* This is *fundamental* to the implementation and
* also allows quick remounting when a filesystem
* is accessed soon after timing out.
* The NFS server layer knows to flush this cache
* when a server goes down so avoiding stale handles.
* Each cache entry keeps a hard reference to
* the corresponding server. This ensures that
* the server keepalive information is maintained.
* The copy of the sockaddr_in here is taken so
* that the port can be twiddled to talk to mountd
* instead of portmap or the NFS server as used
* The port# is flushed if a server goes down.
* The IP address is never flushed - we assume
* that the address of a mounted machine never
* changes. If it does, then you have other
typedef struct fh_cache fh_cache
;
qelem fh_q
; /* List header */
voidp fh_wchan
; /* Wait channel */
int fh_error
; /* Valid data? */
int fh_id
; /* Unique id */
int fh_cid
; /* Callout id */
struct fhstatus fh_handle
; /* Handle on filesystem */
struct sockaddr_in fh_sin
; /* Address of mountd */
fserver
*fh_fs
; /* Server holding filesystem */
char *fh_path
; /* Filesystem on host */
* FH_TTL is the time a file handle will remain in the cache since
* last being used. If the file handle becomes invalid, then it
* will be flushed anyway.
#define FH_TTL (5 * 60) /* five minutes */
#define FH_TTL_ERROR (30) /* 30 seconds */
#define FHID_ALLOC() (++fh_id)
qelem fh_head
= { &fh_head
, &fh_head
};
static int call_mountd
P((fh_cache
*, unsigned long, fwd_fun
, voidp
));
static fh_cache
*find_nfs_fhandle_cache
P((voidp idv
, int done
));
static fh_cache
*find_nfs_fhandle_cache(idv
, done
)
ITER(fp
, fh_cache
, &fh_head
) {
dlog("fh cache gives fp %#x, fs %s", fp2
, fp2
->fh_path
);
dlog("fh cache search failed");
fp2
->fh_error
= ETIMEDOUT
;
* Called when a filehandle appears
static void got_nfs_fh
P((voidp pkt
, int len
, struct sockaddr_in
*sa
,
struct sockaddr_in
*ia
, voidp idv
, int done
));
static void got_nfs_fh(pkt
, len
, sa
, ia
, idv
, done
)
struct sockaddr_in
*sa
, *ia
;
fh_cache
*fp
= find_nfs_fhandle_cache(idv
, done
);
fp
->fh_error
= pickup_rpc_reply(pkt
, len
, (voidp
) &fp
->fh_handle
, xdr_fhstatus
);
dlog("got filehandle for %s:%s", fp
->fh_fs
->fs_host
, fp
->fh_path
);
* Wakeup anything sleeping on this filehandle
dlog("Calling wakeup on %#x", fp
->fh_wchan
);
void flush_nfs_fhandle_cache
P((fserver
*fs
));
void flush_nfs_fhandle_cache(fs
)
ITER(fp
, fh_cache
, &fh_head
) {
fp
->fh_sin
.sin_port
= (u_short
) 0;
static void discard_fh
P((fh_cache
*fp
));
static void discard_fh(fp
)
dlog("Discarding filehandle for %s:%s", fp
->fh_fs
->fs_host
, fp
->fh_path
);
free((voidp
) fp
->fh_path
);
* Determine the file handle for a node
static int prime_nfs_fhandle_cache
P((char *path
, fserver
*fs
, struct fhstatus
*fhbuf
, voidp wchan
));
static int prime_nfs_fhandle_cache(path
, fs
, fhbuf
, wchan
)
fh_cache
*fp
, *fp_save
= 0;
dlog("Searching cache for %s:%s", fs
->fs_host
, path
);
ITER(fp
, fh_cache
, &fh_head
) {
if (fs
== fp
->fh_fs
&& strcmp(path
, fp
->fh_path
) == 0) {
error
= fp
->fh_error
= unx_error(fp
->fh_handle
.fhs_status
);
bcopy((voidp
) &fp
->fh_handle
, (voidp
) fhbuf
,
fp
->fh_cid
= timeout(FH_TTL
, discard_fh
, (voidp
) fp
);
} else if (error
== EACCES
) {
* Now decode the file handle return code.
plog(XLOG_INFO
, "Filehandle denied for \"%s:%s\"",
plog(XLOG_INFO
, "Filehandle error for \"%s:%s\": %m",
* The error was returned from the remote mount daemon.
* Policy: this error will be cached for now...
* Still thinking about it, but we can re-use.
* Policy: make sure we recompute if required again
* in case this was caused by a network failure.
* This can thrash mountd's though... If you find
* your mountd going slowly then:
* 1. Add a fork() loop to main.
* 2. Remove the call to innetgr() and don't use
* netgroups, especially if you don't use YP.
bzero((voidp
) fp
, sizeof(*fp
));
ins_que(&fp
->fh_q
, &fh_head
);
fp
->fh_id
= FHID_ALLOC();
fp
->fh_cid
= timeout(FH_TTL
, discard_fh
, (voidp
) fp
);
* If the address has changed then don't try to re-use the
if (fp
->fh_sin
.sin_addr
.s_addr
!= fs
->fs_ip
->sin_addr
.s_addr
) {
fp
->fh_fs
= dup_srvr(fs
);
fp
->fh_path
= strdup(path
);
error
= call_mountd(fp
, MOUNTPROC_MNT
, got_nfs_fh
, wchan
);
* Local error - cache for a short period
* just to prevent thrashing.
fp
->fh_cid
= timeout(error
< 0 ? 2 * ALLOWED_MOUNT_TIME
: FH_TTL_ERROR
,
static int call_mountd
P((fh_cache
*fp
, u_long proc
, fwd_fun f
, voidp wchan
));
static int call_mountd(fp
, proc
, f
, wchan
)
nfs_auth
= authunix_create_default();
if (fp
->fh_sin
.sin_port
== 0) {
error
= nfs_srvr_port(fp
->fh_fs
, &port
, wchan
);
fp
->fh_sin
.sin_port
= port
;
rpc_msg_init(&mnt_msg
, MOUNTPROG
, MOUNTVERS
, (unsigned long) 0);
len
= make_rpc_packet(iobuf
, sizeof(iobuf
), proc
,
&mnt_msg
, (voidp
) &fp
->fh_path
, xdr_nfspath
, nfs_auth
);
error
= fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD
, fp
->fh_id
),
(voidp
) iobuf
, len
, &fp
->fh_sin
, &fp
->fh_sin
, (voidp
) fp
->fh_id
, f
);
/*-------------------------------------------------------------------------*/
* NFS needs the local filesystem, remote filesystem
* Local filesystem defaults to remote and vice-versa.
if (fo
->opt_fs
&& !fo
->opt_rfs
)
fo
->opt_rfs
= fo
->opt_fs
;
plog(XLOG_USER
, "nfs: no remote filesystem specified");
plog(XLOG_USER
, "nfs: no remote host specified");
* Determine magic cookie to put in mtab
fo
->fs_mtab
= (char *) xrealloc(fo
->fs_mtab
, strlen(fo
->opt_rhost
) +
strlen(fo
->opt_rfs
) + 2);
sprintf(fo
->fs_mtab
, "%s:%s", fo
->opt_rhost
, fo
->opt_rfs
);
dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
fo
->opt_rhost
, fo
->opt_rfs
, fo
->opt_fs
);
* Initialise am structure for nfs
char *colon
= strchr(mf
->mf_info
, ':');
error
= prime_nfs_fhandle_cache(colon
+1, mf
->mf_server
, (struct fhstatus
*) 0, (voidp
) mf
);
mount_nfs_fh(fhp
, dir
, fs_name
, opts
, mf
)
struct nfs_args nfs_args
;
char host
[MAXHOSTNAMELEN
+ MAXPATHLEN
+ 2];
fserver
*fs
= mf
->mf_server
;
MTYPE_TYPE type
= MOUNT_TYPE_NFS
;
bzero((voidp
) &nfs_args
, sizeof(nfs_args
)); /* Paranoid */
* Extract host name to give to kernel
if (!(colon
= strchr(fs_name
, ':')))
#ifndef NFS_ARGS_NEEDS_PATH
strncpy(host
, fs_name
, sizeof(host
));
#ifndef NFS_ARGS_NEEDS_PATH
#endif /* NFS_ARGS_NEEDS_PATH */
bzero((voidp
) &nfs_args
, sizeof(nfs_args
));
mnt
.mnt_fsname
= fs_name
;
mnt
.mnt_type
= MTAB_TYPE_NFS
;
retry
= hasmntval(&mnt
, "retry");
NFS_FH_DREF(nfs_args
.fh
, (NFS_FH_TYPE
) fhp
->fhstatus_u
.fhs_fhandle
);
nfs_args
.optstr
= mnt
.mnt_opts
;
nfs_args
.hostname
= host
;
nfs_args
.flags
|= NFSMNT_HOSTNAME
;
* Most kernels have a name length restriction.
if (strlen(host
) >= HOSTNAMESZ
)
strcpy(host
+ HOSTNAMESZ
- 3, "..");
if (nfs_args
.rsize
= hasmntval(&mnt
, "rsize"))
nfs_args
.flags
|= NFSMNT_RSIZE
;
if (nfs_args
.wsize
= hasmntval(&mnt
, "wsize"))
nfs_args
.flags
|= NFSMNT_WSIZE
;
if (nfs_args
.timeo
= hasmntval(&mnt
, "timeo"))
nfs_args
.flags
|= NFSMNT_TIMEO
;
if (nfs_args
.retrans
= hasmntval(&mnt
, "retrans"))
nfs_args
.flags
|= NFSMNT_RETRANS
;
if (nfs_args
.biods
= hasmntval(&mnt
, "biods"))
nfs_args
.flags
|= NFSMNT_BIODS
;
#endif /* NFSMNT_BIODS */
* This isn't supported by the ping algorithm yet.
* In any case, it is all done in nfs_init().
if (port
= hasmntval(&mnt
, "port"))
sin
.sin_port
= htons(port
);
sin
.sin_port
= htons(NFS_PORT
); /* XXX should use portmapper */
if (hasmntopt(&mnt
, MNTOPT_SOFT
) != NULL
)
nfs_args
.flags
|= NFSMNT_SOFT
;
if (hasmntopt(&mnt
, MNTOPT_INTR
) != NULL
)
nfs_args
.flags
|= NFSMNT_INT
;
if (hasmntopt(&mnt
, MNTOPT_NODEVS
) != NULL
)
nfs_args
.flags
|= NFSMNT_NODEVS
;
#endif /* MNTOPT_NODEVS */
if (nfs_args
.pg_thresh
= hasmntval(&mnt
, "pgthresh"))
nfs_args
.flags
|= NFSMNT_PGTHRESH
;
#endif /* NFSMNT_PGTHRESH */
NFS_SA_DREF(nfs_args
, fs
->fs_ip
);
flags
= compute_mount_flags(&mnt
);
if (hasmntopt(&mnt
, "tcp") != NULL
)
nfs_args
.sotype
= SOCK_STREAM
;
* Ultrix passes the flags argument as part of the
* mount data structure, rather than using the
* flags argument to the system call. This is
if (!(nfs_args
.flags
& NFSMNT_PGTHRESH
)) {
nfs_args
.pg_thresh
= 64; /* 64k - XXX */
nfs_args
.flags
|= NFSMNT_PGTHRESH
;
nfs_args
.gfs_flags
= flags
;
nfs_args
.flags
|= NFSMNT_RONLY
;
return mount_fs(&mnt
, flags
, (caddr_t
) &nfs_args
, retry
, type
);
static mount_nfs(dir
, fs_name
, opts
, mf
)
if (!(colon
= strchr(fs_name
, ':')))
dlog("locating fhandle for %s", fs_name
);
error
= prime_nfs_fhandle_cache(colon
+1, mf
->mf_server
, &fhs
, (voidp
) 0);
return mount_nfs_fh(&fhs
, dir
, fs_name
, opts
, mf
);
int error
= mount_nfs(mf
->mf_mount
, mf
->mf_info
,
mf
->mf_fo
->opt_opts
, mf
);
static int nfs_umount(mp
)
int error
= UMOUNT_FS(mf
->mf_mount
);
static void nfs_umounted(mp
)
* Don't bother to inform remote mountd
* that we are finished. Until a full
* track of filehandles is maintained
* the mountd unmount callback cannot
* be done correctly anyway...
if (mf
->mf_error
|| mf
->mf_refc
> 1)
* Call the mount daemon on the server to
* announce that we are not using the fs any more.
* This is *wrong*. The mountd should be called
* when the fhandle is flushed from the cache, and
* a reference held to the cached entry while the
colon
= path
= strchr(mf
->mf_info
, ':');
dlog("calling mountd for %s", mf
->mf_info
);
f
.fh_sin
.sin_port
= (u_short
) 0;
(void) prime_nfs_fhandle_cache(colon
+1, mf
->mf_server
, (struct fhstatus
*) 0, (voidp
) mf
);
(void) call_mountd(&f
, MOUNTPROC_UMNT
, (fwd_fun
) 0, (voidp
) 0);
#endif /* INFORM_MOUNTD */
FS_MKMNT
|FS_BACKGROUND
|FS_AMQINFO