/*
- * Copyright (c) 2000-2007 Apple Inc. All rights reserved.
+ * Copyright (c) 2000-2011 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
#include <sys/vm.h>
#include <sys/vmparam.h>
+#include <netinet/in.h>
+
#include <nfs/nfsproto.h>
#include <nfs/rpcv2.h>
#include <nfs/nfs.h>
struct nfsrv_sockhead nfsrv_socklist, nfsrv_deadsocklist, nfsrv_sockwg,
nfsrv_sockwait, nfsrv_sockwork;
struct nfsrv_sock *nfsrv_udpsock = NULL;
+struct nfsrv_sock *nfsrv_udp6sock = NULL;
/* NFS exports */
struct nfsrv_expfs_list nfsrv_exports;
-struct nfsrv_export_hashhead *nfsrv_export_hashtbl;
+struct nfsrv_export_hashhead *nfsrv_export_hashtbl = NULL;
+int nfsrv_export_hash_size = NFSRVEXPHASHSZ;
u_long nfsrv_export_hash;
lck_grp_t *nfsrv_export_rwlock_group;
lck_rw_t nfsrv_export_rwlock;
+#if CONFIG_FSE
/* NFS server file modification event generator */
struct nfsrv_fmod_hashhead *nfsrv_fmod_hashtbl;
u_long nfsrv_fmod_hash;
lck_grp_t *nfsrv_fmod_grp;
lck_mtx_t *nfsrv_fmod_mutex;
static int nfsrv_fmod_timer_on = 0;
-
int nfsrv_fsevents_enabled = 1;
+#endif
/* NFS server timers */
+#if CONFIG_FSE
thread_call_t nfsrv_fmod_timer_call;
+#endif
thread_call_t nfsrv_deadsock_timer_call;
thread_call_t nfsrv_wg_timer_call;
int nfsrv_wg_timer_on;
int nfsrv_async = 0;
-static int nfsrv_authorize(vnode_t,vnode_t,kauth_action_t,vfs_context_t,struct nfs_export_options*,int);
-static int nfsrv_wg_coalesce(struct nfsrv_descript *, struct nfsrv_descript *);
+int nfsrv_authorize(vnode_t,vnode_t,kauth_action_t,vfs_context_t,struct nfs_export_options*,int);
+int nfsrv_wg_coalesce(struct nfsrv_descript *, struct nfsrv_descript *);
+void nfsrv_modified(vnode_t, vfs_context_t);
extern void IOSleep(int);
+extern int safe_getpath(struct vnode *dvp, char *leafname, char *path, int _len, int *truncated_path);
/*
* Initialize the data structures for the server.
return;
}
- if (sizeof (struct nfsrv_sock) > NFS_SVCALLOC) {
+ if (sizeof (struct nfsrv_sock) > NFS_SVCALLOC)
printf("struct nfsrv_sock bloated (> %dbytes)\n",NFS_SVCALLOC);
- printf("Try reducing NFS_UIDHASHSIZ\n");
- }
/* init nfsd mutex */
nfsd_lck_grp = lck_grp_alloc_init("nfsd", LCK_GRP_ATTR_NULL);
nfsrv_slp_mutex_group = lck_grp_alloc_init("nfsrv-slp-mutex", LCK_GRP_ATTR_NULL);
/* init export data structures */
- nfsrv_export_hashtbl = hashinit(8, M_TEMP, &nfsrv_export_hash);
LIST_INIT(&nfsrv_exports);
nfsrv_export_rwlock_group = lck_grp_alloc_init("nfsrv-export-rwlock", LCK_GRP_ATTR_NULL);
lck_rw_init(&nfsrv_export_rwlock, nfsrv_export_rwlock_group, LCK_ATTR_NULL);
nfsrv_reqcache_lck_grp = lck_grp_alloc_init("nfsrv_reqcache", LCK_GRP_ATTR_NULL);
nfsrv_reqcache_mutex = lck_mtx_alloc_init(nfsrv_reqcache_lck_grp, LCK_ATTR_NULL);
+#if CONFIG_FSE
/* init NFS server file modified event generation */
nfsrv_fmod_hashtbl = hashinit(NFSRVFMODHASHSZ, M_TEMP, &nfsrv_fmod_hash);
nfsrv_fmod_grp = lck_grp_alloc_init("nfsrv_fmod", LCK_GRP_ATTR_NULL);
nfsrv_fmod_mutex = lck_mtx_alloc_init(nfsrv_fmod_grp, LCK_ATTR_NULL);
+#endif
/* initialize NFS server timer callouts */
+#if CONFIG_FSE
nfsrv_fmod_timer_call = thread_call_allocate(nfsrv_fmod_timer, NULL);
+#endif
nfsrv_deadsock_timer_call = thread_call_allocate(nfsrv_deadsock_timer, NULL);
nfsrv_wg_timer_call = thread_call_allocate(nfsrv_wg_timer, NULL);
TAILQ_INIT(&nfsd_head);
TAILQ_INIT(&nfsd_queue);
nfsrv_udpsock = NULL;
+ nfsrv_udp6sock = NULL;
+ /* Setup the up-call handling */
+ nfsrv_uc_init();
+
/* initialization complete */
nfsrv_initted = NFSRV_INITIALIZED;
}
int error, attrerr;
struct vnode_attr vattr;
struct nfs_filehandle nfh;
- u_long nfsmode;
+ u_int32_t nfsmode;
kauth_action_t testaction;
struct nfs_export *nx;
struct nfs_export_options *nxo;
* obtain good performance in the optimistic mode.
*/
if (nfsmode & NFS_ACCESS_READ) {
- if (vnode_isdir(vp)) {
- testaction =
- KAUTH_VNODE_LIST_DIRECTORY |
- KAUTH_VNODE_READ_EXTATTRIBUTES;
- } else {
- testaction =
- KAUTH_VNODE_READ_DATA |
- KAUTH_VNODE_READ_EXTATTRIBUTES;
- }
+ testaction = vnode_isdir(vp) ? KAUTH_VNODE_LIST_DIRECTORY : KAUTH_VNODE_READ_DATA;
if (nfsrv_authorize(vp, NULL, testaction, ctx, nxo, 0))
nfsmode &= ~NFS_ACCESS_READ;
}
vfs_context_t ctx,
mbuf_t *mrepp)
{
- struct nameidata ni, *nip = ∋
+ struct nameidata ni;
vnode_t vp, dirp = NULL;
struct nfs_filehandle dnfh, nfh;
struct nfs_export *nx = NULL;
struct nfs_export_options *nxo;
int error, attrerr, dirattrerr, isdotdot;
- uint32_t len;
+ uint32_t len = 0;
uid_t saved_uid;
struct vnode_attr va, dirattr, *vap = &va;
struct nfsm_chain *nmreq, nmrep;
nfsmerr_if(error);
ni.ni_cnd.cn_nameiop = LOOKUP;
+#if CONFIG_TRIGGERS
+ ni.ni_op = OP_LOOKUP;
+#endif
ni.ni_cnd.cn_flags = LOCKLEAF;
error = nfsm_chain_get_path_namei(nmreq, len, &ni);
isdotdot = ((len == 2) && (ni.ni_cnd.cn_pnbuf[0] == '.') && (ni.ni_cnd.cn_pnbuf[1] == '.'));
nameidone(&ni);
- vp = nip->ni_vp;
+ vp = ni.ni_vp;
error = nfsrv_vptofh(nx, nd->nd_vers, (isdotdot ? &dnfh : NULL), vp, ctx, &nfh);
if (!error) {
nfsm_srv_vattr_init(vap, nd->nd_vers);
struct nfs_export_options *nxo;
struct nfsm_chain *nmreq, nmrep;
mbuf_t mpath, mp;
- uio_t uiop = NULL;
+ uio_t auio = NULL;
char uio_buf[ UIO_SIZEOF(4) ];
char *uio_bufp = &uio_buf[0];
int uio_buflen = UIO_SIZEOF(4);
error = ENOMEM;
nfsmerr_if(error);
}
- uiop = uio_createwithbuffer(mpcnt, 0, UIO_SYSSPACE, UIO_READ, uio_bufp, uio_buflen);
- if (!uiop)
+ auio = uio_createwithbuffer(mpcnt, 0, UIO_SYSSPACE, UIO_READ, uio_bufp, uio_buflen);
+ if (!auio)
error = ENOMEM;
nfsmerr_if(error);
for (mp = mpath; mp; mp = mbuf_next(mp))
- uio_addiov(uiop, CAST_USER_ADDR_T((caddr_t)mbuf_data(mp)), mbuf_len(mp));
+ uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(mp)), mbuf_len(mp));
error = nfsrv_fhtovp(&nfh, nd, &vp, &nx, &nxo);
nfsmerr_if(error);
if (!error)
error = nfsrv_authorize(vp, NULL, KAUTH_VNODE_READ_DATA, ctx, nxo, 0);
if (!error)
- error = VNOP_READLINK(vp, uiop, ctx);
+ error = VNOP_READLINK(vp, auio, ctx);
if (vp) {
if (nd->nd_vers == NFS_VER3) {
nfsm_srv_vattr_init(&vattr, NFS_VER3);
nfsm_chain_build_done(error, &nmrep);
goto nfsmout;
}
- if (uiop && (uio_resid(uiop) > 0)) {
- // LP64todo - fix this
- len -= uio_resid(uiop);
+ if (auio && (uio_resid(auio) > 0)) {
+ len -= uio_resid(auio);
tlen = nfsm_rndup(len);
nfsm_adj(mpath, NFS_MAXPATHLEN-tlen, tlen-len);
}
struct nfs_filehandle nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
- uio_t uiop = NULL;
+ uio_t auio = NULL;
char *uio_bufp = NULL;
struct vnode_attr vattr, *vap = &vattr;
off_t off;
else
nfsm_chain_get_32(error, nmreq, off);
nfsm_chain_get_32(error, nmreq, reqlen);
- maxlen = NFS_SRVMAXDATA(nd);
+ maxlen = NFSRV_NDMAXDATA(nd);
if (reqlen > maxlen)
reqlen = maxlen;
nfsmerr_if(error);
nfsmerr_if(error);
MALLOC(uio_bufp, char *, UIO_SIZEOF(mreadcnt), M_TEMP, M_WAITOK);
if (uio_bufp)
- uiop = uio_createwithbuffer(mreadcnt, off, UIO_SYSSPACE,
+ auio = uio_createwithbuffer(mreadcnt, off, UIO_SYSSPACE,
UIO_READ, uio_bufp, UIO_SIZEOF(mreadcnt));
- if (!uio_bufp || !uiop) {
+ if (!uio_bufp || !auio) {
error = ENOMEM;
goto errorexit;
}
for (m = mread; m; m = mbuf_next(m))
- uio_addiov(uiop, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), mbuf_len(m));
- error = VNOP_READ(vp, uiop, IO_NODELOCKED, ctx);
+ uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), mbuf_len(m));
+ error = VNOP_READ(vp, auio, IO_NODELOCKED, ctx);
} else {
- uiop = uio_createwithbuffer(0, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
- if (!uiop) {
+ auio = uio_createwithbuffer(0, 0, UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
+ if (!auio) {
error = ENOMEM;
goto errorexit;
}
vp = NULL;
/* trim off any data not actually read */
- // LP64todo - fix this
- len -= uio_resid(uiop);
+ len -= uio_resid(auio);
tlen = nfsm_rndup(len);
if (count != tlen || tlen != len)
nfsm_adj(mread, count - tlen, tlen - len);
return (error);
}
+#if CONFIG_FSE
/*
* NFS File modification reporting
*
void
nfsrv_fmod_timer(__unused void *param0, __unused void *param1)
{
- struct nfsrv_fmod_hashhead *head;
- struct nfsrv_fmod *fp, *nfp;
+ struct nfsrv_fmod_hashhead *headp, firehead;
+ struct nfsrv_fmod *fp, *nfp, *pfp;
uint64_t timenow, next_deadline;
- int interval = 0;
- int i;
+ int interval = 0, i, fmod_fire;
+ LIST_INIT(&firehead);
lck_mtx_lock(nfsrv_fmod_mutex);
+again:
clock_get_uptime(&timenow);
clock_interval_to_deadline(nfsrv_fmod_pendtime, 1000 * 1000,
&next_deadline);
/*
* Scan all the hash chains
*/
+ fmod_fire = 0;
for (i = 0; i < NFSRVFMODHASHSZ; i++) {
/*
* For each hash chain, look for an entry
* that has exceeded the deadline.
*/
- head = &nfsrv_fmod_hashtbl[i];
- LIST_FOREACH(fp, head, fm_link) {
+ headp = &nfsrv_fmod_hashtbl[i];
+ LIST_FOREACH(fp, headp, fm_link) {
if (timenow >= fp->fm_deadline)
break;
if (fp->fm_deadline < next_deadline)
* following entries in the chain, since they're
* sorted in time order.
*/
+ pfp = NULL;
while (fp) {
- /*
- * Fire off the content modified fsevent for each
- * entry, remove it from the list, and free it.
- */
-#if CONFIG_FSE
- if (nfsrv_fsevents_enabled)
+ /* move each entry to the fire list */
+ nfp = LIST_NEXT(fp, fm_link);
+ LIST_REMOVE(fp, fm_link);
+ fmod_fire++;
+ if (pfp)
+ LIST_INSERT_AFTER(pfp, fp, fm_link);
+ else
+ LIST_INSERT_HEAD(&firehead, fp, fm_link);
+ pfp = fp;
+ fp = nfp;
+ }
+ }
+
+ if (fmod_fire) {
+ lck_mtx_unlock(nfsrv_fmod_mutex);
+ /*
+ * Fire off the content modified fsevent for each
+ * entry and free it.
+ */
+ LIST_FOREACH_SAFE(fp, &firehead, fm_link, nfp) {
+ if (nfsrv_fsevents_enabled) {
+ fp->fm_context.vc_thread = current_thread();
add_fsevent(FSE_CONTENT_MODIFIED, &fp->fm_context,
FSE_ARG_VNODE, fp->fm_vp,
FSE_ARG_DONE);
-#endif
+ }
vnode_put(fp->fm_vp);
kauth_cred_unref(&fp->fm_context.vc_ucred);
- nfp = LIST_NEXT(fp, fm_link);
LIST_REMOVE(fp, fm_link);
FREE(fp, M_TEMP);
- nfsrv_fmod_pending--;
- fp = nfp;
}
+ lck_mtx_lock(nfsrv_fmod_mutex);
+ nfsrv_fmod_pending -= fmod_fire;
+ goto again;
}
/*
lck_mtx_unlock(nfsrv_fmod_mutex);
}
-#if CONFIG_FSE
/*
* When a vnode has been written to, enter it in the hash
* table of vnodes pending creation of an fsevent. If the
* callout timer isn't already running, schedule a callback
* for nfsrv_fmod_pendtime msec from now.
*/
-static void
+void
nfsrv_modified(vnode_t vp, vfs_context_t ctx)
{
uint64_t deadline;
struct nfs_filehandle nfh;
struct nfs_export *nx;
struct nfs_export_options *nxo;
- uio_t uiop = NULL;
+ uio_t auio = NULL;
char *uio_bufp = NULL;
off_t off;
uid_t saved_uid;
} else {
mlen = 0;
}
- if ((len > NFS_MAXDATA) || (len < 0) || (mlen < len)) {
+ if ((len > NFSRV_MAXDATA) || (len < 0) || (mlen < len)) {
error = EIO;
goto nfsmerr;
}
mcount++;
MALLOC(uio_bufp, char *, UIO_SIZEOF(mcount), M_TEMP, M_WAITOK);
if (uio_bufp)
- uiop = uio_createwithbuffer(mcount, off, UIO_SYSSPACE, UIO_WRITE, uio_bufp, UIO_SIZEOF(mcount));
- if (!uio_bufp || !uiop)
+ auio = uio_createwithbuffer(mcount, off, UIO_SYSSPACE, UIO_WRITE, uio_bufp, UIO_SIZEOF(mcount));
+ if (!uio_bufp || !auio)
error = ENOMEM;
nfsmerr_if(error);
for (m = nmreq->nmc_mcur; m; m = mbuf_next(m))
if ((mlen = mbuf_len(m)) > 0)
- uio_addiov(uiop, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), mlen);
+ uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), mlen);
/*
* XXX The IO_METASYNC flag indicates that all metadata (and not just
* enough to ensure data integrity) mus be written to stable storage
else
ioflags = (IO_METASYNC | IO_SYNC | IO_NODELOCKED);
- error = VNOP_WRITE(vp, uiop, ioflags, ctx);
- OSAddAtomic(1, (SInt32*)&nfsstats.srvvop_writes);
+ error = VNOP_WRITE(vp, auio, ioflags, ctx);
+ OSAddAtomic64(1, &nfsstats.srvvop_writes);
/* update export stats */
NFSStatAdd64(&nx->nx_stats.bytes_written, len);
*/
#define NWDELAYHASH(sock, f) \
- (&(sock)->ns_wdelayhashtbl[(*((u_long *)(f))) % NFS_WDELAYHASHSIZ])
+ (&(sock)->ns_wdelayhashtbl[(*((u_int32_t *)(f))) % NFS_WDELAYHASHSIZ])
/* These macros compare nfsrv_descript structures. */
#define NFSW_CONTIG(o, n) \
(((o)->nd_eoff >= (n)->nd_off) && nfsrv_fhmatch(&(o)->nd_fh, &(n)->nd_fh))
int preattrerr, postattrerr;
vnode_t vp;
mbuf_t m;
- uio_t uiop = NULL;
+ uio_t auio = NULL;
char *uio_bufp = NULL;
u_quad_t cur_usec;
struct timeval now;
mlen = 0;
}
- if ((nd->nd_len > NFS_MAXDATA) || (nd->nd_len < 0) || (mlen < nd->nd_len)) {
+ if ((nd->nd_len > NFSRV_MAXDATA) || (nd->nd_len < 0) || (mlen < nd->nd_len)) {
error = EIO;
nfsmerr:
nd->nd_repstat = error;
MALLOC(uio_bufp, char *, UIO_SIZEOF(i), M_TEMP, M_WAITOK);
if (uio_bufp)
- uiop = uio_createwithbuffer(i, nd->nd_off, UIO_SYSSPACE,
+ auio = uio_createwithbuffer(i, nd->nd_off, UIO_SYSSPACE,
UIO_WRITE, uio_bufp, UIO_SIZEOF(i));
- if (!uio_bufp || !uiop)
+ if (!uio_bufp || !auio)
error = ENOMEM;
if (!error) {
for (m = nmreq->nmc_mhead; m; m = mbuf_next(m))
if ((tlen = mbuf_len(m)) > 0)
- uio_addiov(uiop, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), tlen);
- error = VNOP_WRITE(vp, uiop, ioflags, ctx);
- OSAddAtomic(1, (SInt32*)&nfsstats.srvvop_writes);
+ uio_addiov(auio, CAST_USER_ADDR_T((caddr_t)mbuf_data(m)), tlen);
+ error = VNOP_WRITE(vp, auio, ioflags, ctx);
+ OSAddAtomic64(1, &nfsstats.srvvop_writes);
/* update export stats */
NFSStatAdd64(&nx->nx_stats.bytes_written, nd->nd_len);
* - update the nd_eoff and nd_stable for owp
* - put nd on owp's nd_coalesce list
*/
-static int
+int
nfsrv_wg_coalesce(struct nfsrv_descript *owp, struct nfsrv_descript *nd)
{
int overlap, error;
struct nameidata ni;
int error, rdev, dpreattrerr, dpostattrerr, postattrerr;
int how, exclusive_flag;
- uint32_t len;
+ uint32_t len = 0, cnflags;
vnode_t vp, dvp, dirp;
struct nfs_filehandle nfh;
struct nfs_export *nx = NULL;
ni.ni_cnd.cn_nameiop = 0;
rdev = 0;
- /*
- * Save the original credential UID in case they are
- * mapped and we need to map the IDs in the attributes.
- */
saved_uid = kauth_cred_getuid(nd->nd_cr);
nfsm_chain_get_fh_ptr(error, nmreq, nd->nd_vers, nfh.nfh_fhp, nfh.nfh_len);
nfsmerr_if(error);
ni.ni_cnd.cn_nameiop = CREATE;
+#if CONFIG_TRIGGERS
+ ni.ni_op = OP_LINK;
+#endif
ni.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
error = nfsm_chain_get_path_namei(nmreq, len, &ni);
if (!error) {
if (vp == NULL) {
kauth_acl_t xacl = NULL;
- /*
- * If the credentials were mapped, we should
- * map the same values in the attributes.
- */
- if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(nd->nd_cr) != saved_uid)) {
- int ismember;
- VATTR_SET(vap, va_uid, kauth_cred_getuid(nd->nd_cr));
- if (kauth_cred_ismember_gid(nd->nd_cr, vap->va_gid, &ismember) || !ismember)
- VATTR_SET(vap, va_gid, kauth_cred_getgid(nd->nd_cr));
- }
-
/* authorize before creating */
error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
}
VATTR_CLEAR_ACTIVE(vap, va_data_size);
VATTR_CLEAR_ACTIVE(vap, va_access_time);
+ /*
+ * Server policy is to alway use the mapped rpc credential for
+ * file system object creation. This has the nice side effect of
+ * enforcing BSD creation semantics
+ */
+ VATTR_CLEAR_ACTIVE(vap, va_uid);
+ VATTR_CLEAR_ACTIVE(vap, va_gid);
/* validate new-file security information */
- if (!error) {
+ if (!error)
error = vnode_authattr_new(dvp, vap, 0, ctx);
- if (error && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) {
- /*
- * Most NFS servers just ignore the UID/GID attributes, so we
- * try ignoring them if that'll help the request succeed.
- */
- VATTR_CLEAR_ACTIVE(vap, va_uid);
- VATTR_CLEAR_ACTIVE(vap, va_gid);
- error = vnode_authattr_new(dvp, vap, 0, ctx);
- }
- }
if (vap->va_type == VREG || vap->va_type == VSOCK) {
vp = NULL;
}
ni.ni_cnd.cn_nameiop = LOOKUP;
+#if CONFIG_TRIGGERS
+ ni.ni_op = OP_LOOKUP;
+#endif
ni.ni_cnd.cn_flags &= ~LOCKPARENT;
ni.ni_cnd.cn_context = ctx;
ni.ni_startdir = dvp;
ni.ni_usedvp = dvp;
- error = lookup(&ni);
+ cnflags = ni.ni_cnd.cn_flags; /* store in case we have to restore */
+ while ((error = lookup(&ni)) == ERECYCLE) {
+ ni.ni_cnd.cn_flags = cnflags;
+ ni.ni_cnd.cn_nameptr = ni.ni_cnd.cn_pnbuf;
+ ni.ni_usedvp = ni.ni_dvp = ni.ni_startdir = dvp;
+ }
if (!error) {
if (ni.ni_cnd.cn_flags & ISSYMLINK)
error = EINVAL;
struct vnode_attr va, *vap = &va;
struct nameidata ni;
int error, dpreattrerr, dpostattrerr, postattrerr;
- uint32_t len;
- u_long major, minor;
+ uint32_t len = 0, cnflags;
+ u_int32_t major = 0, minor = 0;
enum vtype vtyp;
+ nfstype nvtype;
vnode_t vp, dvp, dirp;
struct nfs_filehandle nfh;
struct nfs_export *nx = NULL;
vp = dvp = dirp = NULL;
ni.ni_cnd.cn_nameiop = 0;
- /*
- * Save the original credential UID in case they are
- * mapped and we need to map the IDs in the attributes.
- */
saved_uid = kauth_cred_getuid(nd->nd_cr);
nfsm_chain_get_fh_ptr(error, nmreq, NFS_VER3, nfh.nfh_fhp, nfh.nfh_len);
nfsmerr_if(error);
ni.ni_cnd.cn_nameiop = CREATE;
+#if CONFIG_TRIGGERS
+ ni.ni_op = OP_LINK;
+#endif
ni.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
error = nfsm_chain_get_path_namei(nmreq, len, &ni);
if (!error) {
dvp = ni.ni_dvp;
vp = ni.ni_vp;
- nfsm_chain_get_32(error, nmreq, vtyp);
+ nfsm_chain_get_32(error, nmreq, nvtype);
nfsmerr_if(error);
- vtyp = nfstov_type(vtyp, NFS_VER3);
+ vtyp = nfstov_type(nvtype, NFS_VER3);
if (!error && (vtyp != VCHR) && (vtyp != VBLK) && (vtyp != VSOCK) && (vtyp != VFIFO)) {
error = NFSERR_BADTYPE;
goto out;
}
VATTR_SET(vap, va_type, vtyp);
- /*
- * If the credentials were mapped, we should
- * map the same values in the attributes.
- */
- if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(nd->nd_cr) != saved_uid)) {
- int ismember;
- VATTR_SET(vap, va_uid, kauth_cred_getuid(nd->nd_cr));
- if (kauth_cred_ismember_gid(nd->nd_cr, vap->va_gid, &ismember) || !ismember)
- VATTR_SET(vap, va_gid, kauth_cred_getgid(nd->nd_cr));
- }
-
/* authorize before creating */
error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
}
VATTR_CLEAR_ACTIVE(vap, va_data_size);
VATTR_CLEAR_ACTIVE(vap, va_access_time);
+ /*
+ * Server policy is to alway use the mapped rpc credential for
+ * file system object creation. This has the nice side effect of
+ * enforcing BSD creation semantics
+ */
+ VATTR_CLEAR_ACTIVE(vap, va_uid);
+ VATTR_CLEAR_ACTIVE(vap, va_gid);
/* validate new-file security information */
- if (!error) {
+ if (!error)
error = vnode_authattr_new(dvp, vap, 0, ctx);
- if (error && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) {
- /*
- * Most NFS servers just ignore the UID/GID attributes, so we
- * try ignoring them if that'll help the request succeed.
- */
- VATTR_CLEAR_ACTIVE(vap, va_uid);
- VATTR_CLEAR_ACTIVE(vap, va_gid);
- error = vnode_authattr_new(dvp, vap, 0, ctx);
- }
- }
+
if (error)
goto out1;
vp = NULL;
}
ni.ni_cnd.cn_nameiop = LOOKUP;
+#if CONFIG_TRIGGERS
+ ni.ni_op = OP_LOOKUP;
+#endif
ni.ni_cnd.cn_flags &= ~LOCKPARENT;
ni.ni_cnd.cn_context = vfs_context_current();
ni.ni_startdir = dvp;
ni.ni_usedvp = dvp;
- error = lookup(&ni);
+ cnflags = ni.ni_cnd.cn_flags; /* store in case we have to restore */
+ while ((error = lookup(&ni)) == ERECYCLE) {
+ ni.ni_cnd.cn_flags = cnflags;
+ ni.ni_cnd.cn_nameptr = ni.ni_cnd.cn_pnbuf;
+ ni.ni_usedvp = ni.ni_dvp = ni.ni_startdir = dvp;
+ }
if (!error) {
vp = ni.ni_vp;
if (ni.ni_cnd.cn_flags & ISSYMLINK)
{
struct nameidata ni;
int error, dpreattrerr, dpostattrerr;
- uint32_t len;
+ uint32_t len = 0;
uid_t saved_uid;
vnode_t vp, dvp, dirp = NULL;
struct vnode_attr dpreattr, dpostattr;
nfsmerr_if(error);
ni.ni_cnd.cn_nameiop = DELETE;
+#if CONFIG_TRIGGERS
+ ni.ni_op = OP_UNLINK;
+#endif
ni.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
error = nfsm_chain_get_path_namei(nmreq, len, &ni);
if (!error) {
struct nfsm_chain *nmreq, nmrep;
char *from_name, *to_name;
#if CONFIG_FSE
- int from_len, to_len;
+ int from_len=0, to_len=0;
fse_info from_finfo, to_finfo;
#endif
u_char didstats = 0;
fdpreattrerr = fdpostattrerr = ENOENT;
tdpreattrerr = tdpostattrerr = ENOENT;
saved_uid = kauth_cred_getuid(nd->nd_cr);
+ fromlen = tolen = 0;
frompath = topath = NULL;
fdirp = tdirp = NULL;
nmreq = &nd->nd_nmreq;
kauth_cred_ref(saved_cred);
retry:
fromni.ni_cnd.cn_nameiop = DELETE;
+#if CONFIG_TRIGGERS
+ fromni.ni_op = OP_UNLINK;
+#endif
fromni.ni_cnd.cn_flags = WANTPARENT;
fromni.ni_cnd.cn_pnbuf = frompath;
}
toni.ni_cnd.cn_nameiop = RENAME;
+#if CONFIG_TRIGGERS
+ toni.ni_op = OP_RENAME;
+#endif
toni.ni_cnd.cn_flags = WANTPARENT;
toni.ni_cnd.cn_pnbuf = topath;
*/
#if CONFIG_FSE
if (nfsrv_fsevents_enabled && need_fsevent(FSE_RENAME, fvp)) {
+ int from_truncated = 0, to_truncated = 0;
+
get_fse_info(fvp, &from_finfo, ctx);
if (tvp)
get_fse_info(tvp, &to_finfo, ctx);
from_name = get_pathbuff();
- from_len = MAXPATHLEN;
- if (from_name && vn_getpath(fdvp, from_name, &from_len)) {
- release_pathbuff(from_name);
- from_name = NULL;
- } else if ((from_len + 1 + fromni.ni_cnd.cn_namelen + 1) < MAXPATHLEN) {
- // if the path is not just "/", then append a "/"
- if (from_len > 2) {
- from_name[from_len-1] = '/';
- } else {
- from_len--;
- }
- strlcpy(&from_name[from_len], fromni.ni_cnd.cn_nameptr, MAXPATHLEN-from_len);
- from_len += fromni.ni_cnd.cn_namelen + 1;
- from_name[from_len] = '\0';
+ if (from_name) {
+ from_len = safe_getpath(fdvp, fromni.ni_cnd.cn_nameptr, from_name, MAXPATHLEN, &from_truncated);
}
-
+
to_name = from_name ? get_pathbuff() : NULL;
- to_len = MAXPATHLEN;
+ if (to_name) {
+ to_len = safe_getpath(tdvp, toni.ni_cnd.cn_nameptr, to_name, MAXPATHLEN, &to_truncated);
+ }
- if (!to_name) {
- if (from_name) {
- release_pathbuff(from_name);
- from_name = NULL;
- }
- } else if (vn_getpath(tdvp, to_name, &to_len)) {
- release_pathbuff(from_name);
- release_pathbuff(to_name);
- from_name = to_name = NULL;
- } else if ((to_len + 1 + toni.ni_cnd.cn_namelen + 1) < MAXPATHLEN) {
- // if the path is not just "/", then append a "/"
- if (to_len > 2) {
- to_name[to_len-1] = '/';
- } else {
- to_len--;
- }
- strlcpy(&to_name[to_len], toni.ni_cnd.cn_nameptr, MAXPATHLEN-to_len);
- to_len += toni.ni_cnd.cn_namelen + 1;
- to_name[to_len] = '\0';
+ if (from_truncated || to_truncated) {
+ from_finfo.mode |= FSE_TRUNCATED_PATH;
}
+
} else {
from_name = NULL;
to_name = NULL;
goto out;
ni.ni_cnd.cn_nameiop = CREATE;
+#if CONFIG_TRIGGERS
+ ni.ni_op = OP_LINK;
+#endif
ni.ni_cnd.cn_flags = LOCKPARENT;
error = nfsm_chain_get_path_namei(nmreq, len, &ni);
if (!error)
#if CONFIG_FSE
if (nfsrv_fsevents_enabled && !error && need_fsevent(FSE_CREATE_FILE, dvp)) {
char *target_path = NULL;
- int plen;
+ int plen, truncated=0;
fse_info finfo;
/* build the path to the new link file */
- plen = MAXPATHLEN;
- if ((target_path = get_pathbuff()) && !vn_getpath(dvp, target_path, &plen)) {
- if ((plen + 1 + ni.ni_cnd.cn_namelen + 1) < MAXPATHLEN) {
- target_path[plen-1] = '/';
- strlcpy(&target_path[plen], ni.ni_cnd.cn_nameptr, MAXPATHLEN-plen);
- plen += ni.ni_cnd.cn_namelen;
- }
- if (get_fse_info(vp, &finfo, ctx) == 0)
+ target_path = get_pathbuff();
+ if (target_path) {
+ plen = safe_getpath(dvp, ni.ni_cnd.cn_nameptr, target_path, MAXPATHLEN, &truncated);
+
+ if (get_fse_info(vp, &finfo, ctx) == 0) {
+ if (truncated) {
+ finfo.mode |= FSE_TRUNCATED_PATH;
+ }
add_fsevent(FSE_CREATE_FILE, ctx,
FSE_ARG_STRING, plen, target_path,
FSE_ARG_FINFO, &finfo,
FSE_ARG_DONE);
- }
- if (target_path)
+ }
+
release_pathbuff(target_path);
+ }
}
#endif
struct vnode_attr va, *vap = &va;
struct nameidata ni;
int error, dpreattrerr, dpostattrerr, postattrerr;
- uint32_t len, linkdatalen;
+ uint32_t len = 0, linkdatalen, cnflags;
uid_t saved_uid;
char *linkdata;
vnode_t vp, dvp, dirp;
linkdata = NULL;
dirp = NULL;
- /*
- * Save the original credential UID in case they are
- * mapped and we need to map the IDs in the attributes.
- */
saved_uid = kauth_cred_getuid(nd->nd_cr);
ni.ni_cnd.cn_nameiop = 0;
nfsmerr_if(error);
ni.ni_cnd.cn_nameiop = CREATE;
+#if CONFIG_TRIGGERS
+ ni.ni_op = OP_LINK;
+#endif
ni.ni_cnd.cn_flags = LOCKPARENT;
error = nfsm_chain_get_path_namei(nmreq, len, &ni);
if (!error) {
goto out;
}
- /*
- * If the credentials were mapped, we should
- * map the same values in the attributes.
- */
- if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(nd->nd_cr) != saved_uid)) {
- int ismember;
- VATTR_SET(vap, va_uid, kauth_cred_getuid(nd->nd_cr));
- if (kauth_cred_ismember_gid(nd->nd_cr, vap->va_gid, &ismember) || !ismember)
- VATTR_SET(vap, va_gid, kauth_cred_getgid(nd->nd_cr));
- }
VATTR_SET(vap, va_type, VLNK);
VATTR_CLEAR_ACTIVE(vap, va_data_size);
VATTR_CLEAR_ACTIVE(vap, va_access_time);
+ /*
+ * Server policy is to alway use the mapped rpc credential for
+ * file system object creation. This has the nice side effect of
+ * enforcing BSD creation semantics
+ */
+ VATTR_CLEAR_ACTIVE(vap, va_uid);
+ VATTR_CLEAR_ACTIVE(vap, va_gid);
/* authorize before creating */
error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_FILE, ctx, nxo, 0);
/* validate given attributes */
- if (!error) {
+ if (!error)
error = vnode_authattr_new(dvp, vap, 0, ctx);
- if (error && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) {
- /*
- * Most NFS servers just ignore the UID/GID attributes, so we
- * try ignoring them if that'll help the request succeed.
- */
- VATTR_CLEAR_ACTIVE(vap, va_uid);
- VATTR_CLEAR_ACTIVE(vap, va_gid);
- error = vnode_authattr_new(dvp, vap, 0, ctx);
- }
- }
+
if (!error)
error = VNOP_SYMLINK(dvp, &vp, &ni.ni_cnd, vap, linkdata, ctx);
if (!error && (nd->nd_vers == NFS_VER3)) {
if (vp == NULL) {
ni.ni_cnd.cn_nameiop = LOOKUP;
+#if CONFIG_TRIGGERS
+ ni.ni_op = OP_LOOKUP;
+#endif
ni.ni_cnd.cn_flags &= ~(LOCKPARENT | FOLLOW);
ni.ni_cnd.cn_flags |= (NOFOLLOW | LOCKLEAF);
ni.ni_cnd.cn_context = ctx;
ni.ni_startdir = dvp;
ni.ni_usedvp = dvp;
- error = lookup(&ni);
+ cnflags = ni.ni_cnd.cn_flags; /* store in case we have to restore */
+ while ((error = lookup(&ni)) == ERECYCLE) {
+ ni.ni_cnd.cn_flags = cnflags;
+ ni.ni_cnd.cn_nameptr = ni.ni_cnd.cn_pnbuf;
+ ni.ni_usedvp = ni.ni_dvp = ni.ni_startdir = dvp;
+ }
if (!error)
vp = ni.ni_vp;
}
/*
* nfs mkdir service
*/
+
int
nfsrv_mkdir(
struct nfsrv_descript *nd,
struct vnode_attr va, *vap = &va;
struct nameidata ni;
int error, dpreattrerr, dpostattrerr, postattrerr;
- uint32_t len;
+ uint32_t len = 0;
vnode_t vp, dvp, dirp;
struct nfs_filehandle nfh;
struct nfs_export *nx = NULL;
nmreq = &nd->nd_nmreq;
nfsm_chain_null(&nmrep);
- /*
- * Save the original credential UID in case they are
- * mapped and we need to map the IDs in the attributes.
- */
saved_uid = kauth_cred_getuid(nd->nd_cr);
ni.ni_cnd.cn_nameiop = 0;
nfsmerr_if(error);
ni.ni_cnd.cn_nameiop = CREATE;
+#if CONFIG_TRIGGERS
+ ni.ni_op = OP_LINK;
+#endif
ni.ni_cnd.cn_flags = LOCKPARENT;
error = nfsm_chain_get_path_namei(nmreq, len, &ni);
if (!error) {
goto out;
}
- /*
- * If the credentials were mapped, we should
- * map the same values in the attributes.
- */
- if ((vap->va_uid == saved_uid) && (kauth_cred_getuid(nd->nd_cr) != saved_uid)) {
- int ismember;
- VATTR_SET(vap, va_uid, kauth_cred_getuid(nd->nd_cr));
- if (kauth_cred_ismember_gid(nd->nd_cr, vap->va_gid, &ismember) || !ismember)
- VATTR_SET(vap, va_gid, kauth_cred_getgid(nd->nd_cr));
- }
-
error = nfsrv_authorize(dvp, NULL, KAUTH_VNODE_ADD_SUBDIRECTORY, ctx, nxo, 0);
/* construct ACL and handle inheritance */
if (!error && xacl != NULL)
VATTR_SET(vap, va_acl, xacl);
}
+
VATTR_CLEAR_ACTIVE(vap, va_data_size);
VATTR_CLEAR_ACTIVE(vap, va_access_time);
+ /*
+ * We don't support the S_ISGID bit for directories. Solaris and other
+ * SRV4 derived systems might set this to get BSD semantics, which we enforce
+ * any ways.
+ */
+ if (VATTR_IS_ACTIVE(vap, va_mode))
+ vap->va_mode &= ~S_ISGID;
+ /*
+ * Server policy is to alway use the mapped rpc credential for
+ * file system object creation. This has the nice side effect of
+ * enforcing BSD creation semantics
+ */
+ VATTR_CLEAR_ACTIVE(vap, va_uid);
+ VATTR_CLEAR_ACTIVE(vap, va_gid);
/* validate new-file security information */
- if (!error) {
+ if (!error)
error = vnode_authattr_new(dvp, vap, 0, ctx);
- if (error && (VATTR_IS_ACTIVE(vap, va_uid) || VATTR_IS_ACTIVE(vap, va_gid))) {
- /*
- * Most NFS servers just ignore the UID/GID attributes, so we
- * try ignoring them if that'll help the request succeed.
- */
- VATTR_CLEAR_ACTIVE(vap, va_uid);
- VATTR_CLEAR_ACTIVE(vap, va_gid);
- error = vnode_authattr_new(dvp, vap, 0, ctx);
- }
- }
+ /*
+ * vnode_authattr_new can return errors other than EPERM, but that's not going to
+ * sit well with our clients so we map all errors to EPERM.
+ */
+ if (error)
+ error = EPERM;
if (!error)
error = VNOP_MKDIR(dvp, &vp, &ni.ni_cnd, vap, ctx);
mbuf_t *mrepp)
{
int error, dpreattrerr, dpostattrerr;
- uint32_t len;
+ uint32_t len = 0;
uid_t saved_uid;
vnode_t vp, dvp, dirp;
struct vnode_attr dpreattr, dpostattr;
nfsmerr_if(error);
ni.ni_cnd.cn_nameiop = DELETE;
+#if CONFIG_TRIGGERS
+ ni.ni_op = OP_UNLINK;
+#endif
ni.ni_cnd.cn_flags = LOCKPARENT | LOCKLEAF;
error = nfsm_chain_get_path_namei(nmreq, len, &ni);
if (!error) {
error = 0;
attrerr = ENOENT;
- nentries = 0;
+ count = nentries = 0;
nmreq = &nd->nd_nmreq;
nfsm_chain_null(&nmrep);
rbuf = NULL;
off = toff;
siz = ((count + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
- xfer = NFS_SRVMAXDATA(nd);
+ xfer = NFSRV_NDMAXDATA(nd);
if (siz > xfer)
siz = xfer;
fullsiz = siz;
error = nfsrv_credcheck(nd, ctx, nx, nxo);
nfsmerr_if(error);
+ if (nxo->nxo_flags & NX_MANGLEDNAMES || nd->nd_vers == NFS_VER2)
+ vnopflag |= VNODE_READDIR_NAMEMAX;
+
if ((nd->nd_vers == NFS_VER2) || (nxo->nxo_flags & NX_32BITCLIENTS))
vnopflag |= VNODE_READDIR_SEEKOFF32;
+
if (nd->nd_vers == NFS_VER3) {
nfsm_srv_vattr_init(&attr, NFS_VER3);
error = attrerr = vnode_getattr(vp, &attr, ctx);
nfsmerr_if(error);
if (uio_resid(auio) != 0) {
- // LP64todo - fix this
siz -= uio_resid(auio);
/* If nothing read, return empty reply with eof set */
nfsmerr_if(error);
off = toff;
- xfer = NFS_SRVMAXDATA(nd);
+ xfer = NFSRV_NDMAXDATA(nd);
dircount = ((dircount + DIRBLKSIZ - 1) & ~(DIRBLKSIZ - 1));
if (dircount > xfer)
dircount = xfer;
if (nxo->nxo_flags & NX_32BITCLIENTS)
vnopflag |= VNODE_READDIR_SEEKOFF32;
+ if (nxo->nxo_flags & NX_MANGLEDNAMES)
+ vnopflag |= VNODE_READDIR_NAMEMAX;
+
nfsm_srv_vattr_init(&attr, NFS_VER3);
error = attrerr = vnode_getattr(vp, &attr, ctx);
if (!error && toff && verf && (verf != attr.va_filerev))
nfsmerr_if(error);
if (uio_resid(auio) != 0) {
- // LP64todo - fix this
siz -= uio_resid(auio);
/* If nothing read, return empty reply with eof set */
maxsize = NFS_MAXDGRAMDATA;
prefsize = NFS_PREFDGRAMDATA;
} else
- maxsize = prefsize = NFS_MAXDATA;
+ maxsize = prefsize = NFSRV_MAXDATA;
nfsm_chain_add_32(error, &nmrep, maxsize);
nfsm_chain_add_32(error, &nmrep, prefsize);
* will return EPERM instead of EACCESS. EPERM is always an error.
*/
-static int
+int
nfsrv_authorize(
vnode_t vp,
vnode_t dvp,