#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/ioctl.h>
#include <sys/signal.h>
#include <sys/proc_internal.h>
#include <sys/kauth.h>
#include <sys/vnode_internal.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/sysctl.h>
#include <sys/mount_internal.h>
#include <sys/kpi_mbuf.h>
#include <sys/socket.h>
#include <sys/socketvar.h>
#include <sys/fcntl.h>
#include <libkern/OSAtomic.h>
#include <sys/vm.h>
#include <sys/vmparam.h>
#if !defined(NO_MOUNT_PRIVATE)
#include <sys/filedesc.h>
#endif
#include <net/if.h>
#include <net/route.h>
#include <netinet/in.h>
#include <nfs/rpcv2.h>
#include <nfs/nfsproto.h>
#include <nfs/nfs.h>
#include <nfs/nfsnode.h>
#include <nfs/nfsmount.h>
#include <nfs/xdr_subs.h>
#include <nfs/nfsm_subs.h>
#include <nfs/nfsdiskless.h>
#include <nfs/nfs_lock.h>
extern int nfs_mountroot(void);
extern int nfs_ticks;
extern int nfs_mount_type;
extern int nfs_resv_mounts;
struct nfsstats nfsstats;
static int nfs_sysctl(int *, u_int, user_addr_t, size_t *, user_addr_t, size_t, vfs_context_t);
#ifdef notyet
SYSCTL_NODE(_vfs, MOUNT_NFS, nfs, CTLFLAG_RW, 0, "NFS filesystem");
SYSCTL_STRUCT(_vfs_nfs, NFS_NFSSTATS, nfsstats, CTLFLAG_RD,
&nfsstats, nfsstats, "");
#endif
SYSCTL_DECL(_vfs_generic_nfs);
SYSCTL_NODE(_vfs_generic_nfs, OID_AUTO, client, CTLFLAG_RW, 0,
"nfs client hinge");
static int nfs_tprintf_initial_delay = NFS_TPRINTF_INITIAL_DELAY;
SYSCTL_INT(_vfs_generic_nfs_client, NFS_TPRINTF_INITIAL_DELAY,
initialdowndelay, CTLFLAG_RW, &nfs_tprintf_initial_delay, 0, "");
static int nfs_tprintf_delay = NFS_TPRINTF_DELAY;
SYSCTL_INT(_vfs_generic_nfs_client, NFS_TPRINTF_DELAY,
nextdowndelay, CTLFLAG_RW, &nfs_tprintf_delay, 0, "");
static int nfs_iosize(struct nfsmount *nmp);
static int mountnfs(struct user_nfs_args *,mount_t,mbuf_t,proc_t,vnode_t *);
static int nfs_mount(mount_t mp, vnode_t vp, user_addr_t data, vfs_context_t context);
static int nfs_start(mount_t mp, int flags, vfs_context_t context);
static int nfs_unmount(mount_t mp, int mntflags, vfs_context_t context);
static int nfs_root(mount_t mp, vnode_t *vpp, vfs_context_t context);
static int nfs_statfs(mount_t mp, struct vfsstatfs *sbp, vfs_context_t context);
static int nfs_vfs_getattr(mount_t mp, struct vfs_attr *fsap, vfs_context_t context);
static int nfs_sync( mount_t mp, int waitfor, vfs_context_t context);
static int nfs_vptofh(vnode_t vp, int *fhlenp, unsigned char *fhp, vfs_context_t context);
static int nfs_fhtovp(mount_t mp, int fhlen, unsigned char *fhp, vnode_t *vpp, vfs_context_t context);
static int nfs_vget(mount_t , ino64_t, vnode_t *, vfs_context_t context);
struct vfsops nfs_vfsops = {
nfs_mount,
nfs_start,
nfs_unmount,
nfs_root,
NULL,
nfs_vfs_getattr,
nfs_sync,
nfs_vget,
nfs_fhtovp,
nfs_vptofh,
nfs_init,
nfs_sysctl,
NULL
};
static int
nfs_mount_diskless(struct nfs_dlmount *, const char *, int, vnode_t *, mount_t *);
#if !defined(NO_MOUNT_PRIVATE)
static int
nfs_mount_diskless_private(struct nfs_dlmount *, const char *, int, vnode_t *, mount_t *);
#endif
static int nfs_iosize(nmp)
struct nfsmount* nmp;
{
int iosize;
iosize = max(nmp->nm_rsize, nmp->nm_wsize);
if (iosize < PAGE_SIZE)
iosize = PAGE_SIZE;
return (trunc_page_32(iosize));
}
int
nfs_statfs(mount_t mp, struct vfsstatfs *sbp, vfs_context_t context)
{
proc_t p = vfs_context_proc(context);
vnode_t vp;
struct nfs_statfs *sfp;
caddr_t cp;
u_long *tl;
long t1, t2;
caddr_t bpos, dpos, cp2;
struct nfsmount *nmp = VFSTONFS(mp);
int error = 0, v3 = (nmp->nm_flag & NFSMNT_NFSV3), retattr;
mbuf_t mreq, mrep, md, mb, mb2;
u_int64_t xid;
kauth_cred_t cred;
struct ucred temp_cred;
#ifndef nolint
sfp = (struct nfs_statfs *)0;
#endif
vp = nmp->nm_dvp;
if ((error = vnode_get(vp)))
return(error);
bzero(&temp_cred, sizeof(temp_cred));
temp_cred.cr_ngroups = 1;
cred = kauth_cred_create(&temp_cred);
if (v3 && (nmp->nm_state & NFSSTA_GOTFSINFO) == 0)
nfs_fsinfo(nmp, vp, cred, p);
nfsm_reqhead(NFSX_FH(v3));
if (error) {
kauth_cred_rele(cred);
vnode_put(vp);
return (error);
}
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_FSSTAT]);
nfsm_fhtom(vp, v3);
nfsm_request(vp, NFSPROC_FSSTAT, p, cred, &xid);
if (v3 && mrep)
nfsm_postop_attr_update(vp, v3, retattr, &xid);
nfsm_dissect(sfp, struct nfs_statfs *, NFSX_STATFS(v3));
sbp->f_flags = nmp->nm_flag;
sbp->f_iosize = nfs_iosize(nmp);
if (v3) {
u_quad_t tquad, tquad2, bsize;
bsize = NFS_FABLKSIZE;
fxdr_hyper(&sfp->sf_tbytes, &tquad);
tquad /= bsize;
while ((tquad & ~0x7fffffff) && (bsize < 0x40000000)) {
bsize <<= 1;
tquad >>= 1;
}
sbp->f_blocks = (tquad & ~0x7fffffff) ? 0x7fffffff : (long)tquad;
fxdr_hyper(&sfp->sf_fbytes, &tquad);
tquad /= bsize;
sbp->f_bfree = (tquad & ~0x7fffffff) ? 0x7fffffff : (long)tquad;
fxdr_hyper(&sfp->sf_abytes, &tquad);
tquad /= bsize;
sbp->f_bavail = (tquad & ~0x7fffffff) ? 0x7fffffff : (long)tquad;
sbp->f_bsize = (long)bsize;
fxdr_hyper(&sfp->sf_tfiles, &tquad);
fxdr_hyper(&sfp->sf_ffiles, &tquad2);
while (tquad & ~0x7fffffff) {
tquad >>= 1;
tquad2 >>= 1;
}
sbp->f_files = tquad;
sbp->f_ffree = tquad2;
} else {
sbp->f_bsize = fxdr_unsigned(long, sfp->sf_bsize);
sbp->f_blocks = fxdr_unsigned(long, sfp->sf_blocks);
sbp->f_bfree = fxdr_unsigned(long, sfp->sf_bfree);
sbp->f_bavail = fxdr_unsigned(long, sfp->sf_bavail);
sbp->f_files = 0;
sbp->f_ffree = 0;
}
nfsm_reqdone;
kauth_cred_rele(cred);
vnode_put(vp);
return (error);
}
static int
nfs_vfs_getattr(mount_t mp, struct vfs_attr *fsap, vfs_context_t context)
{
int error = 0;
if (VFSATTR_IS_ACTIVE(fsap, f_bsize) ||
VFSATTR_IS_ACTIVE(fsap, f_iosize) ||
VFSATTR_IS_ACTIVE(fsap, f_blocks) ||
VFSATTR_IS_ACTIVE(fsap, f_bfree) ||
VFSATTR_IS_ACTIVE(fsap, f_bavail) ||
VFSATTR_IS_ACTIVE(fsap, f_bused) ||
VFSATTR_IS_ACTIVE(fsap, f_files) ||
VFSATTR_IS_ACTIVE(fsap, f_ffree)) {
struct vfsstatfs sb;
error = nfs_statfs(mp, &sb, context);
if (!error) {
VFSATTR_RETURN(fsap, f_bsize, sb.f_bsize);
VFSATTR_RETURN(fsap, f_iosize, sb.f_iosize);
VFSATTR_RETURN(fsap, f_blocks, sb.f_blocks);
VFSATTR_RETURN(fsap, f_bfree, sb.f_bfree);
VFSATTR_RETURN(fsap, f_bavail, sb.f_bavail);
VFSATTR_RETURN(fsap, f_bused, sb.f_blocks - sb.f_bfree);
VFSATTR_RETURN(fsap, f_files, sb.f_files);
VFSATTR_RETURN(fsap, f_ffree, sb.f_ffree);
}
}
if (VFSATTR_IS_ACTIVE(fsap, f_capabilities)) {
struct nfsmount *nmp;
struct nfsv3_pathconf pc;
u_int32_t caps, valid;
vnode_t vp;
int v3;
if (!(nmp = VFSTONFS(mp)))
return (ENXIO);
vp = nmp->nm_dvp;
v3 = (nmp->nm_flag & NFSMNT_NFSV3);
caps = valid = 0;
if (v3) {
if (!(nmp->nm_state & NFSSTA_GOTFSINFO)) {
nfs_fsinfo(nmp, vp, vfs_context_ucred(context),
vfs_context_proc(context));
if (!(nmp = VFSTONFS(vnode_mount(vp))))
return (ENXIO);
}
if (nmp->nm_state & NFSSTA_GOTFSINFO) {
valid |= VOL_CAP_FMT_SYMBOLICLINKS |
VOL_CAP_FMT_HARDLINKS;
if (nmp->nm_fsinfo.fsproperties & NFSV3FSINFO_SYMLINK)
caps |= VOL_CAP_FMT_SYMBOLICLINKS;
if (nmp->nm_fsinfo.fsproperties & NFSV3FSINFO_LINK)
caps |= VOL_CAP_FMT_HARDLINKS;
if ((nmp->nm_fsinfo.fsproperties & NFSV3FSINFO_HOMOGENEOUS) &&
!(nmp->nm_state & NFSSTA_GOTPATHCONF)) {
error = nfs_pathconfrpc(vp, &pc,
vfs_context_ucred(context),
vfs_context_proc(context));
if (!(nmp = VFSTONFS(vnode_mount(vp))))
return (ENXIO);
if (!error) {
nfs_pathconf_cache(nmp, &pc);
}
}
if (nmp->nm_state & NFSSTA_GOTPATHCONF) {
valid |= VOL_CAP_FMT_CASE_SENSITIVE |
VOL_CAP_FMT_CASE_PRESERVING;
if (!(nmp->nm_fsinfo.pcflags &
NFSPCINFO_CASE_INSENSITIVE))
caps |= VOL_CAP_FMT_CASE_SENSITIVE;
if (nmp->nm_fsinfo.pcflags &
NFSPCINFO_CASE_PRESERVING)
caps |= VOL_CAP_FMT_CASE_PRESERVING;
}
if (nmp->nm_fsinfo.maxfilesize >= 0x20000000000ULL)
caps |= VOL_CAP_FMT_2TB_FILESIZE;
} else {
caps |= VOL_CAP_FMT_2TB_FILESIZE;
}
}
fsap->f_capabilities.capabilities[VOL_CAPABILITIES_FORMAT] =
caps;
fsap->f_capabilities.valid[VOL_CAPABILITIES_FORMAT] =
VOL_CAP_FMT_PERSISTENTOBJECTIDS |
VOL_CAP_FMT_FAST_STATFS |
VOL_CAP_FMT_2TB_FILESIZE |
valid;
caps = valid = 0;
if ((!nfslockdvnode && !nfslockdwaiting) ||
(nmp->nm_flag & NFSMNT_NOLOCKS)) {
valid = VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK;
} else if (nmp->nm_state & NFSSTA_LOCKSWORK) {
caps = VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK;
valid = VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK;
}
fsap->f_capabilities.capabilities[VOL_CAPABILITIES_INTERFACES] =
caps;
fsap->f_capabilities.valid[VOL_CAPABILITIES_INTERFACES] =
VOL_CAP_INT_SEARCHFS |
VOL_CAP_INT_ATTRLIST |
VOL_CAP_INT_NFSEXPORT |
VOL_CAP_INT_READDIRATTR |
VOL_CAP_INT_EXCHANGEDATA |
VOL_CAP_INT_COPYFILE |
VOL_CAP_INT_ALLOCATE |
VOL_CAP_INT_VOL_RENAME |
valid;
fsap->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED1] = 0;
fsap->f_capabilities.valid[VOL_CAPABILITIES_RESERVED1] = 0;
fsap->f_capabilities.capabilities[VOL_CAPABILITIES_RESERVED2] = 0;
fsap->f_capabilities.valid[VOL_CAPABILITIES_RESERVED2] = 0;
VFSATTR_SET_SUPPORTED(fsap, f_capabilities);
}
if (VFSATTR_IS_ACTIVE(fsap, f_attributes)) {
fsap->f_attributes.validattr.commonattr = 0;
fsap->f_attributes.validattr.volattr =
ATTR_VOL_CAPABILITIES | ATTR_VOL_ATTRIBUTES;
fsap->f_attributes.validattr.dirattr = 0;
fsap->f_attributes.validattr.fileattr = 0;
fsap->f_attributes.validattr.forkattr = 0;
fsap->f_attributes.nativeattr.commonattr = 0;
fsap->f_attributes.nativeattr.volattr =
ATTR_VOL_CAPABILITIES | ATTR_VOL_ATTRIBUTES;
fsap->f_attributes.nativeattr.dirattr = 0;
fsap->f_attributes.nativeattr.fileattr = 0;
fsap->f_attributes.nativeattr.forkattr = 0;
VFSATTR_SET_SUPPORTED(fsap, f_attributes);
}
return (error);
}
int
nfs_fsinfo(nmp, vp, cred, p)
struct nfsmount *nmp;
vnode_t vp;
kauth_cred_t cred;
proc_t p;
{
struct nfsv3_fsinfo *fsp;
caddr_t cp;
long t1, t2;
u_long *tl;
int prefsize, maxsize;
caddr_t bpos, dpos, cp2;
int error = 0, retattr;
mbuf_t mreq, mrep, md, mb, mb2;
u_int64_t xid;
nfsm_reqhead(NFSX_FH(1));
if (error)
return (error);
OSAddAtomic(1, (SInt32*)&nfsstats.rpccnt[NFSPROC_FSINFO]);
nfsm_fhtom(vp, 1);
nfsm_request(vp, NFSPROC_FSINFO, p, cred, &xid);
if (mrep) {
nfsm_postop_attr_update(vp, 1, retattr, &xid);
}
if (!error) {
nfsm_dissect(fsp, struct nfsv3_fsinfo *, NFSX_V3FSINFO);
prefsize = fxdr_unsigned(u_long, fsp->fs_wtpref);
if (prefsize < nmp->nm_wsize)
nmp->nm_wsize = (prefsize + NFS_FABLKSIZE - 1) &
~(NFS_FABLKSIZE - 1);
maxsize = fxdr_unsigned(u_long, fsp->fs_wtmax);
if (maxsize < nmp->nm_wsize) {
nmp->nm_wsize = maxsize & ~(NFS_FABLKSIZE - 1);
if (nmp->nm_wsize == 0)
nmp->nm_wsize = maxsize;
}
prefsize = fxdr_unsigned(u_long, fsp->fs_rtpref);
if (prefsize < nmp->nm_rsize)
nmp->nm_rsize = (prefsize + NFS_FABLKSIZE - 1) &
~(NFS_FABLKSIZE - 1);
maxsize = fxdr_unsigned(u_long, fsp->fs_rtmax);
if (maxsize < nmp->nm_rsize) {
nmp->nm_rsize = maxsize & ~(NFS_FABLKSIZE - 1);
if (nmp->nm_rsize == 0)
nmp->nm_rsize = maxsize;
}
prefsize = fxdr_unsigned(u_long, fsp->fs_dtpref);
if (prefsize < nmp->nm_readdirsize)
nmp->nm_readdirsize = prefsize;
if (maxsize < nmp->nm_readdirsize) {
nmp->nm_readdirsize = maxsize;
}
fxdr_hyper(&fsp->fs_maxfilesize, &nmp->nm_fsinfo.maxfilesize);
nmp->nm_fsinfo.fsproperties = fxdr_unsigned(u_long, fsp->fs_properties);
nmp->nm_state |= NFSSTA_GOTFSINFO;
}
nfsm_reqdone;
return (error);
}
int
nfs_mountroot()
{
struct nfs_diskless nd;
struct nfs_vattr nvattr;
mount_t mp;
vnode_t vp;
proc_t procp;
int error;
#if !defined(NO_MOUNT_PRIVATE)
mount_t mppriv;
vnode_t vppriv;
#endif
int v3, sotype;
procp = current_proc();
bzero((caddr_t) &nd, sizeof(nd));
error = nfs_boot_init(&nd, procp);
if (error) {
panic("nfs_boot_init failed with %d\n", error);
}
v3 = 1;
sotype = SOCK_STREAM;
tryagain:
error = nfs_boot_getfh(&nd, procp, v3, sotype);
if (error) {
if (error == EHOSTDOWN || error == EHOSTUNREACH) {
if (nd.nd_root.ndm_path)
FREE_ZONE(nd.nd_root.ndm_path,
MAXPATHLEN, M_NAMEI);
if (nd.nd_private.ndm_path)
FREE_ZONE(nd.nd_private.ndm_path,
MAXPATHLEN, M_NAMEI);
return (error);
}
if (v3) {
if (sotype == SOCK_STREAM) {
printf("nfs_boot_getfh(v3,TCP) failed with %d, trying UDP...\n", error);
sotype = SOCK_DGRAM;
goto tryagain;
}
printf("nfs_boot_getfh(v3,UDP) failed with %d, trying v2...\n", error);
v3 = 0;
sotype = SOCK_STREAM;
goto tryagain;
} else if (sotype == SOCK_STREAM) {
printf("nfs_boot_getfh(v2,TCP) failed with %d, trying UDP...\n", error);
sotype = SOCK_DGRAM;
goto tryagain;
}
panic("nfs_boot_getfh(v2,UDP) failed with %d\n", error);
}
#if !defined(NO_MOUNT_PRIVATE)
if ((error = nfs_mount_diskless(&nd.nd_root, "/", MNT_RDONLY|MNT_ROOTFS, &vp, &mp)))
#else
if ((error = nfs_mount_diskless(&nd.nd_root, "/", MNT_ROOTFS, &vp, &mp)))
#endif
{
if (v3) {
if (sotype == SOCK_STREAM) {
printf("nfs_mount_diskless(v3,TCP) failed with %d, trying UDP...\n", error);
sotype = SOCK_DGRAM;
goto tryagain;
}
printf("nfs_mount_diskless(v3,UDP) failed with %d, trying v2...\n", error);
v3 = 0;
sotype = SOCK_STREAM;
goto tryagain;
} else if (sotype == SOCK_STREAM) {
printf("nfs_mount_diskless(v2,TCP) failed with %d, trying UDP...\n", error);
sotype = SOCK_DGRAM;
goto tryagain;
}
panic("nfs_mount_diskless(v2,UDP) root failed with %d\n", error);
}
printf("root on %s\n", (char *)&nd.nd_root.ndm_host);
vfs_unbusy(mp);
mount_list_add(mp);
rootvp = vp;
#if !defined(NO_MOUNT_PRIVATE)
if (nd.nd_private.ndm_saddr.sin_addr.s_addr) {
error = nfs_mount_diskless_private(&nd.nd_private, "/private",
0, &vppriv, &mppriv);
if (error) {
panic("nfs_mount_diskless private failed with %d\n", error);
}
printf("private on %s\n", (char *)&nd.nd_private.ndm_host);
vfs_unbusy(mppriv);
mount_list_add(mppriv);
}
#endif
if (nd.nd_root.ndm_path)
FREE_ZONE(nd.nd_root.ndm_path, MAXPATHLEN, M_NAMEI);
if (nd.nd_private.ndm_path)
FREE_ZONE(nd.nd_private.ndm_path, MAXPATHLEN, M_NAMEI);
error = nfs_getattr(vp, &nvattr, kauth_cred_get(), procp);
if (error) panic("nfs_mountroot: getattr for root");
return (0);
}
static int
nfs_mount_diskless(
struct nfs_dlmount *ndmntp,
const char *mntname,
int mntflag,
vnode_t *vpp,
mount_t *mpp)
{
struct user_nfs_args args;
mount_t mp;
mbuf_t m;
int error;
proc_t procp;
procp = current_proc();
if ((error = vfs_rootmountalloc("nfs", ndmntp->ndm_host, &mp))) {
printf("nfs_mount_diskless: NFS not configured");
return (error);
}
mp->mnt_flag |= mntflag;
if (!(mntflag & MNT_RDONLY))
mp->mnt_flag &= ~MNT_RDONLY;
bzero((caddr_t) &args, sizeof(args));
args.addr = CAST_USER_ADDR_T(&ndmntp->ndm_saddr);
args.addrlen = ndmntp->ndm_saddr.sin_len;
args.sotype = ndmntp->ndm_sotype;
args.fh = CAST_USER_ADDR_T(&ndmntp->ndm_fh[0]);
args.fhsize = ndmntp->ndm_fhlen;
args.hostname = CAST_USER_ADDR_T(ndmntp->ndm_host);
args.flags = NFSMNT_RESVPORT;
if (ndmntp->ndm_nfsv3)
args.flags |= NFSMNT_NFSV3;
error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_SONAME, &m);
if (error) {
printf("nfs_mount_diskless: mbuf_get(soname) failed");
return (error);
}
mbuf_setlen(m, ndmntp->ndm_saddr.sin_len);
bcopy((caddr_t)args.addr, mbuf_data(m), ndmntp->ndm_saddr.sin_len);
if ((error = mountnfs(&args, mp, m, procp, vpp))) {
printf("nfs_mountroot: mount %s failed: %d\n", mntname, error);
mount_list_lock();
mp->mnt_vtable->vfc_refcount--;
mount_list_unlock();
vfs_unbusy(mp);
mount_lock_destroy(mp);
FREE_ZONE(mp, sizeof(struct mount), M_MOUNT);
return (error);
}
*mpp = mp;
return (0);
}
#if !defined(NO_MOUNT_PRIVATE)
static int
nfs_mount_diskless_private(
struct nfs_dlmount *ndmntp,
const char *mntname,
int mntflag,
vnode_t *vpp,
mount_t *mpp)
{
struct user_nfs_args args;
mount_t mp;
mbuf_t m;
int error;
proc_t procp;
struct vfstable *vfsp;
struct nameidata nd;
vnode_t vp;
struct vfs_context context;
procp = current_proc();
context.vc_proc = procp;
context.vc_ucred = kauth_cred_get();
{
struct filedesc *fdp;
fdp = procp->p_fd;
mountlist.tqh_first->mnt_flag |= MNT_ROOTFS;
if (VFS_ROOT(mountlist.tqh_first, &rootvnode, NULL))
panic("cannot find root vnode");
error = vnode_ref(rootvnode);
if (error) {
printf("nfs_mountroot: vnode_ref() failed on root vnode!\n");
return (error);
}
fdp->fd_cdir = rootvnode;
fdp->fd_rdir = NULL;
}
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE32,
mntname, &context);
if ((error = namei(&nd))) {
printf("nfs_mountroot: private namei failed!\n");
return (error);
}
{
vnode_rele(rootvnode);
}
nameidone(&nd);
vp = nd.ni_vp;
if ((error = VNOP_FSYNC(vp, MNT_WAIT, &context)) ||
(error = buf_invalidateblks(vp, BUF_WRITE_DATA, 0, 0))) {
vnode_put(vp);
return (error);
}
if (vnode_vtype(vp) != VDIR) {
vnode_put(vp);
return (ENOTDIR);
}
for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next)
if (!strcmp(vfsp->vfc_name, "nfs"))
break;
if (vfsp == NULL) {
printf("nfs_mountroot: private NFS not configured\n");
vnode_put(vp);
return (ENODEV);
}
if (vnode_mountedhere(vp) != NULL) {
vnode_put(vp);
return (EBUSY);
}
mp = _MALLOC_ZONE((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK);
if (!mp) {
printf("nfs_mountroot: unable to allocate mount structure\n");
vnode_put(vp);
return (ENOMEM);
}
bzero((char *)mp, (u_long)sizeof(struct mount));
mp->mnt_maxreadcnt = mp->mnt_maxwritecnt = MAXPHYS;
mp->mnt_segreadcnt = mp->mnt_segwritecnt = 32;
mount_lock_init(mp);
TAILQ_INIT(&mp->mnt_vnodelist);
TAILQ_INIT(&mp->mnt_workerqueue);
TAILQ_INIT(&mp->mnt_newvnodes);
(void)vfs_busy(mp, LK_NOWAIT);
TAILQ_INIT(&mp->mnt_vnodelist);
mount_list_lock();
vfsp->vfc_refcount++;
mount_list_unlock();
mp->mnt_vtable = vfsp;
mp->mnt_op = vfsp->vfc_vfsops;
mp->mnt_flag = mntflag;
mp->mnt_flag |= vfsp->vfc_flags & MNT_VISFLAGMASK;
strncpy(mp->mnt_vfsstat.f_fstypename, vfsp->vfc_name, MFSNAMELEN-1);
vp->v_mountedhere = mp;
mp->mnt_vnodecovered = vp;
mp->mnt_vfsstat.f_owner = kauth_cred_getuid(kauth_cred_get());
(void) copystr(mntname, mp->mnt_vfsstat.f_mntonname, MNAMELEN - 1, 0);
(void) copystr(ndmntp->ndm_host, mp->mnt_vfsstat.f_mntfromname, MNAMELEN - 1, 0);
bzero((caddr_t) &args, sizeof(args));
args.addr = CAST_USER_ADDR_T(&ndmntp->ndm_saddr);
args.addrlen = ndmntp->ndm_saddr.sin_len;
args.sotype = ndmntp->ndm_sotype;
args.fh = CAST_USER_ADDR_T(ndmntp->ndm_fh);
args.fhsize = ndmntp->ndm_fhlen;
args.hostname = CAST_USER_ADDR_T(ndmntp->ndm_host);
args.flags = NFSMNT_RESVPORT;
if (ndmntp->ndm_nfsv3)
args.flags |= NFSMNT_NFSV3;
error = mbuf_get(MBUF_WAITOK, MBUF_TYPE_SONAME, &m);
if (error) {
printf("nfs_mount_diskless_private: mbuf_get(soname) failed");
return (error);
}
mbuf_setlen(m, ndmntp->ndm_saddr.sin_len);
bcopy((caddr_t)args.addr, mbuf_data(m), ndmntp->ndm_saddr.sin_len);
if ((error = mountnfs(&args, mp, m, procp, &vp))) {
printf("nfs_mountroot: mount %s failed: %d\n", mntname, error);
mount_list_lock();
vfsp->vfc_refcount--;
mount_list_unlock();
vfs_unbusy(mp);
mount_lock_destroy(mp);
FREE_ZONE(mp, sizeof (struct mount), M_MOUNT);
return (error);
}
*mpp = mp;
*vpp = vp;
return (0);
}
#endif
static int
nfs_mount(mount_t mp, vnode_t vp, user_addr_t data, vfs_context_t context)
{
proc_t p = vfs_context_proc(context);
int error, argsvers;
struct user_nfs_args args;
struct nfs_args tempargs;
mbuf_t nam;
size_t len;
u_char nfh[NFSX_V3FHMAX];
char *mntfrom;
error = copyin(data, (caddr_t)&argsvers, sizeof (argsvers));
if (error)
return (error);
switch (argsvers) {
case 3:
if (vfs_context_is64bit(context))
error = copyin(data, (caddr_t)&args, sizeof (struct user_nfs_args3));
else
error = copyin(data, (caddr_t)&tempargs, sizeof (struct nfs_args3));
break;
case 4:
if (vfs_context_is64bit(context))
error = copyin(data, (caddr_t)&args, sizeof (args));
else
error = copyin(data, (caddr_t)&tempargs, sizeof (tempargs));
break;
default:
return (EPROGMISMATCH);
}
if (error)
return (error);
if (!vfs_context_is64bit(context)) {
args.version = tempargs.version;
args.addrlen = tempargs.addrlen;
args.sotype = tempargs.sotype;
args.proto = tempargs.proto;
args.fhsize = tempargs.fhsize;
args.flags = tempargs.flags;
args.wsize = tempargs.wsize;
args.rsize = tempargs.rsize;
args.readdirsize = tempargs.readdirsize;
args.timeo = tempargs.timeo;
args.retrans = tempargs.retrans;
args.maxgrouplist = tempargs.maxgrouplist;
args.readahead = tempargs.readahead;
args.leaseterm = tempargs.leaseterm;
args.deadthresh = tempargs.deadthresh;
args.addr = CAST_USER_ADDR_T(tempargs.addr);
args.fh = CAST_USER_ADDR_T(tempargs.fh);
args.hostname = CAST_USER_ADDR_T(tempargs.hostname);
if (argsvers >= 4) {
args.acregmin = tempargs.acregmin;
args.acregmax = tempargs.acregmax;
args.acdirmin = tempargs.acdirmin;
args.acdirmax = tempargs.acdirmax;
}
}
if (args.fhsize < 0 || args.fhsize > NFSX_V3FHMAX)
return (EINVAL);
error = copyin(args.fh, (caddr_t)nfh, args.fhsize);
if (error)
return (error);
mntfrom = &vfs_statfs(mp)->f_mntfromname[0];
error = copyinstr(args.hostname, mntfrom, MAXPATHLEN-1, &len);
if (error)
return (error);
bzero(&mntfrom[len], MAXPATHLEN - len);
error = sockargs(&nam, args.addr, args.addrlen, MBUF_TYPE_SONAME);
if (error)
return (error);
args.fh = CAST_USER_ADDR_T(&nfh[0]);
error = mountnfs(&args, mp, nam, p, &vp);
return (error);
}
static int
mountnfs(
struct user_nfs_args *argp,
mount_t mp,
mbuf_t nam,
proc_t p,
vnode_t *vpp)
{
struct nfsmount *nmp;
struct nfsnode *np;
int error, maxio;
struct nfs_vattr nvattrs;
struct vfs_context context;
u_int64_t xid;
if (argp->sotype == SOCK_STREAM)
argp->flags &= ~NFSMNT_NOCONN;
if (vfs_flags(mp) & MNT_UPDATE) {
nmp = VFSTONFS(mp);
mbuf_freem(nam);
return (0);
} else {
MALLOC_ZONE(nmp, struct nfsmount *,
sizeof (struct nfsmount), M_NFSMNT, M_WAITOK);
if (!nmp) {
mbuf_freem(nam);
return (ENOMEM);
}
bzero((caddr_t)nmp, sizeof (struct nfsmount));
TAILQ_INIT(&nmp->nm_uidlruhead);
TAILQ_INIT(&nmp->nm_bufq);
vfs_setfsprivate(mp, nmp);
}
nmp->nm_timeo = NFS_TIMEO;
nmp->nm_retry = NFS_RETRANS;
if (argp->sotype == SOCK_DGRAM) {
nmp->nm_wsize = NFS_DGRAM_WSIZE;
nmp->nm_rsize = NFS_DGRAM_RSIZE;
} else {
nmp->nm_wsize = NFS_WSIZE;
nmp->nm_rsize = NFS_RSIZE;
}
nmp->nm_readdirsize = NFS_READDIRSIZE;
nmp->nm_numgrps = NFS_MAXGRPS;
nmp->nm_readahead = NFS_DEFRAHEAD;
nmp->nm_tprintf_delay = nfs_tprintf_delay;
if (nmp->nm_tprintf_delay < 0)
nmp->nm_tprintf_delay = 0;
nmp->nm_tprintf_initial_delay = nfs_tprintf_initial_delay;
if (nmp->nm_tprintf_initial_delay < 0)
nmp->nm_tprintf_initial_delay = 0;
nmp->nm_acregmin = NFS_MINATTRTIMO;
nmp->nm_acregmax = NFS_MAXATTRTIMO;
nmp->nm_acdirmin = NFS_MINDIRATTRTIMO;
nmp->nm_acdirmax = NFS_MAXDIRATTRTIMO;
vfs_getnewfsid(mp);
nmp->nm_mountp = mp;
vfs_setauthopaque(mp);
nmp->nm_flag = argp->flags;
nmp->nm_nam = nam;
if ((argp->flags & NFSMNT_TIMEO) && argp->timeo > 0) {
nmp->nm_timeo = (argp->timeo * NFS_HZ + 5) / 10;
if (nmp->nm_timeo < NFS_MINTIMEO)
nmp->nm_timeo = NFS_MINTIMEO;
else if (nmp->nm_timeo > NFS_MAXTIMEO)
nmp->nm_timeo = NFS_MAXTIMEO;
}
if ((argp->flags & NFSMNT_RETRANS) && argp->retrans > 1) {
nmp->nm_retry = argp->retrans;
if (nmp->nm_retry > NFS_MAXREXMIT)
nmp->nm_retry = NFS_MAXREXMIT;
}
if (argp->flags & NFSMNT_NFSV3) {
if (argp->sotype == SOCK_DGRAM)
maxio = NFS_MAXDGRAMDATA;
else
maxio = NFS_MAXDATA;
} else
maxio = NFS_V2MAXDATA;
if ((argp->flags & NFSMNT_WSIZE) && argp->wsize > 0) {
nmp->nm_wsize = argp->wsize;
nmp->nm_wsize &= ~(NFS_FABLKSIZE - 1);
if (nmp->nm_wsize <= 0)
nmp->nm_wsize = NFS_FABLKSIZE;
}
if (nmp->nm_wsize > maxio)
nmp->nm_wsize = maxio;
if (nmp->nm_wsize > MAXBSIZE)
nmp->nm_wsize = MAXBSIZE;
if ((argp->flags & NFSMNT_RSIZE) && argp->rsize > 0) {
nmp->nm_rsize = argp->rsize;
nmp->nm_rsize &= ~(NFS_FABLKSIZE - 1);
if (nmp->nm_rsize <= 0)
nmp->nm_rsize = NFS_FABLKSIZE;
}
if (nmp->nm_rsize > maxio)
nmp->nm_rsize = maxio;
if (nmp->nm_rsize > MAXBSIZE)
nmp->nm_rsize = MAXBSIZE;
if ((argp->flags & NFSMNT_READDIRSIZE) && argp->readdirsize > 0) {
nmp->nm_readdirsize = argp->readdirsize;
}
if (nmp->nm_readdirsize > maxio)
nmp->nm_readdirsize = maxio;
if (nmp->nm_readdirsize > nmp->nm_rsize)
nmp->nm_readdirsize = nmp->nm_rsize;
if ((argp->flags & NFSMNT_MAXGRPS) && argp->maxgrouplist >= 0 &&
argp->maxgrouplist <= NFS_MAXGRPS)
nmp->nm_numgrps = argp->maxgrouplist;
if ((argp->flags & NFSMNT_READAHEAD) && argp->readahead >= 0 &&
argp->readahead <= NFS_MAXRAHEAD)
nmp->nm_readahead = argp->readahead;
if (argp->version >= 4) {
if ((argp->flags & NFSMNT_ACREGMIN) && argp->acregmin >= 0)
nmp->nm_acregmin = argp->acregmin;
if ((argp->flags & NFSMNT_ACREGMAX) && argp->acregmax >= 0)
nmp->nm_acregmax = argp->acregmax;
if ((argp->flags & NFSMNT_ACDIRMIN) && argp->acdirmin >= 0)
nmp->nm_acdirmin = argp->acdirmin;
if ((argp->flags & NFSMNT_ACDIRMAX) && argp->acdirmax >= 0)
nmp->nm_acdirmax = argp->acdirmax;
if (nmp->nm_acregmin > nmp->nm_acregmax)
nmp->nm_acregmin = nmp->nm_acregmax;
if (nmp->nm_acdirmin > nmp->nm_acdirmax)
nmp->nm_acdirmin = nmp->nm_acdirmax;
}
nmp->nm_sotype = argp->sotype;
nmp->nm_soproto = argp->proto;
if (!nfs_mbuf_mlen)
nfs_mbuf_init();
if (nmp->nm_sotype == SOCK_DGRAM &&
(error = nfs_connect(nmp, (struct nfsreq *)0)))
goto bad;
error = nfs_getattr_no_vnode(mp, CAST_DOWN(caddr_t, argp->fh), argp->fhsize,
proc_ucred(p), p, &nvattrs, &xid);
if (error) {
goto bad;
}
error = nfs_nget(mp, NULL, NULL, CAST_DOWN(caddr_t, argp->fh), argp->fhsize,
&nvattrs, &xid, NG_MARKROOT, &np);
if (error)
goto bad;
nmp->nm_dvp = *vpp = NFSTOV(np);
error = vnode_ref(*vpp);
if (error) {
vnode_put(*vpp);
goto bad;
}
vnode_put(*vpp);
if (nmp->nm_flag & NFSMNT_NFSV3)
nfs_fsinfo(nmp, *vpp, proc_ucred(p), p);
vfs_statfs(mp)->f_iosize = nfs_iosize(nmp);
if (nmp->nm_flag & NFSMNT_NFSV3)
vfs_setauthopaqueaccess(mp);
context.vc_proc = p;
context.vc_ucred = proc_ucred(p);
nfs_statfs(mp, vfs_statfs(mp), &context);
if (nmp->nm_flag & NFSMNT_RESVPORT)
nfs_resv_mounts++;
nmp->nm_state |= NFSSTA_MOUNTED;
return (0);
bad:
nfs_disconnect(nmp);
FREE_ZONE((caddr_t)nmp, sizeof (struct nfsmount), M_NFSMNT);
mbuf_freem(nam);
return (error);
}
static int
nfs_unmount(
mount_t mp,
int mntflags,
__unused vfs_context_t context)
{
register struct nfsmount *nmp;
vnode_t vp;
int error, flags = 0;
nmp = VFSTONFS(mp);
if (mntflags & MNT_FORCE) {
flags |= FORCECLOSE;
nmp->nm_state |= NFSSTA_FORCE;
nmp->nm_flag |= NFSMNT_SOFT;
}
vp = nmp->nm_dvp;
error = vflush(mp, vp, SKIPSWAP | flags);
if (mntflags & MNT_FORCE) {
error = vflush(mp, NULLVP, flags);
} else {
if (vnode_isinuse(vp, 1))
return (EBUSY);
error = vflush(mp, vp, flags);
}
if (error)
return (error);
nmp->nm_state &= ~NFSSTA_MOUNTED;
if (nmp->nm_flag & NFSMNT_RESVPORT) {
if (--nfs_resv_mounts == 0)
nfs_bind_resv_thread_wake();
}
vnode_rele(vp);
(void)vflush(mp, NULLVP, FORCECLOSE);
vfs_setfsprivate(mp, 0);
nfs_disconnect(nmp);
mbuf_freem(nmp->nm_nam);
if ((nmp->nm_flag & NFSMNT_KERB) == 0) {
struct nfsreq *rp;
for (rp = nfs_reqq.tqh_first; rp; rp = rp->r_chain.tqe_next)
if (rp->r_nmp == nmp)
rp->r_nmp = (struct nfsmount *)0;
if (nmp->nm_state & NFSSTA_WANTRCV) {
nmp->nm_state &= ~NFSSTA_WANTRCV;
wakeup(&nmp->nm_state);
}
FREE_ZONE((caddr_t)nmp, sizeof (struct nfsmount), M_NFSMNT);
}
return (0);
}
static int
nfs_root(mount_t mp, vnode_t *vpp, __unused vfs_context_t context)
{
vnode_t vp;
struct nfsmount *nmp;
int error;
u_long vpid;
nmp = VFSTONFS(mp);
vp = nmp->nm_dvp;
vpid = vnode_vid(vp);
while ((error = vnode_getwithvid(vp, vpid))) {
if ((error != ENOENT) || (vnode_vid(vp) == vpid))
return (error);
vpid = vnode_vid(vp);
}
*vpp = vp;
return (0);
}
struct nfs_sync_cargs {
vfs_context_t context;
int waitfor;
int error;
};
static int
nfs_sync_callout(vnode_t vp, void *arg)
{
struct nfs_sync_cargs *cargs = (struct nfs_sync_cargs*)arg;
int error;
if (LIST_EMPTY(&VTONFS(vp)->n_dirtyblkhd))
return (VNODE_RETURNED);
if (VTONFS(vp)->n_flag & NWRBUSY)
return (VNODE_RETURNED);
error = nfs_flush(vp, cargs->waitfor,
vfs_context_ucred(cargs->context),
vfs_context_proc(cargs->context), 0);
if (error)
cargs->error = error;
return (VNODE_RETURNED);
}
static int
nfs_sync(mount_t mp, int waitfor, vfs_context_t context)
{
struct nfs_sync_cargs cargs;
cargs.waitfor = waitfor;
cargs.context = context;
cargs.error = 0;
vnode_iterate(mp, 0, nfs_sync_callout, &cargs);
return (cargs.error);
}
static int
nfs_vget(
__unused mount_t mp,
__unused ino64_t ino,
__unused vnode_t *vpp,
__unused vfs_context_t context)
{
return (ENOTSUP);
}
static int
nfs_fhtovp(
__unused mount_t mp,
__unused int fhlen,
__unused unsigned char *fhp,
__unused vnode_t *vpp,
__unused vfs_context_t context)
{
return (ENOTSUP);
}
static int
nfs_vptofh(
__unused vnode_t vp,
__unused int *fhlenp,
__unused unsigned char *fhp,
__unused vfs_context_t context)
{
return (ENOTSUP);
}
static int
nfs_start(
__unused mount_t mp,
__unused int flags,
__unused vfs_context_t context)
{
return (0);
}
static int
nfs_sysctl(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp,
user_addr_t newp, size_t newlen, vfs_context_t context)
{
int error = 0, val;
struct sysctl_req *req = NULL;
struct vfsidctl vc;
struct user_vfsidctl user_vc;
mount_t mp;
struct nfsmount *nmp = NULL;
struct vfsquery vq;
boolean_t is_64_bit;
if(namelen > 1)
return ENOTDIR;
is_64_bit = vfs_context_is64bit(context);
switch (name[0]) {
case VFS_CTL_TIMEO:
case VFS_CTL_QUERY:
case VFS_CTL_NOLOCKS:
req = CAST_DOWN(struct sysctl_req *, oldp);
if (is_64_bit) {
error = SYSCTL_IN(req, &user_vc, sizeof(user_vc));
if (error)
return (error);
mp = vfs_getvfs(&user_vc.vc_fsid);
}
else {
error = SYSCTL_IN(req, &vc, sizeof(vc));
if (error)
return (error);
mp = vfs_getvfs(&vc.vc_fsid);
}
if (mp == NULL)
return (ENOENT);
nmp = VFSTONFS(mp);
if (nmp == NULL)
return (ENOENT);
bzero(&vq, sizeof(vq));
req->newidx = 0;
if (is_64_bit) {
req->newptr = user_vc.vc_ptr;
req->newlen = (size_t)user_vc.vc_len;
}
else {
req->newptr = CAST_USER_ADDR_T(vc.vc_ptr);
req->newlen = vc.vc_len;
}
}
switch(name[0]) {
case NFS_NFSSTATS:
if(!oldp) {
*oldlenp = sizeof nfsstats;
return 0;
}
if(*oldlenp < sizeof nfsstats) {
*oldlenp = sizeof nfsstats;
return ENOMEM;
}
error = copyout(&nfsstats, oldp, sizeof nfsstats);
if (error)
return (error);
if(newp && newlen != sizeof nfsstats)
return EINVAL;
if(newp) {
return copyin(newp, &nfsstats, sizeof nfsstats);
}
return 0;
case VFS_CTL_NOLOCKS:
val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0;
if (req->oldptr != USER_ADDR_NULL) {
error = SYSCTL_OUT(req, &val, sizeof(val));
if (error)
return (error);
}
if (req->newptr != USER_ADDR_NULL) {
error = SYSCTL_IN(req, &val, sizeof(val));
if (error)
return (error);
if (val)
nmp->nm_flag |= NFSMNT_NOLOCKS;
else
nmp->nm_flag &= ~NFSMNT_NOLOCKS;
}
break;
case VFS_CTL_QUERY:
if (nmp->nm_state & NFSSTA_TIMEO)
vq.vq_flags |= VQ_NOTRESP;
if (!(nmp->nm_flag & NFSMNT_NOLOCKS) &&
(nmp->nm_state & NFSSTA_LOCKTIMEO))
vq.vq_flags |= VQ_NOTRESPLOCK;
error = SYSCTL_OUT(req, &vq, sizeof(vq));
break;
case VFS_CTL_TIMEO:
if (req->oldptr != USER_ADDR_NULL) {
error = SYSCTL_OUT(req, &nmp->nm_tprintf_initial_delay,
sizeof(nmp->nm_tprintf_initial_delay));
if (error)
return (error);
}
if (req->newptr != USER_ADDR_NULL) {
error = SYSCTL_IN(req, &nmp->nm_tprintf_initial_delay,
sizeof(nmp->nm_tprintf_initial_delay));
if (error)
return (error);
if (nmp->nm_tprintf_initial_delay < 0)
nmp->nm_tprintf_initial_delay = 0;
}
break;
default:
return (ENOTSUP);
}
return (error);
}