718 lines
17 KiB
C
718 lines
17 KiB
C
/* $OpenBSD: nfs_ops.c,v 1.28 2024/04/23 13:34:51 jsg Exp $ */
|
|
|
|
/*-
|
|
* Copyright (c) 1990 Jan-Simon Pendry
|
|
* Copyright (c) 1990 Imperial College of Science, Technology & Medicine
|
|
* Copyright (c) 1990, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Berkeley by
|
|
* Jan-Simon Pendry at Imperial College, London.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "am.h"
|
|
#include <sys/stat.h>
|
|
|
|
#ifdef HAS_NFS
|
|
|
|
#define NFS
|
|
#define NFSCLIENT
|
|
|
|
#include "mount.h"
|
|
|
|
/*
|
|
* Network file system
|
|
*/
|
|
|
|
/*
|
|
* 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
|
|
* elsewhere.
|
|
* 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
|
|
* problems...
|
|
*/
|
|
typedef struct fh_cache fh_cache;
|
|
struct fh_cache {
|
|
qelem fh_q; /* List header */
|
|
void *fh_wchan; /* Wait channel */
|
|
int fh_error; /* Valid data? */
|
|
int fh_id; /* Unique id */
|
|
int fh_cid; /* Callout id */
|
|
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 */
|
|
|
|
static int fh_id = 0;
|
|
#define FHID_ALLOC() (++fh_id)
|
|
extern qelem fh_head;
|
|
qelem fh_head = { &fh_head, &fh_head };
|
|
|
|
static int call_mountd(fh_cache*, unsigned long, fwd_fun, void *);
|
|
|
|
AUTH *nfs_auth;
|
|
|
|
static fh_cache *
|
|
find_nfs_fhandle_cache(void *idv, int done)
|
|
{
|
|
fh_cache *fp, *fp2 = 0;
|
|
/* XXX EVIL XXX */
|
|
int id = (int) ((long)idv);
|
|
|
|
ITER(fp, fh_cache, &fh_head) {
|
|
if (fp->fh_id == id) {
|
|
fp2 = fp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
if (fp2) {
|
|
dlog("fh cache gives fp %#x, fs %s", fp2, fp2->fh_path);
|
|
} else {
|
|
dlog("fh cache search failed");
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
if (fp2 && !done) {
|
|
fp2->fh_error = ETIMEDOUT;
|
|
return 0;
|
|
}
|
|
|
|
return fp2;
|
|
}
|
|
|
|
/*
|
|
* Called when a filehandle appears
|
|
*/
|
|
static void
|
|
got_nfs_fh(void *pkt, int len, struct sockaddr_in *sa,
|
|
struct sockaddr_in *ia, void *idv, int done)
|
|
{
|
|
fh_cache *fp = find_nfs_fhandle_cache(idv, done);
|
|
if (fp) {
|
|
fp->fh_handle.fhs_vers = MOUNTVERS;
|
|
fp->fh_error = pickup_rpc_reply(pkt, len, &fp->fh_handle,
|
|
xdr_fhstatus);
|
|
if (!fp->fh_error) {
|
|
#ifdef DEBUG
|
|
dlog("got filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
|
|
#endif /* DEBUG */
|
|
/*
|
|
* Wakeup anything sleeping on this filehandle
|
|
*/
|
|
if (fp->fh_wchan) {
|
|
#ifdef DEBUG
|
|
dlog("Calling wakeup on %#x", fp->fh_wchan);
|
|
#endif /* DEBUG */
|
|
wakeup(fp->fh_wchan);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
flush_nfs_fhandle_cache(fserver *fs)
|
|
{
|
|
fh_cache *fp;
|
|
ITER(fp, fh_cache, &fh_head) {
|
|
if (fp->fh_fs == fs || fs == 0) {
|
|
fp->fh_sin.sin_port = (u_short) 0;
|
|
fp->fh_error = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
discard_fh(void *arg)
|
|
{
|
|
fh_cache *fp = arg;
|
|
|
|
rem_que(&fp->fh_q);
|
|
#ifdef DEBUG
|
|
dlog("Discarding filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
|
|
#endif /* DEBUG */
|
|
free_srvr(fp->fh_fs);
|
|
free(fp->fh_path);
|
|
free(fp);
|
|
}
|
|
|
|
/*
|
|
* Determine the file handle for a node
|
|
*/
|
|
static int
|
|
prime_nfs_fhandle_cache(char *path, fserver *fs, fhstatus *fhbuf, void *wchan)
|
|
{
|
|
fh_cache *fp, *fp_save = 0;
|
|
int error;
|
|
int reuse_id = FALSE;
|
|
|
|
#ifdef DEBUG
|
|
dlog("Searching cache for %s:%s", fs->fs_host, path);
|
|
#endif /* DEBUG */
|
|
|
|
/*
|
|
* First search the cache
|
|
*/
|
|
ITER(fp, fh_cache, &fh_head) {
|
|
if (fs == fp->fh_fs && strcmp(path, fp->fh_path) == 0) {
|
|
switch (fp->fh_error) {
|
|
case 0:
|
|
error = fp->fh_error = unx_error(fp->fh_handle.fhs_stat);
|
|
if (error == 0) {
|
|
if (fhbuf)
|
|
bcopy(&fp->fh_handle, fhbuf,
|
|
sizeof(fp->fh_handle));
|
|
if (fp->fh_cid)
|
|
untimeout(fp->fh_cid);
|
|
fp->fh_cid = timeout(FH_TTL,
|
|
discard_fh, fp);
|
|
} else if (error == EACCES) {
|
|
/*
|
|
* Now decode the file handle return code.
|
|
*/
|
|
plog(XLOG_INFO, "Filehandle denied for \"%s:%s\"",
|
|
fs->fs_host, path);
|
|
} else {
|
|
errno = error; /* XXX */
|
|
plog(XLOG_INFO, "Filehandle error for \"%s:%s\": %m",
|
|
fs->fs_host, path);
|
|
}
|
|
|
|
/*
|
|
* The error was returned from the remote mount daemon.
|
|
* Policy: this error will be cached for now...
|
|
*/
|
|
return error;
|
|
|
|
case -1:
|
|
/*
|
|
* Still thinking about it, but we can re-use.
|
|
*/
|
|
fp_save = fp;
|
|
reuse_id = TRUE;
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* Return the error.
|
|
* 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.
|
|
*/
|
|
error = fp->fh_error;
|
|
fp->fh_error = -1;
|
|
return error;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Not in cache
|
|
*/
|
|
if (fp_save) {
|
|
fp = fp_save;
|
|
/*
|
|
* Re-use existing slot
|
|
*/
|
|
untimeout(fp->fh_cid);
|
|
free_srvr(fp->fh_fs);
|
|
free(fp->fh_path);
|
|
} else {
|
|
fp = ALLOC(fh_cache);
|
|
bzero(fp, sizeof(*fp));
|
|
ins_que(&fp->fh_q, &fh_head);
|
|
}
|
|
if (!reuse_id)
|
|
fp->fh_id = FHID_ALLOC();
|
|
fp->fh_wchan = wchan;
|
|
fp->fh_error = -1;
|
|
fp->fh_cid = timeout(FH_TTL, discard_fh, fp);
|
|
|
|
/*
|
|
* If the address has changed then don't try to re-use the
|
|
* port information
|
|
*/
|
|
if (fp->fh_sin.sin_addr.s_addr != fs->fs_ip->sin_addr.s_addr) {
|
|
fp->fh_sin = *fs->fs_ip;
|
|
fp->fh_sin.sin_port = 0;
|
|
}
|
|
fp->fh_fs = dup_srvr(fs);
|
|
fp->fh_path = strdup(path);
|
|
|
|
error = call_mountd(fp, MOUNTPROC_MNT, got_nfs_fh, wchan);
|
|
if (error) {
|
|
/*
|
|
* Local error - cache for a short period
|
|
* just to prevent thrashing.
|
|
*/
|
|
untimeout(fp->fh_cid);
|
|
fp->fh_cid = timeout(error < 0 ? 2 * ALLOWED_MOUNT_TIME : FH_TTL_ERROR,
|
|
discard_fh, fp);
|
|
fp->fh_error = error;
|
|
} else {
|
|
error = fp->fh_error;
|
|
}
|
|
return error;
|
|
}
|
|
|
|
int
|
|
make_nfs_auth(void)
|
|
{
|
|
/*
|
|
* From: Chris Metcalf <metcalf@masala.lcs.mit.edu>
|
|
* Use hostd, not just hostname. Note that uids
|
|
* and gids and the gidlist are type *int* and not the
|
|
* system uid_t and gid_t types.
|
|
*/
|
|
static int group_wheel = 0;
|
|
nfs_auth = authunix_create(hostd, 0, 0, 1, &group_wheel);
|
|
if (!nfs_auth)
|
|
return ENOBUFS;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
call_mountd(fh_cache *fp, u_long proc, fwd_fun f, void *wchan)
|
|
{
|
|
struct rpc_msg mnt_msg;
|
|
int len;
|
|
char iobuf[8192];
|
|
int error;
|
|
|
|
if (!nfs_auth) {
|
|
error = make_nfs_auth();
|
|
if (error)
|
|
return error;
|
|
}
|
|
|
|
if (fp->fh_sin.sin_port == 0) {
|
|
u_short port;
|
|
error = nfs_srvr_port(fp->fh_fs, &port, wchan);
|
|
if (error)
|
|
return error;
|
|
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, &fp->fh_path, xdr_nfspath, nfs_auth);
|
|
|
|
/*
|
|
* XXX EVIL! We cast fh_id to a pointer, then back to an int
|
|
* XXX later.
|
|
*/
|
|
if (len > 0) {
|
|
error = fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD, fp->fh_id),
|
|
iobuf, len, &fp->fh_sin, &fp->fh_sin,
|
|
(void *)((long)fp->fh_id), f);
|
|
} else {
|
|
error = -len;
|
|
}
|
|
/*
|
|
* It may be the case that we're sending to the wrong MOUNTD port. This
|
|
* occurs if mountd is restarted on the server after the port has been
|
|
* looked up and stored in the filehandle cache somewhere. The correct
|
|
* solution, if we're going to cache port numbers is to catch the ICMP
|
|
* port unreachable reply from the server and cause the portmap request
|
|
* to be redone. The quick solution here is to invalidate the MOUNTD
|
|
* port.
|
|
*/
|
|
fp->fh_sin.sin_port = 0;
|
|
|
|
return error;
|
|
}
|
|
|
|
/*-------------------------------------------------------------------------*/
|
|
|
|
/*
|
|
* NFS needs the local filesystem, remote filesystem
|
|
* remote hostname.
|
|
* Local filesystem defaults to remote and vice-versa.
|
|
*/
|
|
static char *
|
|
nfs_match(am_opts *fo)
|
|
{
|
|
char *xmtab;
|
|
if (fo->opt_fs && !fo->opt_rfs)
|
|
fo->opt_rfs = fo->opt_fs;
|
|
if (!fo->opt_rfs) {
|
|
plog(XLOG_USER, "nfs: no remote filesystem specified");
|
|
return FALSE;
|
|
}
|
|
if (!fo->opt_rhost) {
|
|
plog(XLOG_USER, "nfs: no remote host specified");
|
|
return FALSE;
|
|
}
|
|
/*
|
|
* Determine magic cookie to put in mtab
|
|
*/
|
|
xmtab = xmalloc(strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2);
|
|
snprintf(xmtab, strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2,
|
|
"%s:%s", fo->opt_rhost, fo->opt_rfs);
|
|
#ifdef DEBUG
|
|
dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
|
|
fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
|
|
#endif /* DEBUG */
|
|
|
|
return xmtab;
|
|
}
|
|
|
|
/*
|
|
* Initialise am structure for nfs
|
|
*/
|
|
static int
|
|
nfs_init(mntfs *mf)
|
|
{
|
|
if (!mf->mf_private) {
|
|
int error;
|
|
fhstatus fhs;
|
|
|
|
char *colon = strchr(mf->mf_info, ':');
|
|
if (colon == 0)
|
|
return ENOENT;
|
|
|
|
error = prime_nfs_fhandle_cache(colon+1, mf->mf_server,
|
|
&fhs, mf);
|
|
if (!error) {
|
|
mf->mf_private = ALLOC(fhstatus);
|
|
mf->mf_prfree = free;
|
|
bcopy(&fhs, mf->mf_private, sizeof(fhs));
|
|
}
|
|
return error;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
mount_nfs_fh(fhstatus *fhp, char *dir, char *fs_name, char *opts,
|
|
mntfs *mf)
|
|
{
|
|
struct nfs_args nfs_args;
|
|
struct mntent mnt;
|
|
int retry;
|
|
char *colon;
|
|
/*char *path;*/
|
|
char host[HOST_NAME_MAX+1 + PATH_MAX+2];
|
|
fserver *fs = mf->mf_server;
|
|
int flags;
|
|
char *xopts;
|
|
int error;
|
|
#ifdef notdef
|
|
unsigned short port;
|
|
#endif /* notdef */
|
|
|
|
const char *type = MOUNT_NFS;
|
|
|
|
bzero(&nfs_args, sizeof(nfs_args)); /* Paranoid */
|
|
|
|
/*
|
|
* Extract host name to give to kernel
|
|
*/
|
|
if (!(colon = strchr(fs_name, ':')))
|
|
return ENOENT;
|
|
strlcpy(host, fs_name, sizeof(host));
|
|
/*path = colon + 1;*/
|
|
|
|
if (mf->mf_remopts && *mf->mf_remopts && !islocalnet(fs->fs_ip->sin_addr.s_addr))
|
|
xopts = strdup(mf->mf_remopts);
|
|
else
|
|
xopts = strdup(opts);
|
|
|
|
bzero(&nfs_args, sizeof(nfs_args));
|
|
|
|
mnt.mnt_dir = dir;
|
|
mnt.mnt_fsname = fs_name;
|
|
mnt.mnt_type = "nfs";
|
|
mnt.mnt_opts = xopts;
|
|
mnt.mnt_freq = 0;
|
|
mnt.mnt_passno = 0;
|
|
|
|
retry = hasmntval(&mnt, "retry");
|
|
if (retry <= 0)
|
|
retry = 1; /* XXX */
|
|
|
|
/*again:*/
|
|
|
|
/*
|
|
* set mount args
|
|
*/
|
|
nfs_args.fh = (void *)fhp->fhs_fhandle;
|
|
nfs_args.fhsize = fhp->fhs_size;
|
|
nfs_args.version = NFS_ARGSVERSION;
|
|
|
|
nfs_args.hostname = host;
|
|
#ifdef HOSTNAMESZ
|
|
/*
|
|
* Most kernels have a name length restriction.
|
|
*/
|
|
if (strlen(host) >= HOSTNAMESZ)
|
|
strlcpy(host + HOSTNAMESZ - 3, "..", sizeof host - HOSTNAMESZ + 3);
|
|
#endif /* HOSTNAMESZ */
|
|
|
|
if ((nfs_args.rsize = hasmntval(&mnt, "rsize")))
|
|
nfs_args.flags |= NFSMNT_RSIZE;
|
|
|
|
#ifdef NFSMNT_READDIRSIZE
|
|
if ((nfs_args.readdirsize = hasmntval(&mnt, "readdirsize"))) {
|
|
nfs_args.flags |= NFSMNT_READDIRSIZE;
|
|
} else if (nfs_args.rsize) {
|
|
nfs_args.readdirsize = nfs_args.rsize;
|
|
nfs_args.flags |= NFSMNT_READDIRSIZE;
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
|
|
#ifdef NFSMNT_BIODS
|
|
if ((nfs_args.biods = hasmntval(&mnt, "biods")))
|
|
nfs_args.flags |= NFSMNT_BIODS;
|
|
|
|
#endif /* NFSMNT_BIODS */
|
|
|
|
#ifdef NFSMNT_MAXGRPS
|
|
if ((nfs_args.maxgrouplist = hasmntval(&mnt, "maxgroups")))
|
|
nfs_args.flags |= NFSMNT_MAXGRPS;
|
|
#endif /* NFSMNT_MAXGRPS */
|
|
|
|
#ifdef NFSMNT_READAHEAD
|
|
if ((nfs_args.readahead = hasmntval(&mnt, "readahead")))
|
|
nfs_args.flags |= NFSMNT_READAHEAD;
|
|
#endif /* NFSMNT_READAHEAD */
|
|
|
|
#ifdef notdef
|
|
/*
|
|
* 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);
|
|
else
|
|
sin.sin_port = htons(NFS_PORT); /* XXX should use portmapper */
|
|
#endif /* notdef */
|
|
|
|
if (hasmntopt(&mnt, "soft") != NULL)
|
|
nfs_args.flags |= NFSMNT_SOFT;
|
|
|
|
#ifdef NFSMNT_SPONGY
|
|
if (hasmntopt(&mnt, "spongy") != NULL) {
|
|
nfs_args.flags |= NFSMNT_SPONGY;
|
|
if (nfs_args.flags & NFSMNT_SOFT) {
|
|
plog(XLOG_USER, "Mount opts soft and spongy are incompatible - soft ignored");
|
|
nfs_args.flags &= ~NFSMNT_SOFT;
|
|
}
|
|
}
|
|
#endif /* MNTOPT_SPONGY */
|
|
|
|
if (hasmntopt(&mnt, "intr") != NULL)
|
|
nfs_args.flags |= NFSMNT_INT;
|
|
|
|
#ifdef MNTOPT_NODEVS
|
|
if (hasmntopt(&mnt, MNTOPT_NODEVS) != NULL)
|
|
nfs_args.flags |= NFSMNT_NODEVS;
|
|
#endif /* MNTOPT_NODEVS */
|
|
|
|
|
|
if (hasmntopt(&mnt, "noconn") != NULL)
|
|
nfs_args.flags |= NFSMNT_NOCONN;
|
|
|
|
if (hasmntopt(&mnt, "resvport") != NULL)
|
|
nfs_args.flags |= NFSMNT_RESVPORT;
|
|
|
|
#ifdef NFSMNT_PGTHRESH
|
|
if ((nfs_args.pg_thresh = hasmntval(&mnt, "pgthresh")))
|
|
nfs_args.flags |= NFSMNT_PGTHRESH;
|
|
#endif /* NFSMNT_PGTHRESH */
|
|
|
|
nfs_args.addr = (struct sockaddr *)fs->fs_ip;
|
|
nfs_args.addrlen = sizeof(*fs->fs_ip);
|
|
nfs_args.sotype = SOCK_DGRAM;
|
|
nfs_args.proto = 0;
|
|
|
|
flags = compute_mount_flags(&mnt);
|
|
|
|
#ifdef NFSMNT_NOCTO
|
|
if (hasmntopt(&mnt, "nocto") != NULL)
|
|
nfs_args.flags |= NFSMNT_NOCTO;
|
|
#endif /* NFSMNT_NOCTO */
|
|
|
|
if (hasmntopt(&mnt, "tcp") != NULL)
|
|
nfs_args.sotype = SOCK_STREAM;
|
|
|
|
|
|
|
|
error = mount_fs(&mnt, flags, (caddr_t) &nfs_args, retry, type);
|
|
free(xopts);
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
mount_nfs(char *dir, char *fs_name, char *opts, mntfs *mf)
|
|
{
|
|
#ifdef notdef
|
|
int error;
|
|
fhstatus fhs;
|
|
char *colon;
|
|
|
|
if (!(colon = strchr(fs_name, ':')))
|
|
return ENOENT;
|
|
|
|
#ifdef DEBUG
|
|
dlog("locating fhandle for %s", fs_name);
|
|
#endif /* DEBUG */
|
|
error = prime_nfs_fhandle_cache(colon+1, mf->mf_server, &fhs, NULL);
|
|
|
|
if (error)
|
|
return error;
|
|
|
|
return mount_nfs_fh(&fhs, dir, fs_name, opts, mf);
|
|
#endif
|
|
if (!mf->mf_private) {
|
|
plog(XLOG_ERROR, "Missing filehandle for %s", fs_name);
|
|
return EINVAL;
|
|
}
|
|
|
|
return mount_nfs_fh((fhstatus *) mf->mf_private, dir, fs_name, opts, mf);
|
|
}
|
|
|
|
static int
|
|
nfs_fmount(mntfs *mf)
|
|
{
|
|
int error;
|
|
|
|
error = mount_nfs(mf->mf_mount, mf->mf_info, mf->mf_mopts, mf);
|
|
|
|
#ifdef DEBUG
|
|
if (error) {
|
|
errno = error;
|
|
dlog("mount_nfs: %m");
|
|
}
|
|
#endif /* DEBUG */
|
|
return error;
|
|
}
|
|
|
|
static int
|
|
nfs_fumount(mntfs *mf)
|
|
{
|
|
return (umount_fs(mf->mf_mount));
|
|
}
|
|
|
|
static void
|
|
nfs_umounted(am_node *mp)
|
|
{
|
|
|
|
#ifdef KICK_KERNEL
|
|
/* This should go into the mainline code, not in nfs_ops... */
|
|
|
|
/*
|
|
* Run lstat over the underlying directory in
|
|
* case this was a direct mount. This will
|
|
* get the kernel back in sync with reality.
|
|
*/
|
|
if (mp->am_parent && mp->am_parent->am_path &&
|
|
STREQ(mp->am_parent->am_mnt->mf_ops->fs_type, "direct")) {
|
|
struct stat stb;
|
|
pid_t pid;
|
|
if ((pid = background()) == 0) {
|
|
if (lstat(mp->am_parent->am_path, &stb) < 0) {
|
|
plog(XLOG_ERROR, "lstat(%s) after unmount: %m", mp->am_parent->am_path);
|
|
#ifdef DEBUG
|
|
} else {
|
|
dlog("hack lstat(%s): ok", mp->am_parent->am_path);
|
|
#endif /* DEBUG */
|
|
}
|
|
_exit(0);
|
|
}
|
|
}
|
|
#endif /* KICK_KERNEL */
|
|
}
|
|
|
|
/*
|
|
* Network file system
|
|
*/
|
|
am_ops nfs_ops = {
|
|
"nfs",
|
|
nfs_match,
|
|
nfs_init,
|
|
auto_fmount,
|
|
nfs_fmount,
|
|
auto_fumount,
|
|
nfs_fumount,
|
|
efs_lookuppn,
|
|
efs_readdir,
|
|
0, /* nfs_readlink */
|
|
0, /* nfs_mounted */
|
|
nfs_umounted,
|
|
find_nfs_srvr,
|
|
FS_MKMNT|FS_BACKGROUND|FS_AMQINFO
|
|
};
|
|
|
|
#endif /* HAS_NFS */
|