#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 <sys/quota.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/krpc.h>
#include <nfs/nfsproto.h>
#include <nfs/nfs.h>
#include <nfs/nfsnode.h>
#include <nfs/nfs_gss.h>
#include <nfs/nfsmount.h>
#include <nfs/xdr_subs.h>
#include <nfs/nfsm_subs.h>
#include <nfs/nfsdiskless.h>
#include <nfs/nfs_lock.h>
#if CONFIG_MACF
#include <security/mac_framework.h>
#endif
#include <pexpert/pexpert.h>
int nfs_ticks;
static lck_grp_t *nfs_mount_grp;
uint32_t nfs_fs_attr_bitmap[NFS_ATTR_BITMAP_LEN];
uint32_t nfs_object_attr_bitmap[NFS_ATTR_BITMAP_LEN];
uint32_t nfs_getattr_bitmap[NFS_ATTR_BITMAP_LEN];
struct nfs_reqqhead nfs_reqq;
lck_grp_t *nfs_request_grp;
lck_mtx_t *nfs_request_mutex;
thread_call_t nfs_request_timer_call;
int nfs_request_timer_on;
u_long nfs_xid = 0;
u_long nfs_xidwrap = 0;
thread_call_t nfs_buf_timer_call;
lck_grp_t *nfsiod_lck_grp;
lck_mtx_t *nfsiod_mutex;
struct nfsiodlist nfsiodfree, nfsiodwork;
struct nfsiodmountlist nfsiodmounts;
int nfsiod_thread_count = 0;
int nfsiod_thread_max = NFS_DEFASYNCTHREAD;
int nfs_max_async_writes = NFS_DEFMAXASYNCWRITES;
int nfs_iosize = NFS_IOSIZE;
int nfs_access_cache_timeout = NFS_MAXATTRTIMO;
int nfs_allow_async = 0;
int nfs_statfs_rate_limit = NFS_DEFSTATFSRATELIMIT;
int nfs_lockd_mounts = 0;
int nfs_lockd_request_sent = 0;
int nfs_tprintf_initial_delay = NFS_TPRINTF_INITIAL_DELAY;
int nfs_tprintf_delay = NFS_TPRINTF_DELAY;
static int mountnfs(struct user_nfs_args *,mount_t,mbuf_t,vfs_context_t,vnode_t *);
static int nfs_mount_diskless(struct nfs_dlmount *, const char *, int, vnode_t *, mount_t *, vfs_context_t);
#if !defined(NO_MOUNT_PRIVATE)
static int nfs_mount_diskless_private(struct nfs_dlmount *, const char *, int, vnode_t *, mount_t *, vfs_context_t);
#endif
static int nfs_vfs_mount(mount_t, vnode_t, user_addr_t, vfs_context_t);
static int nfs_vfs_start(mount_t, int, vfs_context_t);
static int nfs_vfs_unmount(mount_t, int, vfs_context_t);
static int nfs_vfs_root(mount_t, vnode_t *, vfs_context_t);
static int nfs_vfs_quotactl(mount_t, int, uid_t, caddr_t, vfs_context_t);
static int nfs_vfs_getattr(mount_t, struct vfs_attr *, vfs_context_t);
static int nfs_vfs_sync(mount_t, int, vfs_context_t);
static int nfs_vfs_vget(mount_t, ino64_t, vnode_t *, vfs_context_t);
static int nfs_vfs_vptofh(vnode_t, int *, unsigned char *, vfs_context_t);
static int nfs_vfs_fhtovp(mount_t, int, unsigned char *, vnode_t *, vfs_context_t);
static int nfs_vfs_init(struct vfsconf *);
static int nfs_vfs_sysctl(int *, u_int, user_addr_t, size_t *, user_addr_t, size_t, vfs_context_t);
struct vfsops nfs_vfsops = {
nfs_vfs_mount,
nfs_vfs_start,
nfs_vfs_unmount,
nfs_vfs_root,
nfs_vfs_quotactl,
nfs_vfs_getattr,
nfs_vfs_sync,
nfs_vfs_vget,
nfs_vfs_fhtovp,
nfs_vfs_vptofh,
nfs_vfs_init,
nfs_vfs_sysctl,
NULL,
{ NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL }
};
static int nfs3_mount(struct nfsmount *, vfs_context_t, struct user_nfs_args *, nfsnode_t *);
static int nfs4_mount(struct nfsmount *, vfs_context_t, struct user_nfs_args *, nfsnode_t *);
static int nfs3_update_statfs(struct nfsmount *, vfs_context_t);
static int nfs4_update_statfs(struct nfsmount *, vfs_context_t);
#if !QUOTA
#define nfs3_getquota NULL
#define nfs4_getquota NULL
#else
static int nfs3_getquota(struct nfsmount *, vfs_context_t, u_long, int, struct dqblk *);
static int nfs4_getquota(struct nfsmount *, vfs_context_t, u_long, int, struct dqblk *);
#endif
struct nfs_funcs nfs3_funcs = {
nfs3_mount,
nfs3_update_statfs,
nfs3_getquota,
nfs3_access_rpc,
nfs3_getattr_rpc,
nfs3_setattr_rpc,
nfs3_read_rpc_async,
nfs3_read_rpc_async_finish,
nfs3_readlink_rpc,
nfs3_write_rpc_async,
nfs3_write_rpc_async_finish,
nfs3_commit_rpc,
nfs3_lookup_rpc_async,
nfs3_lookup_rpc_async_finish,
nfs3_remove_rpc,
nfs3_rename_rpc
};
struct nfs_funcs nfs4_funcs = {
nfs4_mount,
nfs4_update_statfs,
nfs4_getquota,
nfs4_access_rpc,
nfs4_getattr_rpc,
nfs4_setattr_rpc,
nfs4_read_rpc_async,
nfs4_read_rpc_async_finish,
nfs4_readlink_rpc,
nfs4_write_rpc_async,
nfs4_write_rpc_async_finish,
nfs4_commit_rpc,
nfs4_lookup_rpc_async,
nfs4_lookup_rpc_async_finish,
nfs4_remove_rpc,
nfs4_rename_rpc
};
static int
nfs_vfs_init(struct vfsconf *vfsp)
{
int i;
if (sizeof (struct nfsnode) > NFS_NODEALLOC) {
printf("struct nfsnode bloated (> %dbytes)\n", NFS_NODEALLOC);
printf("Try reducing NFS_SMALLFH\n");
}
if (sizeof (struct nfsmount) > NFS_MNTALLOC) {
printf("struct nfsmount bloated (> %dbytes)\n", NFS_MNTALLOC);
printf("Try reducing NFS_MUIDHASHSIZ\n");
}
nfs_ticks = (hz * NFS_TICKINTVL + 500) / 1000;
if (nfs_ticks < 1)
nfs_ticks = 1;
TAILQ_INIT(&nfsiodfree);
TAILQ_INIT(&nfsiodwork);
TAILQ_INIT(&nfsiodmounts);
nfsiod_lck_grp = lck_grp_alloc_init("nfsiod", LCK_GRP_ATTR_NULL);
nfsiod_mutex = lck_mtx_alloc_init(nfsiod_lck_grp, LCK_ATTR_NULL);
nfs_mount_grp = lck_grp_alloc_init("nfs_mount", LCK_GRP_ATTR_NULL);
nfs_request_grp = lck_grp_alloc_init("nfs_request", LCK_GRP_ATTR_NULL);
nfs_request_mutex = lck_mtx_alloc_init(nfs_request_grp, LCK_ATTR_NULL);
TAILQ_INIT(&nfs_reqq);
nfs_nbinit();
nfs_nhinit();
nfs_lockinit();
nfs_gss_init();
NFS4_PER_FS_ATTRIBUTES(nfs_fs_attr_bitmap);
NFS4_PER_OBJECT_ATTRIBUTES(nfs_object_attr_bitmap);
NFS4_DEFAULT_ATTRIBUTES(nfs_getattr_bitmap);
for (i=0; i < NFS_ATTR_BITMAP_LEN; i++)
nfs_getattr_bitmap[i] &= nfs_object_attr_bitmap[i];
nfs_request_timer_call = thread_call_allocate(nfs_request_timer, NULL);
nfs_buf_timer_call = thread_call_allocate(nfs_buf_timer, NULL);
vfsp->vfc_refcount++;
return (0);
}
static int
nfs3_update_statfs(struct nfsmount *nmp, vfs_context_t ctx)
{
nfsnode_t np;
int error = 0, lockerror, status, nfsvers;
u_int64_t xid;
struct nfsm_chain nmreq, nmrep;
uint32_t val;
nfsvers = nmp->nm_vers;
np = nmp->nm_dnp;
if ((error = vnode_get(NFSTOV(np))))
return(error);
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
nfsm_chain_build_alloc_init(error, &nmreq, NFSX_FH(nfsvers));
nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
nfsm_chain_build_done(error, &nmreq);
nfsmout_if(error);
error = nfs_request(np, NULL, &nmreq, NFSPROC_FSSTAT, ctx,
&nmrep, &xid, &status);
if ((lockerror = nfs_lock(np, NFS_NODE_LOCK_EXCLUSIVE)))
error = lockerror;
if (nfsvers == NFS_VER3)
nfsm_chain_postop_attr_update(error, &nmrep, np, &xid);
if (!lockerror)
nfs_unlock(np);
if (!error)
error = status;
nfsm_assert(error, NFSTONMP(np), ENXIO);
nfsmout_if(error);
lck_mtx_lock(&nmp->nm_lock);
NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SPACE_TOTAL);
NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SPACE_FREE);
NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SPACE_AVAIL);
if (nfsvers == NFS_VER3) {
NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_FILES_AVAIL);
NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_FILES_TOTAL);
NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_FILES_FREE);
nmp->nm_fsattr.nfsa_bsize = NFS_FABLKSIZE;
nfsm_chain_get_64(error, &nmrep, nmp->nm_fsattr.nfsa_space_total);
nfsm_chain_get_64(error, &nmrep, nmp->nm_fsattr.nfsa_space_free);
nfsm_chain_get_64(error, &nmrep, nmp->nm_fsattr.nfsa_space_avail);
nfsm_chain_get_64(error, &nmrep, nmp->nm_fsattr.nfsa_files_total);
nfsm_chain_get_64(error, &nmrep, nmp->nm_fsattr.nfsa_files_free);
nfsm_chain_get_64(error, &nmrep, nmp->nm_fsattr.nfsa_files_avail);
} else {
nfsm_chain_adv(error, &nmrep, NFSX_UNSIGNED); nfsm_chain_get_32(error, &nmrep, nmp->nm_fsattr.nfsa_bsize);
nfsm_chain_get_32(error, &nmrep, val);
nfsmout_if(error);
if (nmp->nm_fsattr.nfsa_bsize <= 0)
nmp->nm_fsattr.nfsa_bsize = NFS_FABLKSIZE;
nmp->nm_fsattr.nfsa_space_total = (uint64_t)val * nmp->nm_fsattr.nfsa_bsize;
nfsm_chain_get_32(error, &nmrep, val);
nfsmout_if(error);
nmp->nm_fsattr.nfsa_space_free = (uint64_t)val * nmp->nm_fsattr.nfsa_bsize;
nfsm_chain_get_32(error, &nmrep, val);
nfsmout_if(error);
nmp->nm_fsattr.nfsa_space_avail = (uint64_t)val * nmp->nm_fsattr.nfsa_bsize;
}
lck_mtx_unlock(&nmp->nm_lock);
nfsmout:
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
vnode_put(NFSTOV(np));
return (error);
}
static int
nfs4_update_statfs(struct nfsmount *nmp, vfs_context_t ctx)
{
nfsnode_t np;
int error = 0, lockerror, status, nfsvers, numops;
u_int64_t xid;
struct nfsm_chain nmreq, nmrep;
uint32_t bitmap[NFS_ATTR_BITMAP_LEN];
struct nfs_vattr nvattr;
nfsvers = nmp->nm_vers;
np = nmp->nm_dnp;
if ((error = vnode_get(NFSTOV(np))))
return(error);
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
numops = 2;
nfsm_chain_build_alloc_init(error, &nmreq, 15 * NFSX_UNSIGNED);
nfsm_chain_add_compound_header(error, &nmreq, "statfs", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
NFS_COPY_ATTRIBUTES(nfs_getattr_bitmap, bitmap);
NFS4_STATFS_ATTRIBUTES(bitmap);
nfsm_chain_add_bitmap_masked(error, &nmreq, bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
error = nfs_request(np, NULL, &nmreq, NFSPROC4_COMPOUND, ctx, &nmrep, &xid, &status);
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsm_assert(error, NFSTONMP(np), ENXIO);
nfsmout_if(error);
lck_mtx_lock(&nmp->nm_lock);
NFS_CLEAR_ATTRIBUTES(nvattr.nva_bitmap);
error = nfs4_parsefattr(&nmrep, &nmp->nm_fsattr, &nvattr, NULL, NULL);
lck_mtx_unlock(&nmp->nm_lock);
nfsmout_if(error);
if ((lockerror = nfs_lock(np, NFS_NODE_LOCK_EXCLUSIVE)))
error = lockerror;
if (!error)
nfs_loadattrcache(np, &nvattr, &xid, 0);
if (!lockerror)
nfs_unlock(np);
nfsm_assert(error, NFSTONMP(np), ENXIO);
nfsmout_if(error);
nmp->nm_fsattr.nfsa_bsize = NFS_FABLKSIZE;
nfsmout:
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
vnode_put(NFSTOV(np));
return (error);
}
static int
nfs_vfs_getattr(mount_t mp, struct vfs_attr *fsap, vfs_context_t ctx)
{
struct nfsmount *nmp;
uint32_t bsize;
int error = 0, nfsvers;
if (!(nmp = VFSTONFS(mp)))
return (ENXIO);
nfsvers = nmp->nm_vers;
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)) {
int statfsrate = nfs_statfs_rate_limit;
int refresh = 1;
if ((statfsrate > 0) && (statfsrate < 1000000)) {
struct timeval now;
uint32_t stamp;
microuptime(&now);
lck_mtx_lock(&nmp->nm_lock);
stamp = (now.tv_sec * statfsrate) + (now.tv_usec / (1000000/statfsrate));
if (stamp != nmp->nm_fsattrstamp) {
refresh = 1;
nmp->nm_fsattrstamp = stamp;
} else {
refresh = 0;
}
lck_mtx_unlock(&nmp->nm_lock);
}
if (refresh)
error = nmp->nm_funcs->nf_update_statfs(nmp, ctx);
if ((error == ESTALE) || (error == ETIMEDOUT))
error = 0;
if (error)
return (error);
lck_mtx_lock(&nmp->nm_lock);
VFSATTR_RETURN(fsap, f_iosize, nfs_iosize);
VFSATTR_RETURN(fsap, f_bsize, nmp->nm_fsattr.nfsa_bsize);
bsize = nmp->nm_fsattr.nfsa_bsize;
if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SPACE_TOTAL))
VFSATTR_RETURN(fsap, f_blocks, nmp->nm_fsattr.nfsa_space_total / bsize);
if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SPACE_FREE))
VFSATTR_RETURN(fsap, f_bfree, nmp->nm_fsattr.nfsa_space_free / bsize);
if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SPACE_AVAIL))
VFSATTR_RETURN(fsap, f_bavail, nmp->nm_fsattr.nfsa_space_avail / bsize);
if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SPACE_TOTAL) &&
NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SPACE_FREE))
VFSATTR_RETURN(fsap, f_bused,
(nmp->nm_fsattr.nfsa_space_total / bsize) -
(nmp->nm_fsattr.nfsa_space_free / bsize));
if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_FILES_TOTAL))
VFSATTR_RETURN(fsap, f_files, nmp->nm_fsattr.nfsa_files_total);
if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_FILES_FREE))
VFSATTR_RETURN(fsap, f_ffree, nmp->nm_fsattr.nfsa_files_free);
lck_mtx_unlock(&nmp->nm_lock);
}
if (VFSATTR_IS_ACTIVE(fsap, f_capabilities)) {
u_int32_t caps, valid;
nfsnode_t np;
nfsm_assert(error, VFSTONFS(mp), ENXIO);
if (error)
return (error);
np = nmp->nm_dnp;
lck_mtx_lock(&nmp->nm_lock);
caps = valid = 0;
if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SYMLINK_SUPPORT)) {
valid |= VOL_CAP_FMT_SYMBOLICLINKS;
if (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_SYMLINK)
caps |= VOL_CAP_FMT_SYMBOLICLINKS;
}
if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_LINK_SUPPORT)) {
valid |= VOL_CAP_FMT_HARDLINKS;
if (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_LINK)
caps |= VOL_CAP_FMT_HARDLINKS;
}
if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_CASE_INSENSITIVE)) {
valid |= VOL_CAP_FMT_CASE_SENSITIVE;
if (!(nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_CASE_INSENSITIVE))
caps |= VOL_CAP_FMT_CASE_SENSITIVE;
}
if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_CASE_PRESERVING)) {
valid |= VOL_CAP_FMT_CASE_PRESERVING;
if (nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_CASE_PRESERVING)
caps |= VOL_CAP_FMT_CASE_PRESERVING;
}
if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXFILESIZE)) {
if (nmp->nm_fsattr.nfsa_maxfilesize >= 0x20000000000ULL)
caps |= VOL_CAP_FMT_2TB_FILESIZE;
} else if (nfsvers >= NFS_VER3) {
caps |= VOL_CAP_FMT_2TB_FILESIZE;
}
if (nfsvers >= NFS_VER4) {
caps |= VOL_CAP_FMT_HIDDEN_FILES;
valid |= VOL_CAP_FMT_HIDDEN_FILES;
}
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 (nfsvers >= NFS_VER4) {
caps = VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK;
valid = VOL_CAP_INT_ADVLOCK | VOL_CAP_INT_FLOCK;
} else if ((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);
lck_mtx_unlock(&nmp->nm_lock);
}
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);
}
static int
nfs3_fsinfo(struct nfsmount *nmp, nfsnode_t np, vfs_context_t ctx)
{
int error = 0, lockerror, status, prefsize, maxsize, nmlocked = 0;
u_int64_t xid;
uint32_t val;
struct nfsm_chain nmreq, nmrep;
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
nfsm_chain_build_alloc_init(error, &nmreq, NFSX_FH(nmp->nm_vers));
nfsm_chain_add_fh(error, &nmreq, nmp->nm_vers, np->n_fhp, np->n_fhsize);
nfsm_chain_build_done(error, &nmreq);
nfsmout_if(error);
error = nfs_request(np, NULL, &nmreq, NFSPROC_FSINFO, ctx,
&nmrep, &xid, &status);
if ((lockerror = nfs_lock(np, NFS_NODE_LOCK_EXCLUSIVE)))
error = lockerror;
nfsm_chain_postop_attr_update(error, &nmrep, np, &xid);
if (!lockerror)
nfs_unlock(np);
if (!error)
error = status;
nfsmout_if(error);
lck_mtx_lock(&nmp->nm_lock);
nmlocked = 1;
nfsm_chain_get_32(error, &nmrep, maxsize);
nfsm_chain_get_32(error, &nmrep, prefsize);
nfsmout_if(error);
nmp->nm_fsattr.nfsa_maxread = maxsize;
if (prefsize < nmp->nm_rsize)
nmp->nm_rsize = (prefsize + NFS_FABLKSIZE - 1) &
~(NFS_FABLKSIZE - 1);
if (maxsize < nmp->nm_rsize) {
nmp->nm_rsize = maxsize & ~(NFS_FABLKSIZE - 1);
if (nmp->nm_rsize == 0)
nmp->nm_rsize = maxsize;
}
nfsm_chain_adv(error, &nmrep, NFSX_UNSIGNED);
nfsm_chain_get_32(error, &nmrep, maxsize);
nfsm_chain_get_32(error, &nmrep, prefsize);
nfsmout_if(error);
nmp->nm_fsattr.nfsa_maxwrite = maxsize;
if (prefsize < nmp->nm_wsize)
nmp->nm_wsize = (prefsize + NFS_FABLKSIZE - 1) &
~(NFS_FABLKSIZE - 1);
if (maxsize < nmp->nm_wsize) {
nmp->nm_wsize = maxsize & ~(NFS_FABLKSIZE - 1);
if (nmp->nm_wsize == 0)
nmp->nm_wsize = maxsize;
}
nfsm_chain_adv(error, &nmrep, NFSX_UNSIGNED);
nfsm_chain_get_32(error, &nmrep, prefsize);
nfsmout_if(error);
if (prefsize < nmp->nm_readdirsize)
nmp->nm_readdirsize = prefsize;
if (maxsize < nmp->nm_readdirsize)
nmp->nm_readdirsize = maxsize;
nfsm_chain_get_64(error, &nmrep, maxsize);
nmp->nm_fsattr.nfsa_maxfilesize = maxsize;
nfsm_chain_adv(error, &nmrep, 2 * NFSX_UNSIGNED);
nfsm_chain_get_32(error, &nmrep, val);
nfsmout_if(error);
if (val & NFSV3FSINFO_LINK)
nmp->nm_fsattr.nfsa_flags |= NFS_FSFLAG_LINK;
if (val & NFSV3FSINFO_SYMLINK)
nmp->nm_fsattr.nfsa_flags |= NFS_FSFLAG_SYMLINK;
if (val & NFSV3FSINFO_HOMOGENEOUS)
nmp->nm_fsattr.nfsa_flags |= NFS_FSFLAG_HOMOGENEOUS;
if (val & NFSV3FSINFO_CANSETTIME)
nmp->nm_fsattr.nfsa_flags |= NFS_FSFLAG_SET_TIME;
nmp->nm_state |= NFSSTA_GOTFSINFO;
NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXREAD);
NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXWRITE);
NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXFILESIZE);
NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_LINK_SUPPORT);
NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_SYMLINK_SUPPORT);
NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_HOMOGENEOUS);
NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_CANSETTIME);
nfsmout:
if (nmlocked)
lck_mtx_unlock(&nmp->nm_lock);
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
return (error);
}
int
nfs_mountroot(void)
{
struct nfs_diskless nd;
struct nfs_vattr nvattr;
mount_t mp = NULL;
vnode_t vp = NULL;
vfs_context_t ctx;
int error;
#if !defined(NO_MOUNT_PRIVATE)
mount_t mppriv = NULL;
vnode_t vppriv = NULL;
#endif
int v3, sotype;
bzero((caddr_t) &nd, sizeof(nd));
error = nfs_boot_init(&nd);
if (error) {
panic("nfs_boot_init failed with %d\n", error);
}
v3 = 1;
sotype = SOCK_STREAM;
tryagain:
error = nfs_boot_getfh(&nd, 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;
}
switch(error) {
case EPROGUNAVAIL:
panic("nfs_boot_getfh(v2,UDP) failed: NFS server mountd not responding - check server configuration: %s", PE_boot_args());
case EACCES:
case EPERM:
panic("nfs_boot_getfh(v2,UDP) failed: NFS server refused mount - check server configuration: %s", PE_boot_args());
default:
panic("nfs_boot_getfh(v2,UDP) failed with %d: %s", error, PE_boot_args());
}
}
ctx = vfs_context_kernel();
#if !defined(NO_MOUNT_PRIVATE)
{
int rw_root=0;
int flags = MNT_ROOTFS|MNT_RDONLY;
PE_parse_boot_arg("-rwroot_hack", &rw_root);
if(rw_root)
{
flags = MNT_ROOTFS;
kprintf("-rwroot_hack in effect: mounting root fs read/write\n");
}
if ((error = nfs_mount_diskless(&nd.nd_root, "/", flags, &vp, &mp, ctx)))
#else
if ((error = nfs_mount_diskless(&nd.nd_root, "/", MNT_ROOTFS, &vp, &mp, ctx)))
#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: %s\n", error, PE_boot_args());
}
}
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, ctx);
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(VTONFS(vp), &nvattr, ctx, 0);
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,
vfs_context_t ctx)
{
struct user_nfs_args args;
mount_t mp;
mbuf_t m;
int error;
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(&ndmntp->ndm_saddr, mbuf_data(m), ndmntp->ndm_saddr.sin_len);
if ((error = mountnfs(&args, mp, m, ctx, 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);
#if CONFIG_MACF
mac_mount_label_destroy(mp);
#endif
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,
vfs_context_t ctx)
{
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;
procp = current_proc();
{
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");
goto out;
}
fdp->fd_cdir = rootvnode;
fdp->fd_rdir = NULL;
}
NDINIT(&nd, LOOKUP, FOLLOW | LOCKLEAF, UIO_SYSSPACE32,
CAST_USER_ADDR_T(mntname), ctx);
if ((error = namei(&nd))) {
printf("nfs_mountroot: private namei failed!\n");
goto out;
}
{
vnode_rele(rootvnode);
}
nameidone(&nd);
vp = nd.ni_vp;
if ((error = VNOP_FSYNC(vp, MNT_WAIT, ctx)) ||
(error = buf_invalidateblks(vp, BUF_WRITE_DATA, 0, 0))) {
vnode_put(vp);
goto out;
}
if (vnode_vtype(vp) != VDIR) {
vnode_put(vp);
error = ENOTDIR;
goto out;
}
for (vfsp = vfsconf; vfsp; vfsp = vfsp->vfc_next)
if (!strncmp(vfsp->vfc_name, "nfs", sizeof(vfsp->vfc_name)))
break;
if (vfsp == NULL) {
printf("nfs_mountroot: private NFS not configured\n");
vnode_put(vp);
error = ENODEV;
goto out;
}
if (vnode_mountedhere(vp) != NULL) {
vnode_put(vp);
error = EBUSY;
goto out;
}
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);
error = ENOMEM;
goto out;
}
bzero((char *)mp, (u_long)sizeof(struct mount));
mp->mnt_maxreadcnt = mp->mnt_maxwritecnt = MAXPHYS;
mp->mnt_segreadcnt = mp->mnt_segwritecnt = 32;
mp->mnt_ioflags = 0;
mp->mnt_realrootvp = NULLVP;
mp->mnt_authcache_ttl = CACHED_LOOKUP_RIGHT_TTL;
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);
#if CONFIG_MACF
mac_mount_label_init(mp);
mac_mount_label_associate(ctx, mp);
#endif
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");
goto out;
}
mbuf_setlen(m, ndmntp->ndm_saddr.sin_len);
bcopy(&ndmntp->ndm_saddr, mbuf_data(m), ndmntp->ndm_saddr.sin_len);
if ((error = mountnfs(&args, mp, m, ctx, &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);
#if CONFIG_MACF
mac_mount_label_destroy(mp);
#endif
FREE_ZONE(mp, sizeof (struct mount), M_MOUNT);
goto out;
}
*mpp = mp;
*vpp = vp;
out:
return (error);
}
#endif
static int
nfs_vfs_mount(mount_t mp, vnode_t vp, user_addr_t data, vfs_context_t ctx)
{
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(ctx))
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(ctx))
error = copyin(data, (caddr_t)&args, sizeof (struct user_nfs_args4));
else
error = copyin(data, (caddr_t)&tempargs, sizeof (struct nfs_args4));
break;
case 5:
if (vfs_context_is64bit(ctx))
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(ctx)) {
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 (argsvers >= 5)
args.auth = tempargs.auth;
}
if (args.fhsize < 0 || args.fhsize > NFSX_V3FHMAX)
return (EINVAL);
if (args.fhsize > 0) {
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, ctx, &vp);
return (error);
}
static int
nfs3_mount(
struct nfsmount *nmp,
vfs_context_t ctx,
struct user_nfs_args *argp,
nfsnode_t *npp)
{
int error = 0;
struct nfs_vattr nvattr;
u_int64_t xid;
u_char *fhp;
*npp = NULL;
fhp = CAST_DOWN(u_char *, argp->fh);
error = nfs3_getattr_rpc(NULL, nmp->nm_mountp, fhp, argp->fhsize,
ctx, &nvattr, &xid);
if (error)
goto out;
error = nfs_nget(nmp->nm_mountp, NULL, NULL, fhp, argp->fhsize,
&nvattr, &xid, NG_MARKROOT, npp);
if (*npp)
nfs_unlock(*npp);
if (error)
goto out;
if (nmp->nm_vers == NFS_VER2) {
NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXNAME);
nmp->nm_fsattr.nfsa_maxname = NFS_MAXNAMLEN;
} else if (nmp->nm_vers == NFS_VER3) {
error = nfs3_fsinfo(nmp, *npp, ctx);
if (error)
goto out;
if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_HOMOGENEOUS) &&
(nmp->nm_fsattr.nfsa_flags & NFS_FSFLAG_HOMOGENEOUS)) {
struct nfs_fsattr nfsa;
if (!nfs3_pathconf_rpc(*npp, &nfsa, ctx)) {
lck_mtx_lock(&nmp->nm_lock);
nfs3_pathconf_cache(nmp, &nfsa);
lck_mtx_unlock(&nmp->nm_lock);
}
}
}
out:
if (*npp && error) {
vnode_put(NFSTOV(*npp));
*npp = NULL;
}
return (error);
}
static int
nfs4_mount(
struct nfsmount *nmp,
vfs_context_t ctx,
__unused struct user_nfs_args *argp,
nfsnode_t *npp)
{
struct nfsm_chain nmreq, nmrep;
int error = 0, numops, status, interval;
char *path = &vfs_statfs(nmp->nm_mountp)->f_mntfromname[0];
char *name, *nextname;
fhandle_t fh;
struct nfs_vattr nvattr;
u_int64_t xid;
struct timeval now;
*npp = NULL;
fh.fh_len = 0;
microtime(&now);
nmp->nm_mounttime = ((uint64_t)now.tv_sec << 32) | now.tv_usec;
numops = 2; while (*path && (*path != '/'))
path++;
name = path;
while (*name) {
while (*name && (*name == '/'))
name++;
if (!*name)
break;
nextname = name;
while (*nextname && (*nextname != '/'))
nextname++;
numops++;
name = nextname;
}
nfsm_chain_build_alloc_init(error, &nmreq, 25 * NFSX_UNSIGNED);
nfsm_chain_add_compound_header(error, &nmreq, "mount", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTROOTFH);
name = path;
while (*name) {
while (*name && (*name == '/'))
name++;
if (!*name)
break;
nextname = name;
while (*nextname && (*nextname != '/'))
nextname++;
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_LOOKUP);
nfsm_chain_add_string(error, &nmreq, name, nextname - name);
name = nextname;
}
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
NFS4_DEFAULT_ATTRIBUTES(nmp->nm_fsattr.nfsa_supp_attr);
NFS_BITMAP_SET(nmp->nm_fsattr.nfsa_supp_attr, NFS_FATTR_FILEHANDLE);
nfsm_chain_add_bitmap(error, &nmreq, nmp->nm_fsattr.nfsa_supp_attr, NFS_ATTR_BITMAP_LEN);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
error = nfs_request(NULL, nmp->nm_mountp, &nmreq, NFSPROC4_COMPOUND, ctx, &nmrep, &xid, &status);
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTROOTFH);
name = path;
while (*name) {
while (*name && (*name == '/'))
name++;
if (!*name)
break;
nextname = name;
while (*nextname && (*nextname != '/'))
nextname++;
nfsm_chain_op_check(error, &nmrep, NFS_OP_LOOKUP);
name = nextname;
}
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsmout_if(error);
NFS_CLEAR_ATTRIBUTES(nmp->nm_fsattr.nfsa_bitmap);
NFS_CLEAR_ATTRIBUTES(&nvattr.nva_bitmap);
error = nfs4_parsefattr(&nmrep, &nmp->nm_fsattr, &nvattr, &fh, NULL);
if (!error && !NFS_BITMAP_ISSET(&nvattr.nva_bitmap, NFS_FATTR_FILEHANDLE)) {
printf("nfs: mount didn't return filehandle?\n");
error = EBADRPC;
}
nfsmout_if(error);
error = nfs_nget(nmp->nm_mountp, NULL, NULL, fh.fh_data, fh.fh_len, &nvattr, &xid, NG_MARKROOT, npp);
nfsmout_if(error);
vfs_setlocklocal(nmp->nm_mountp);
if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXREAD)) {
if (nmp->nm_fsattr.nfsa_maxread < (uint64_t)nmp->nm_rsize) {
nmp->nm_rsize = nmp->nm_fsattr.nfsa_maxread & ~(NFS_FABLKSIZE - 1);
if (nmp->nm_rsize == 0)
nmp->nm_rsize = nmp->nm_fsattr.nfsa_maxread;
}
}
if (NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_bitmap, NFS_FATTR_MAXWRITE)) {
if (nmp->nm_fsattr.nfsa_maxwrite < (uint64_t)nmp->nm_wsize) {
nmp->nm_wsize = nmp->nm_fsattr.nfsa_maxwrite & ~(NFS_FABLKSIZE - 1);
if (nmp->nm_wsize == 0)
nmp->nm_wsize = nmp->nm_fsattr.nfsa_maxwrite;
}
}
nmp->nm_renew_timer = thread_call_allocate(nfs4_renew_timer, nmp);
interval = nmp->nm_fsattr.nfsa_lease / 2;
if (interval < 1)
interval = 1;
nfs_interval_timer_start(nmp->nm_renew_timer, interval * 1000);
nfsmout:
if (*npp)
nfs_unlock(*npp);
return (error);
}
static int
mountnfs(
struct user_nfs_args *argp,
mount_t mp,
mbuf_t nam,
vfs_context_t ctx,
vnode_t *vpp)
{
struct nfsmount *nmp;
nfsnode_t np;
int error, maxio, iosize;
struct vfsstatfs *sbp;
struct timespec ts = { 1, 0 };
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));
lck_mtx_init(&nmp->nm_lock, nfs_mount_grp, LCK_ATTR_NULL);
TAILQ_INIT(&nmp->nm_resendq);
TAILQ_INIT(&nmp->nm_iodq);
TAILQ_INIT(&nmp->nm_gsscl);
vfs_setfsprivate(mp, nmp);
nfs_nhinit_finish();
}
lck_mtx_lock(&nmp->nm_lock);
nmp->nm_vers = NFS_VER2;
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;
nmp->nm_auth = RPCAUTH_SYS;
vfs_getnewfsid(mp);
nmp->nm_mountp = mp;
vfs_setauthopaque(mp);
nmp->nm_flag = argp->flags;
nmp->nm_nam = nam;
if (argp->flags & NFSMNT_NFSV4) {
nmp->nm_vers = NFS_VER4;
if (argp->sotype != SOCK_STREAM) {
error = EINVAL;
goto bad;
}
} else if (argp->flags & NFSMNT_NFSV3)
nmp->nm_vers = NFS_VER3;
if (nmp->nm_vers == NFS_VER2)
nmp->nm_flag &= ~NFSMNT_RDIRPLUS;
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 (nmp->nm_vers != NFS_VER2) {
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 > NFS_MAXBSIZE)
nmp->nm_wsize = NFS_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 > NFS_MAXBSIZE)
nmp->nm_rsize = NFS_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->flags & NFSMNT_READAHEAD)
nmp->nm_readahead = argp->readahead;
if (nmp->nm_readahead < 0)
nmp->nm_readahead = 0;
else if (nmp->nm_readahead > NFS_MAXRAHEAD)
nmp->nm_readahead = NFS_MAXRAHEAD;
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;
}
if (argp->version >= 5) {
if (argp->flags & NFSMNT_SECFLAVOR) {
switch (argp->auth) {
case RPCAUTH_SYS:
case RPCAUTH_KRB5:
case RPCAUTH_KRB5I:
case RPCAUTH_KRB5P:
nmp->nm_auth = argp->auth;
break;
default:
error = EINVAL;
goto bad;
}
}
}
if (nmp->nm_vers < NFS_VER4)
nmp->nm_funcs = &nfs3_funcs;
else
nmp->nm_funcs = &nfs4_funcs;
nmp->nm_sotype = argp->sotype;
nmp->nm_soproto = argp->proto;
if (nmp->nm_sotype == SOCK_DGRAM)
TAILQ_INIT(&nmp->nm_cwndq);
lck_mtx_unlock(&nmp->nm_lock);
if (!nfs_mbuf_mhlen)
nfs_mbuf_init();
mp->mnt_vtable->vfc_threadsafe = TRUE;
if ((error = nfs_connect(nmp)))
goto bad;
error = nmp->nm_funcs->nf_mount(nmp, ctx, argp, &np);
if (error)
goto bad;
nmp->nm_dnp = np;
*vpp = NFSTOV(np);
error = vnode_ref(*vpp);
vnode_put(*vpp);
if (error)
goto bad;
if ((error = nmp->nm_funcs->nf_update_statfs(nmp, ctx)))
goto bad;
sbp = vfs_statfs(mp);
sbp->f_bsize = nmp->nm_fsattr.nfsa_bsize;
sbp->f_blocks = nmp->nm_fsattr.nfsa_space_total / sbp->f_bsize;
sbp->f_bfree = nmp->nm_fsattr.nfsa_space_free / sbp->f_bsize;
sbp->f_bavail = nmp->nm_fsattr.nfsa_space_avail / sbp->f_bsize;
sbp->f_bused = (nmp->nm_fsattr.nfsa_space_total / sbp->f_bsize) -
(nmp->nm_fsattr.nfsa_space_free / sbp->f_bsize);
sbp->f_files = nmp->nm_fsattr.nfsa_files_total;
sbp->f_ffree = nmp->nm_fsattr.nfsa_files_free;
sbp->f_iosize = nfs_iosize;
iosize = max(nmp->nm_rsize, nmp->nm_wsize);
if (iosize < PAGE_SIZE)
iosize = PAGE_SIZE;
nmp->nm_biosize = trunc_page_32(iosize);
if (nmp->nm_vers != NFS_VER2)
vfs_setauthopaqueaccess(mp);
if (nmp->nm_flag & NFSMNT_LOCALLOCKS)
vfs_setlocklocal(nmp->nm_mountp);
if (!(nmp->nm_flag & (NFSMNT_NOLOCKS|NFSMNT_LOCALLOCKS)))
nfs_lockd_mount_change(1);
lck_mtx_lock(&nmp->nm_lock);
nmp->nm_state |= NFSSTA_MOUNTED;
lck_mtx_unlock(&nmp->nm_lock);
return (0);
bad:
lck_mtx_lock(&nmp->nm_lock);
nmp->nm_sockflags |= NMSOCK_UNMOUNT;
while (nmp->nm_sockflags & NMSOCK_POKE)
msleep(&nmp->nm_sockflags, &nmp->nm_lock, PZERO-1, "nfswaitpoke", &ts);
while (nmp->nm_sockthd) {
wakeup(&nmp->nm_sockthd);
msleep(&nmp->nm_sockthd, &nmp->nm_lock, PZERO-1, "nfswaitsockthd", &ts);
}
lck_mtx_unlock(&nmp->nm_lock);
nfs_disconnect(nmp);
if (nmp->nm_renew_timer) {
thread_call_cancel(nmp->nm_renew_timer);
thread_call_free(nmp->nm_renew_timer);
}
lck_mtx_destroy(&nmp->nm_lock, nfs_mount_grp);
FREE_ZONE((caddr_t)nmp, sizeof (struct nfsmount), M_NFSMNT);
mbuf_freem(nam);
return (error);
}
static int
nfs_vfs_unmount(
mount_t mp,
int mntflags,
__unused vfs_context_t ctx)
{
struct nfsmount *nmp;
vnode_t vp;
int error, flags = 0, docallback;
struct nfsreq *req, *treq;
struct nfs_reqqhead iodq;
struct timespec ts = { 1, 0 };
nmp = VFSTONFS(mp);
lck_mtx_lock(&nmp->nm_lock);
if (mntflags & MNT_FORCE) {
flags |= FORCECLOSE;
nmp->nm_state |= NFSSTA_FORCE;
nmp->nm_flag |= NFSMNT_SOFT;
}
vp = NFSTOV(nmp->nm_dnp);
lck_mtx_unlock(&nmp->nm_lock);
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);
lck_mtx_lock(&nmp->nm_lock);
nmp->nm_state &= ~NFSSTA_MOUNTED;
lck_mtx_unlock(&nmp->nm_lock);
vnode_rele(vp);
vflush(mp, NULLVP, FORCECLOSE);
if (!TAILQ_EMPTY(&nmp->nm_gsscl))
nfs_gss_clnt_ctx_unmount(nmp, mntflags);
vfs_setfsprivate(mp, 0);
lck_mtx_lock(&nmp->nm_lock);
nmp->nm_sockflags |= NMSOCK_UNMOUNT;
while (nmp->nm_sockflags & NMSOCK_POKE)
msleep(&nmp->nm_sockflags, &nmp->nm_lock, PZERO-1, "nfswaitpoke", &ts);
while (nmp->nm_sockthd) {
wakeup(&nmp->nm_sockthd);
msleep(&nmp->nm_sockthd, &nmp->nm_lock, PZERO-1, "nfswaitsockthd", &ts);
}
lck_mtx_unlock(&nmp->nm_lock);
nfs_disconnect(nmp);
lck_mtx_lock(&nmp->nm_lock);
if (nmp->nm_renew_timer) {
thread_call_cancel(nmp->nm_renew_timer);
thread_call_free(nmp->nm_renew_timer);
}
mbuf_freem(nmp->nm_nam);
lck_mtx_unlock(&nmp->nm_lock);
if (!(nmp->nm_flag & (NFSMNT_NOLOCKS|NFSMNT_LOCALLOCKS)))
nfs_lockd_mount_change(-1);
TAILQ_INIT(&iodq);
lck_mtx_lock(nfs_request_mutex);
TAILQ_FOREACH(req, &nfs_reqq, r_chain) {
if (req->r_nmp == nmp) {
lck_mtx_lock(&req->r_mtx);
req->r_nmp = NULL;
lck_mtx_unlock(&req->r_mtx);
if (req->r_callback.rcb_func) {
lck_mtx_lock(nfsiod_mutex);
if (req->r_achain.tqe_next == NFSREQNOLIST)
TAILQ_INSERT_TAIL(&iodq, req, r_achain);
lck_mtx_unlock(nfsiod_mutex);
}
lck_mtx_lock(&nmp->nm_lock);
if (req->r_rchain.tqe_next != NFSREQNOLIST) {
TAILQ_REMOVE(&nmp->nm_resendq, req, r_rchain);
req->r_rchain.tqe_next = NFSREQNOLIST;
req->r_flags &= ~R_RESENDQ;
}
lck_mtx_unlock(&nmp->nm_lock);
wakeup(req);
}
}
lck_mtx_unlock(nfs_request_mutex);
lck_mtx_lock(nfsiod_mutex);
TAILQ_CONCAT(&iodq, &nmp->nm_iodq, r_achain);
lck_mtx_unlock(nfsiod_mutex);
TAILQ_FOREACH_SAFE(req, &iodq, r_achain, treq) {
TAILQ_REMOVE(&iodq, req, r_achain);
req->r_achain.tqe_next = NFSREQNOLIST;
lck_mtx_lock(&req->r_mtx);
req->r_error = ENXIO;
docallback = !(req->r_flags & R_WAITSENT);
lck_mtx_unlock(&req->r_mtx);
if (docallback)
req->r_callback.rcb_func(req);
}
lck_mtx_destroy(&nmp->nm_lock, nfs_mount_grp);
FREE_ZONE((caddr_t)nmp, sizeof (struct nfsmount), M_NFSMNT);
return (0);
}
static int
nfs_vfs_root(mount_t mp, vnode_t *vpp, __unused vfs_context_t ctx)
{
vnode_t vp;
struct nfsmount *nmp;
int error;
u_long vpid;
nmp = VFSTONFS(mp);
vp = NFSTOV(nmp->nm_dnp);
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);
}
#if !QUOTA
static int
nfs_vfs_quotactl(
__unused mount_t mp,
__unused int cmds,
__unused uid_t uid,
__unused caddr_t datap,
__unused vfs_context_t context)
{
return (ENOTSUP);
}
#else
static int
nfs_aux_request(
struct nfsmount *nmp,
thread_t thd,
struct sockaddr_in *saddr,
mbuf_t mreq,
uint32_t xid,
int timeo,
struct nfsm_chain *nmrep)
{
int error = 0, on = 1, try, sendat = 2;
socket_t so = NULL;
struct timeval tv = { 1, 0 };
mbuf_t m, mrep = NULL;
struct msghdr msg;
uint32_t rxid, reply, reply_status, rejected_status;
uint32_t verf_type, verf_len, accepted_status;
size_t readlen;
if (((error = sock_socket(saddr->sin_family, SOCK_DGRAM, IPPROTO_UDP, NULL, NULL, &so))) ||
((error = sock_setsockopt(so, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)))) ||
((error = sock_setsockopt(so, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)))) ||
((error = sock_setsockopt(so, SOL_SOCKET, SO_NOADDRERR, &on, sizeof(on)))))
goto nfsmout;
for (try=0; try < timeo; try++) {
if ((error = nfs_sigintr(nmp, NULL, thd, 0)))
break;
if (!try || (try == sendat)) {
if ((error = mbuf_copym(mreq, 0, MBUF_COPYALL, MBUF_WAITOK, &m)))
goto nfsmout;
bzero(&msg, sizeof(msg));
msg.msg_name = saddr;
msg.msg_namelen = saddr->sin_len;
if ((error = sock_sendmbuf(so, &msg, m, 0, NULL)))
goto nfsmout;
sendat *= 2;
if (sendat > 30)
sendat = 30;
}
readlen = 1<<18;
bzero(&msg, sizeof(msg));
error = sock_receivembuf(so, &msg, &mrep, 0, &readlen);
if (error == EWOULDBLOCK)
continue;
nfsmout_if(error);
nfsm_chain_dissect_init(error, nmrep, mrep);
nfsm_chain_get_32(error, nmrep, rxid);
nfsm_chain_get_32(error, nmrep, reply);
nfsmout_if(error);
if ((rxid != xid) || (reply != RPC_REPLY))
error = EBADRPC;
nfsm_chain_get_32(error, nmrep, reply_status);
nfsmout_if(error);
if (reply_status == RPC_MSGDENIED) {
nfsm_chain_get_32(error, nmrep, rejected_status);
nfsmout_if(error);
error = (rejected_status == RPC_MISMATCH) ? ENOTSUP : EACCES;
goto nfsmout;
}
nfsm_chain_get_32(error, nmrep, verf_type);
nfsm_chain_get_32(error, nmrep, verf_len);
nfsmout_if(error);
if (verf_len)
nfsm_chain_adv(error, nmrep, nfsm_rndup(verf_len));
nfsm_chain_get_32(error, nmrep, accepted_status);
nfsm_assert(error, (accepted_status == RPC_SUCCESS), EIO);
break;
}
nfsmout:
if (so) {
sock_shutdown(so, SHUT_RDWR);
sock_close(so);
}
mbuf_freem(mreq);
return (error);
}
static int
nfs3_getquota(struct nfsmount *nmp, vfs_context_t ctx, u_long id, int type, struct dqblk *dqb)
{
int error = 0, auth_len, slen, timeo;
int rqvers = (type == GRPQUOTA) ? RPCRQUOTA_EXT_VER : RPCRQUOTA_VER;
thread_t thd = vfs_context_thread(ctx);
kauth_cred_t cred = vfs_context_ucred(ctx);
char *path;
uint64_t xid = 0;
struct nfsm_chain nmreq, nmrep;
mbuf_t mreq;
uint32_t val = 0, bsize;
struct sockaddr *nam = mbuf_data(nmp->nm_nam);
struct sockaddr_in saddr;
struct timeval now;
bcopy(nam, &saddr, min(sizeof(saddr), nam->sa_len));
auth_len = ((((cred->cr_ngroups - 1) > nmp->nm_numgrps) ?
nmp->nm_numgrps : (cred->cr_ngroups - 1)) << 2) +
5 * NFSX_UNSIGNED;
timeo = (nmp->nm_flag & NFSMNT_SOFT) ? 10 : 60;
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
if (nmp->nm_rqport) {
microuptime(&now);
if ((nmp->nm_rqportstamp + 60) >= (uint32_t)now.tv_sec)
goto got_rqport;
}
saddr.sin_port = htons(PMAPPORT);
nfsm_chain_build_alloc_init(error, &nmreq, 4*NFSX_UNSIGNED);
nfsm_chain_add_32(error, &nmreq, RPCPROG_RQUOTA);
nfsm_chain_add_32(error, &nmreq, rqvers);
nfsm_chain_add_32(error, &nmreq, IPPROTO_UDP);
nfsm_chain_add_32(error, &nmreq, 0);
nfsm_chain_build_done(error, &nmreq);
nfsmout_if(error);
error = nfsm_rpchead2(SOCK_DGRAM, PMAPPROG, PMAPVERS, PMAPPROC_GETPORT,
RPCAUTH_SYS, auth_len, cred, NULL, nmreq.nmc_mhead, &xid, &mreq);
nfsmout_if(error);
nmreq.nmc_mhead = NULL;
error = nfs_aux_request(nmp, thd, &saddr, mreq, R_XID32(xid), timeo, &nmrep);
nfsmout_if(error);
nfsm_chain_get_32(error, &nmrep, val);
nfsmout_if(error);
nmp->nm_rqport = val;
microuptime(&now);
nmp->nm_rqportstamp = now.tv_sec;
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
xid = 0;
got_rqport:
saddr.sin_port = htons(nmp->nm_rqport);
path = &vfs_statfs(nmp->nm_mountp)->f_mntfromname[0];
while (*path && (*path != '/'))
path++;
slen = strlen(path);
nfsm_chain_build_alloc_init(error, &nmreq, 3 * NFSX_UNSIGNED + nfsm_rndup(slen));
nfsm_chain_add_string(error, &nmreq, path, slen);
if (type == GRPQUOTA)
nfsm_chain_add_32(error, &nmreq, type);
nfsm_chain_add_32(error, &nmreq, id);
nfsm_chain_build_done(error, &nmreq);
nfsmout_if(error);
error = nfsm_rpchead2(SOCK_DGRAM, RPCPROG_RQUOTA, rqvers, RPCRQUOTA_GET,
RPCAUTH_SYS, auth_len, cred, NULL, nmreq.nmc_mhead, &xid, &mreq);
nfsmout_if(error);
nmreq.nmc_mhead = NULL;
error = nfs_aux_request(nmp, thd, &saddr, mreq, R_XID32(xid), timeo, &nmrep);
nfsmout_if(error);
nfsm_chain_get_32(error, &nmrep, val);
if (!error && (val != RQUOTA_STAT_OK)) {
if (val == RQUOTA_STAT_NOQUOTA)
error = ENOENT;
else if (val == RQUOTA_STAT_EPERM)
error = EPERM;
else
error = EIO;
}
nfsm_chain_get_32(error, &nmrep, bsize);
nfsm_chain_adv(error, &nmrep, NFSX_UNSIGNED);
nfsm_chain_get_32(error, &nmrep, val);
nfsmout_if(error);
dqb->dqb_bhardlimit = (uint64_t)val * bsize;
nfsm_chain_get_32(error, &nmrep, val);
nfsmout_if(error);
dqb->dqb_bsoftlimit = (uint64_t)val * bsize;
nfsm_chain_get_32(error, &nmrep, val);
nfsmout_if(error);
dqb->dqb_curbytes = (uint64_t)val * bsize;
nfsm_chain_get_32(error, &nmrep, dqb->dqb_ihardlimit);
nfsm_chain_get_32(error, &nmrep, dqb->dqb_isoftlimit);
nfsm_chain_get_32(error, &nmrep, dqb->dqb_curinodes);
nfsm_chain_get_32(error, &nmrep, dqb->dqb_btime);
nfsm_chain_get_32(error, &nmrep, dqb->dqb_itime);
nfsmout_if(error);
dqb->dqb_id = id;
nfsmout:
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
return (error);
}
static int
nfs4_getquota(struct nfsmount *nmp, vfs_context_t ctx, u_long id, int type, struct dqblk *dqb)
{
nfsnode_t np;
int error = 0, status, nfsvers, numops;
u_int64_t xid;
struct nfsm_chain nmreq, nmrep;
uint32_t bitmap[NFS_ATTR_BITMAP_LEN];
thread_t thd = vfs_context_thread(ctx);
kauth_cred_t cred = vfs_context_ucred(ctx);
if (type != USRQUOTA)
return (ENOTSUP);
if (!NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_supp_attr, NFS_FATTR_QUOTA_AVAIL_HARD) &&
!NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_supp_attr, NFS_FATTR_QUOTA_AVAIL_SOFT) &&
!NFS_BITMAP_ISSET(nmp->nm_fsattr.nfsa_supp_attr, NFS_FATTR_QUOTA_USED))
return (ENOTSUP);
if (id != kauth_cred_getuid(cred)) {
struct ucred temp_cred;
bzero(&temp_cred, sizeof(temp_cred));
temp_cred.cr_uid = id;
temp_cred.cr_ngroups = cred->cr_ngroups;
bcopy(cred->cr_groups, temp_cred.cr_groups, sizeof(temp_cred.cr_groups));
cred = kauth_cred_create(&temp_cred);
if (!IS_VALID_CRED(cred))
return (ENOMEM);
} else {
kauth_cred_ref(cred);
}
nfsvers = nmp->nm_vers;
np = nmp->nm_dnp;
if ((error = vnode_get(NFSTOV(np)))) {
kauth_cred_unref(&cred);
return(error);
}
nfsm_chain_null(&nmreq);
nfsm_chain_null(&nmrep);
numops = 2;
nfsm_chain_build_alloc_init(error, &nmreq, 15 * NFSX_UNSIGNED);
nfsm_chain_add_compound_header(error, &nmreq, "quota", numops);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_PUTFH);
nfsm_chain_add_fh(error, &nmreq, nfsvers, np->n_fhp, np->n_fhsize);
numops--;
nfsm_chain_add_32(error, &nmreq, NFS_OP_GETATTR);
NFS_CLEAR_ATTRIBUTES(bitmap);
NFS_BITMAP_SET(bitmap, NFS_FATTR_QUOTA_AVAIL_HARD);
NFS_BITMAP_SET(bitmap, NFS_FATTR_QUOTA_AVAIL_SOFT);
NFS_BITMAP_SET(bitmap, NFS_FATTR_QUOTA_USED);
nfsm_chain_add_bitmap_masked(error, &nmreq, bitmap,
NFS_ATTR_BITMAP_LEN, nmp->nm_fsattr.nfsa_supp_attr);
nfsm_chain_build_done(error, &nmreq);
nfsm_assert(error, (numops == 0), EPROTO);
nfsmout_if(error);
error = nfs_request2(np, NULL, &nmreq, NFSPROC4_COMPOUND, thd, cred, 0, &nmrep, &xid, &status);
nfsm_chain_skip_tag(error, &nmrep);
nfsm_chain_get_32(error, &nmrep, numops);
nfsm_chain_op_check(error, &nmrep, NFS_OP_PUTFH);
nfsm_chain_op_check(error, &nmrep, NFS_OP_GETATTR);
nfsm_assert(error, NFSTONMP(np), ENXIO);
nfsmout_if(error);
error = nfs4_parsefattr(&nmrep, NULL, NULL, NULL, dqb);
nfsmout_if(error);
nfsm_assert(error, NFSTONMP(np), ENXIO);
nfsmout:
nfsm_chain_cleanup(&nmreq);
nfsm_chain_cleanup(&nmrep);
vnode_put(NFSTOV(np));
kauth_cred_unref(&cred);
return (error);
}
static int
nfs_vfs_quotactl(mount_t mp, int cmds, uid_t uid, caddr_t datap, vfs_context_t ctx)
{
struct nfsmount *nmp;
int cmd, type, error, nfsvers;
uid_t ruid = vfs_context_ucred(ctx)->cr_ruid;
struct dqblk *dqb = (struct dqblk*)datap;
if (!(nmp = VFSTONFS(mp)))
return (ENXIO);
nfsvers = nmp->nm_vers;
if (uid == ~0U)
uid = ruid;
cmd = cmds >> SUBCMDSHIFT;
switch (cmd) {
case Q_GETQUOTA:
break;
case Q_QUOTAON:
case Q_QUOTAOFF:
case Q_SETQUOTA:
case Q_SETUSE:
case Q_SYNC:
case Q_QUOTASTAT:
return (ENOTSUP);
default:
return (EINVAL);
}
type = cmds & SUBCMDMASK;
if ((u_int)type >= MAXQUOTAS)
return (EINVAL);
if ((uid != ruid) && ((error = vfs_context_suser(ctx))))
return (error);
if (vfs_busy(mp, LK_NOWAIT))
return (0);
bzero(dqb, sizeof(*dqb));
error = nmp->nm_funcs->nf_getquota(nmp, ctx, uid, type, dqb);
vfs_unbusy(mp);
return (error);
}
#endif
struct nfs_sync_cargs {
thread_t thd;
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_wrbusy > 0)
return (VNODE_RETURNED);
if (VTONFS(vp)->n_bflag & (NBFLUSHINPROG|NBINVALINPROG))
return (VNODE_RETURNED);
error = nfs_flush(VTONFS(vp), cargs->waitfor, cargs->thd, 0);
if (error)
cargs->error = error;
return (VNODE_RETURNED);
}
static int
nfs_vfs_sync(mount_t mp, int waitfor, vfs_context_t ctx)
{
struct nfs_sync_cargs cargs;
cargs.waitfor = waitfor;
cargs.thd = vfs_context_thread(ctx);
cargs.error = 0;
vnode_iterate(mp, 0, nfs_sync_callout, &cargs);
return (cargs.error);
}
static int
nfs_vfs_vget(
__unused mount_t mp,
__unused ino64_t ino,
__unused vnode_t *vpp,
__unused vfs_context_t ctx)
{
return (ENOTSUP);
}
static int
nfs_vfs_fhtovp(
__unused mount_t mp,
__unused int fhlen,
__unused unsigned char *fhp,
__unused vnode_t *vpp,
__unused vfs_context_t ctx)
{
return (ENOTSUP);
}
static int
nfs_vfs_vptofh(
__unused vnode_t vp,
__unused int *fhlenp,
__unused unsigned char *fhp,
__unused vfs_context_t ctx)
{
return (ENOTSUP);
}
static int
nfs_vfs_start(
__unused mount_t mp,
__unused int flags,
__unused vfs_context_t ctx)
{
return (0);
}
static int
nfs_vfs_sysctl(int *name, u_int namelen, user_addr_t oldp, size_t *oldlenp,
user_addr_t newp, size_t newlen, vfs_context_t ctx)
{
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 NFSSERVER
struct nfs_exportfs *nxfs;
struct nfs_export *nx;
struct nfs_active_user_list *ulist;
struct nfs_export_stat_desc stat_desc;
struct nfs_export_stat_rec statrec;
struct nfs_user_stat_node *unode, *unode_next;
struct nfs_user_stat_desc ustat_desc;
struct nfs_user_stat_user_rec ustat_rec;
struct nfs_user_stat_path_rec upath_rec;
uint bytes_avail, bytes_total, recs_copied;
uint numExports, totlen, pos, numRecs, count;
#endif
if (namelen > 1)
return (ENOTDIR);
is_64_bit = vfs_context_is64bit(ctx);
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);
#if NFSSERVER
case NFS_EXPORTSTATS:
stat_desc.rec_vers = NFS_EXPORT_STAT_REC_VERSION;
if (!nfsrv_is_initialized()) {
stat_desc.rec_count = 0;
if (oldp && (*oldlenp >= sizeof(struct nfs_export_stat_desc)))
error = copyout(&stat_desc, oldp, sizeof(struct nfs_export_stat_desc));
*oldlenp = sizeof(struct nfs_export_stat_desc);
return (error);
}
lck_rw_lock_shared(&nfsrv_export_rwlock);
numExports = 0;
LIST_FOREACH(nxfs, &nfsrv_exports, nxfs_next)
LIST_FOREACH(nx, &nxfs->nxfs_exports, nx_next)
numExports += 1;
stat_desc.rec_count = numExports;
totlen = sizeof(struct nfs_export_stat_desc) + (numExports * sizeof(struct nfs_export_stat_rec));
if (oldp == 0) {
lck_rw_done(&nfsrv_export_rwlock);
*oldlenp = totlen;
return (0);
}
if (*oldlenp < sizeof(struct nfs_export_stat_desc)) {
lck_rw_done(&nfsrv_export_rwlock);
*oldlenp = totlen;
return (ENOMEM);
}
*oldlenp = totlen;
if (!numExports) {
lck_rw_done(&nfsrv_export_rwlock);
error = copyout(&stat_desc, oldp, sizeof(struct nfs_export_stat_desc));
return (error);
}
numRecs = (*oldlenp - sizeof(struct nfs_export_stat_desc)) / sizeof(struct nfs_export_stat_rec);
if (!numRecs) {
lck_rw_done(&nfsrv_export_rwlock);
stat_desc.rec_count = 0;
error = copyout(&stat_desc, oldp, sizeof(struct nfs_export_stat_desc));
return (error);
}
if (numRecs > numExports)
numRecs = numExports;
stat_desc.rec_count = numRecs;
pos = 0;
error = copyout(&stat_desc, oldp + pos, sizeof(struct nfs_export_stat_desc));
if (error) {
lck_rw_done(&nfsrv_export_rwlock);
return (error);
}
pos += sizeof(struct nfs_export_stat_desc);
count = 0;
LIST_FOREACH(nxfs, &nfsrv_exports, nxfs_next) {
LIST_FOREACH(nx, &nxfs->nxfs_exports, nx_next) {
if (count >= numRecs)
break;
snprintf(statrec.path, sizeof(statrec.path), "%s%s%s",
nxfs->nxfs_path, ((nxfs->nxfs_path[1] && nx->nx_path[0]) ? "/" : ""),
nx->nx_path);
statrec.ops = ((uint64_t)nx->nx_stats.ops.hi << 32) |
nx->nx_stats.ops.lo;
statrec.bytes_read = ((uint64_t)nx->nx_stats.bytes_read.hi << 32) |
nx->nx_stats.bytes_read.lo;
statrec.bytes_written = ((uint64_t)nx->nx_stats.bytes_written.hi << 32) |
nx->nx_stats.bytes_written.lo;
error = copyout(&statrec, oldp + pos, sizeof(statrec));
if (error) {
lck_rw_done(&nfsrv_export_rwlock);
return (error);
}
pos += sizeof(statrec);
}
}
lck_rw_done(&nfsrv_export_rwlock);
break;
case NFS_USERSTATS:
ustat_desc.rec_vers = NFS_USER_STAT_REC_VERSION;
ustat_rec.rec_type = NFS_USER_STAT_USER_REC;
upath_rec.rec_type = NFS_USER_STAT_PATH_REC;
bytes_total = sizeof(struct nfs_user_stat_desc);
bytes_avail = *oldlenp;
recs_copied = 0;
if (!nfsrv_is_initialized())
goto ustat_skip;
nfsrv_active_user_list_reclaim();
if (bytes_avail >= sizeof(struct nfs_user_stat_desc))
bytes_avail -= sizeof(struct nfs_user_stat_desc);
else
bytes_avail = 0;
pos = sizeof(struct nfs_user_stat_desc);
lck_rw_lock_shared(&nfsrv_export_rwlock);
LIST_FOREACH(nxfs, &nfsrv_exports, nxfs_next) {
LIST_FOREACH(nx, &nxfs->nxfs_exports, nx_next) {
if (bytes_avail >= sizeof(struct nfs_user_stat_path_rec)) {
snprintf(upath_rec.path, sizeof(upath_rec.path), "%s%s%s",
nxfs->nxfs_path, ((nxfs->nxfs_path[1] && nx->nx_path[0]) ? "/" : ""),
nx->nx_path);
error = copyout(&upath_rec, oldp + pos, sizeof(struct nfs_user_stat_path_rec));
if (error) {
goto ustat_done;
}
pos += sizeof(struct nfs_user_stat_path_rec);
bytes_avail -= sizeof(struct nfs_user_stat_path_rec);
recs_copied++;
}
else {
bytes_avail = 0;
}
bytes_total += sizeof(struct nfs_user_stat_path_rec);
ulist = &nx->nx_user_list;
lck_mtx_lock(&ulist->user_mutex);
for (unode = TAILQ_FIRST(&ulist->user_lru); unode; unode = unode_next) {
unode_next = TAILQ_NEXT(unode, lru_link);
if (bytes_avail >= sizeof(struct nfs_user_stat_user_rec)) {
ustat_rec.uid = unode->uid;
bcopy(&unode->sock, &ustat_rec.sock, unode->sock.ss_len);
ustat_rec.ops = unode->ops;
ustat_rec.bytes_read = unode->bytes_read;
ustat_rec.bytes_written = unode->bytes_written;
ustat_rec.tm_start = unode->tm_start;
ustat_rec.tm_last = unode->tm_last;
error = copyout(&ustat_rec, oldp + pos, sizeof(struct nfs_user_stat_user_rec));
if (error) {
lck_mtx_unlock(&ulist->user_mutex);
goto ustat_done;
}
pos += sizeof(struct nfs_user_stat_user_rec);
bytes_avail -= sizeof(struct nfs_user_stat_user_rec);
recs_copied++;
}
else {
bytes_avail = 0;
}
bytes_total += sizeof(struct nfs_user_stat_user_rec);
}
lck_mtx_unlock(&ulist->user_mutex);
}
}
ustat_done:
lck_rw_done(&nfsrv_export_rwlock);
ustat_skip:
ustat_desc.rec_count = recs_copied;
if (!error) {
if (*oldlenp >= sizeof(struct nfs_user_stat_desc))
error = copyout(&ustat_desc, oldp, sizeof(struct nfs_user_stat_desc));
else
error = ENOMEM;
*oldlenp = bytes_total;
}
break;
case NFS_USERCOUNT:
if (!oldp) {
*oldlenp = sizeof(nfsrv_user_stat_node_count);
return (0);
}
if (*oldlenp < sizeof(nfsrv_user_stat_node_count)) {
*oldlenp = sizeof(nfsrv_user_stat_node_count);
return (ENOMEM);
}
if (nfsrv_is_initialized()) {
nfsrv_active_user_list_reclaim();
}
error = copyout(&nfsrv_user_stat_node_count, oldp, sizeof(nfsrv_user_stat_node_count));
break;
#endif
case VFS_CTL_NOLOCKS:
if (req->oldptr != USER_ADDR_NULL) {
lck_mtx_lock(&nmp->nm_lock);
val = (nmp->nm_flag & NFSMNT_NOLOCKS) ? 1 : 0;
lck_mtx_unlock(&nmp->nm_lock);
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);
lck_mtx_lock(&nmp->nm_lock);
if (nmp->nm_flag & NFSMNT_LOCALLOCKS) {
error = EINVAL;
} else if (val) {
if (!(nmp->nm_flag & NFSMNT_NOLOCKS))
nfs_lockd_mount_change(-1);
nmp->nm_flag |= NFSMNT_NOLOCKS;
nmp->nm_state &= ~NFSSTA_LOCKTIMEO;
} else {
if (nmp->nm_flag & NFSMNT_NOLOCKS)
nfs_lockd_mount_change(1);
nmp->nm_flag &= ~NFSMNT_NOLOCKS;
}
lck_mtx_unlock(&nmp->nm_lock);
}
break;
case VFS_CTL_QUERY:
lck_mtx_lock(&nmp->nm_lock);
if (nmp->nm_state & (NFSSTA_TIMEO|NFSSTA_JUKEBOXTIMEO))
vq.vq_flags |= VQ_NOTRESP;
if (!(nmp->nm_flag & (NFSMNT_NOLOCKS|NFSMNT_LOCALLOCKS)) &&
(nmp->nm_state & NFSSTA_LOCKTIMEO))
vq.vq_flags |= VQ_NOTRESP;
lck_mtx_unlock(&nmp->nm_lock);
error = SYSCTL_OUT(req, &vq, sizeof(vq));
break;
case VFS_CTL_TIMEO:
if (req->oldptr != USER_ADDR_NULL) {
lck_mtx_lock(&nmp->nm_lock);
val = nmp->nm_tprintf_initial_delay;
lck_mtx_unlock(&nmp->nm_lock);
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);
lck_mtx_lock(&nmp->nm_lock);
if (val < 0)
nmp->nm_tprintf_initial_delay = 0;
else
nmp->nm_tprintf_initial_delay = val;
lck_mtx_unlock(&nmp->nm_lock);
}
break;
default:
return (ENOTSUP);
}
return (error);
}