summaryrefslogtreecommitdiffstats
path: root/sys/fs/nfsserver/nfs_nfsdserv.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/fs/nfsserver/nfs_nfsdserv.c')
-rw-r--r--sys/fs/nfsserver/nfs_nfsdserv.c3367
1 files changed, 3367 insertions, 0 deletions
diff --git a/sys/fs/nfsserver/nfs_nfsdserv.c b/sys/fs/nfsserver/nfs_nfsdserv.c
new file mode 100644
index 0000000..141a614
--- /dev/null
+++ b/sys/fs/nfsserver/nfs_nfsdserv.c
@@ -0,0 +1,3367 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * nfs version 2, 3 and 4 server calls to vnode ops
+ * - these routines generally have 3 phases
+ * 1 - break down and validate rpc request in mbuf list
+ * 2 - do the vnode ops for the request, usually by calling a nfsvno_XXX()
+ * function in nfsd_port.c
+ * 3 - build the rpc reply in an mbuf list
+ * For nfsv4, these functions are called for each Op within the Compound RPC.
+ */
+
+#ifndef APPLEKEXT
+#include <fs/nfs/nfsport.h>
+
+/* Global vars */
+extern u_int32_t newnfs_false, newnfs_true;
+extern enum vtype nv34tov_type[8];
+extern struct timeval nfsboottime;
+extern int nfs_rootfhset, nfsv4root_set;
+#endif /* !APPLEKEXT */
+
+/*
+ * This list defines the GSS mechanisms supported.
+ * (Don't ask me how you get these strings from the RFC stuff like
+ * iso(1), org(3)... but someone did it, so I don't need to know.)
+ */
+static struct nfsgss_mechlist nfsgss_mechlist[] = {
+ { 9, "\052\206\110\206\367\022\001\002\002", 11 },
+ { 0, "", 0 },
+};
+
+/* local functions */
+static void nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
+ struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
+ vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
+ int *diraft_retp, nfsattrbit_t *attrbitp,
+ NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
+ int pathlen);
+static void nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
+ struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
+ vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
+ int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
+ NFSPROC_T *p, struct nfsexstuff *exp);
+
+/*
+ * nfs access service (not a part of NFS V2)
+ */
+APPLESTATIC int
+nfsrvd_access(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ int getret, error = 0;
+ struct nfsvattr nva;
+ u_int32_t testmode, nfsmode, supported = 0;
+
+ if (nd->nd_repstat) {
+ nfsrv_postopattr(nd, 1, &nva);
+ return (0);
+ }
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
+ nfsmode = fxdr_unsigned(u_int32_t, *tl);
+ if ((nd->nd_flag & ND_NFSV4) &&
+ (nfsmode & ~(NFSACCESS_READ | NFSACCESS_LOOKUP |
+ NFSACCESS_MODIFY | NFSACCESS_EXTEND | NFSACCESS_DELETE |
+ NFSACCESS_EXECUTE))) {
+ nd->nd_repstat = NFSERR_INVAL;
+ vput(vp);
+ return (0);
+ }
+ if (nfsmode & NFSACCESS_READ) {
+ supported |= NFSACCESS_READ;
+ if (nfsvno_accchk(vp, NFSV4ACE_READDATA, nd->nd_cred, exp, p,
+ NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED))
+ nfsmode &= ~NFSACCESS_READ;
+ }
+ if (nfsmode & NFSACCESS_MODIFY) {
+ supported |= NFSACCESS_MODIFY;
+ if (nfsvno_accchk(vp, NFSV4ACE_WRITEDATA, nd->nd_cred, exp, p,
+ NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED))
+ nfsmode &= ~NFSACCESS_MODIFY;
+ }
+ if (nfsmode & NFSACCESS_EXTEND) {
+ supported |= NFSACCESS_EXTEND;
+ if (nfsvno_accchk(vp, NFSV4ACE_APPENDDATA, nd->nd_cred, exp, p,
+ NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED))
+ nfsmode &= ~NFSACCESS_EXTEND;
+ }
+ if (nfsmode & NFSACCESS_DELETE) {
+ supported |= NFSACCESS_DELETE;
+ if (nfsvno_accchk(vp, NFSV4ACE_DELETE, nd->nd_cred, exp, p,
+ NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED))
+ nfsmode &= ~NFSACCESS_DELETE;
+ }
+ if (vnode_vtype(vp) == VDIR)
+ testmode = NFSACCESS_LOOKUP;
+ else
+ testmode = NFSACCESS_EXECUTE;
+ if (nfsmode & testmode) {
+ supported |= (nfsmode & testmode);
+ if (nfsvno_accchk(vp, NFSV4ACE_EXECUTE, nd->nd_cred, exp, p,
+ NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED))
+ nfsmode &= ~testmode;
+ }
+ nfsmode &= supported;
+ if (nd->nd_flag & ND_NFSV3) {
+ getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p);
+ nfsrv_postopattr(nd, getret, &nva);
+ }
+ vput(vp);
+ if (nd->nd_flag & ND_NFSV4) {
+ NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(supported);
+ } else
+ NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
+ *tl = txdr_unsigned(nfsmode);
+ return (0);
+nfsmout:
+ vput(vp);
+ return (error);
+}
+
+/*
+ * nfs getattr service
+ */
+APPLESTATIC int
+nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram,
+ vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+ struct nfsvattr nva;
+ fhandle_t fh;
+ int error = 0;
+ struct nfsreferral *refp;
+ nfsattrbit_t attrbits;
+
+ if (nd->nd_repstat)
+ return (0);
+ if (nd->nd_flag & ND_NFSV4) {
+ error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL);
+ if (error) {
+ vput(vp);
+ return (error);
+ }
+
+ /*
+ * Check for a referral.
+ */
+ refp = nfsv4root_getreferral(vp, NULL, 0);
+ if (refp != NULL) {
+ (void) nfsrv_putreferralattr(nd, &attrbits, refp, 1,
+ &nd->nd_repstat);
+ vput(vp);
+ return (0);
+ }
+ if (!nd->nd_repstat)
+ nd->nd_repstat = nfsvno_accchk(vp,
+ NFSV4ACE_READATTRIBUTES,
+ nd->nd_cred, exp, p,
+ NFSACCCHK_NOOVERRIDE, NFSACCCHK_VPISLOCKED);
+ }
+ if (!nd->nd_repstat)
+ nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p);
+ if (!nd->nd_repstat) {
+ if (nd->nd_flag & ND_NFSV4) {
+ if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_FILEHANDLE))
+ nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
+ if (!nd->nd_repstat)
+ nd->nd_repstat = nfsrv_checkgetattr(nd, vp,
+ &nva, &attrbits, nd->nd_cred, p);
+ NFSVOPUNLOCK(vp, 0, p);
+ if (!nd->nd_repstat)
+ (void) nfsvno_fillattr(nd, vp, &nva, &fh,
+ 0, &attrbits, nd->nd_cred, p, isdgram, 1);
+ vrele(vp);
+ } else {
+ nfsrv_fillattr(nd, &nva);
+ vput(vp);
+ }
+ } else {
+ vput(vp);
+ }
+ return (0);
+}
+
+/*
+ * nfs setattr service
+ */
+APPLESTATIC int
+nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
+{
+ struct nfsvattr nva, nva2;
+ u_int32_t *tl;
+ int preat_ret = 1, postat_ret = 1, gcheck = 0, error = 0;
+ struct timespec guard = { 0, 0 };
+ nfsattrbit_t attrbits, retbits;
+ nfsv4stateid_t stateid;
+ NFSACL_T *aclp = NULL;
+
+ if (nd->nd_repstat) {
+ nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
+ return (0);
+ }
+#ifdef NFS4_ACL_EXTATTR_NAME
+ aclp = acl_alloc();
+ aclp->acl_cnt = 0;
+#endif
+ NFSVNO_ATTRINIT(&nva);
+ NFSZERO_ATTRBIT(&retbits);
+ if (nd->nd_flag & ND_NFSV4) {
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
+ stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
+ NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER);
+ }
+ error = nfsrv_sattr(nd, &nva, &attrbits, aclp, p);
+ if (error)
+ goto nfsmout;
+ preat_ret = nfsvno_getattr(vp, &nva2, nd->nd_cred, p);
+ if (!nd->nd_repstat)
+ nd->nd_repstat = preat_ret;
+ if (nd->nd_flag & ND_NFSV3) {
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
+ gcheck = fxdr_unsigned(int, *tl);
+ if (gcheck) {
+ NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+ fxdr_nfsv3time(tl, &guard);
+ }
+ if (!nd->nd_repstat && gcheck &&
+ (nva2.na_ctime.tv_sec != guard.tv_sec ||
+ nva2.na_ctime.tv_nsec != guard.tv_nsec))
+ nd->nd_repstat = NFSERR_NOT_SYNC;
+ if (nd->nd_repstat) {
+ vput(vp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
+ return (0);
+ }
+ } else if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4))
+ nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
+
+ /*
+ * Now that we have all the fields, lets do it.
+ * If the size is being changed write access is required, otherwise
+ * just check for a read only file system.
+ */
+ if (!nd->nd_repstat) {
+ if (NFSVNO_NOTSETSIZE(&nva)) {
+ if (NFSVNO_EXRDONLY(exp) ||
+ (vfs_flags(vnode_mount(vp)) & MNT_RDONLY))
+ nd->nd_repstat = EROFS;
+ } else {
+ if (vnode_vtype(vp) != VREG)
+ nd->nd_repstat = EINVAL;
+ else if (nva2.na_uid != nd->nd_cred->cr_uid ||
+ NFSVNO_EXSTRICTACCESS(exp))
+ nd->nd_repstat = nfsvno_accchk(vp,
+ NFSV4ACE_WRITEDATA, nd->nd_cred, exp, p,
+ NFSACCCHK_NOOVERRIDE,NFSACCCHK_VPISLOCKED);
+ }
+ }
+ if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4))
+ nd->nd_repstat = nfsrv_checksetattr(vp, nd, &stateid,
+ &nva, &attrbits, exp, p);
+
+ if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
+ /*
+ * For V4, try setting the attrbutes in sets, so that the
+ * reply bitmap will be correct for an error case.
+ */
+ if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER) ||
+ NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP)) {
+ NFSVNO_ATTRINIT(&nva2);
+ NFSVNO_SETATTRVAL(&nva2, uid, nva.na_uid);
+ NFSVNO_SETATTRVAL(&nva2, gid, nva.na_gid);
+ nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
+ exp);
+ if (!nd->nd_repstat) {
+ if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNER))
+ NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNER);
+ if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_OWNERGROUP))
+ NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_OWNERGROUP);
+ }
+ }
+ if (!nd->nd_repstat &&
+ NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_SIZE)) {
+ NFSVNO_ATTRINIT(&nva2);
+ NFSVNO_SETATTRVAL(&nva2, size, nva.na_size);
+ nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
+ exp);
+ if (!nd->nd_repstat)
+ NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_SIZE);
+ }
+ if (!nd->nd_repstat &&
+ (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET) ||
+ NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))) {
+ NFSVNO_ATTRINIT(&nva2);
+ NFSVNO_SETATTRVAL(&nva2, atime, nva.na_atime);
+ NFSVNO_SETATTRVAL(&nva2, mtime, nva.na_mtime);
+ if (nva.na_vaflags & VA_UTIMES_NULL) {
+ nva2.na_vaflags |= VA_UTIMES_NULL;
+ NFSVNO_SETACTIVE(&nva2, vaflags);
+ }
+ nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
+ exp);
+ if (!nd->nd_repstat) {
+ if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET))
+ NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEACCESSSET);
+ if (NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET))
+ NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMEMODIFYSET);
+ }
+ }
+ if (!nd->nd_repstat &&
+ NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_MODE)) {
+ NFSVNO_ATTRINIT(&nva2);
+ NFSVNO_SETATTRVAL(&nva2, mode, nva.na_mode);
+ nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
+ exp);
+ if (!nd->nd_repstat)
+ NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_MODE);
+ }
+
+#ifdef NFS4_ACL_EXTATTR_NAME
+ if (!nd->nd_repstat && aclp->acl_cnt > 0 &&
+ NFSISSET_ATTRBIT(&attrbits, NFSATTRBIT_ACL)) {
+ nd->nd_repstat = nfsrv_setacl(vp, aclp, nd->nd_cred, p);
+ if (!nd->nd_repstat)
+ NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_ACL);
+ }
+#endif
+ } else if (!nd->nd_repstat) {
+ nd->nd_repstat = nfsvno_setattr(vp, &nva, nd->nd_cred, p,
+ exp);
+ }
+ if (nd->nd_flag & (ND_NFSV2 | ND_NFSV3)) {
+ postat_ret = nfsvno_getattr(vp, &nva, nd->nd_cred, p);
+ if (!nd->nd_repstat)
+ nd->nd_repstat = postat_ret;
+ }
+ vput(vp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ if (nd->nd_flag & ND_NFSV3)
+ nfsrv_wcc(nd, preat_ret, &nva2, postat_ret, &nva);
+ else if (nd->nd_flag & ND_NFSV4)
+ (void) nfsrv_putattrbit(nd, &retbits);
+ else if (!nd->nd_repstat)
+ nfsrv_fillattr(nd, &nva);
+ return (0);
+nfsmout:
+ vput(vp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ if (nd->nd_flag & ND_NFSV4) {
+ /*
+ * For all nd_repstat, the V4 reply includes a bitmap,
+ * even NFSERR_BADXDR, which is what this will end up
+ * returning.
+ */
+ (void) nfsrv_putattrbit(nd, &retbits);
+ }
+ return (error);
+}
+
+/*
+ * nfs lookup rpc
+ * (Also performs lookup parent for v4)
+ */
+APPLESTATIC int
+nfsrvd_lookup(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
+ __unused struct nfsexstuff *exp)
+{
+ struct nameidata named;
+ vnode_t vp, dirp = NULL;
+ int error, dattr_ret = 1;
+ struct nfsvattr nva, dattr;
+ char *bufp;
+ u_long *hashp;
+
+ if (nd->nd_repstat) {
+ nfsrv_postopattr(nd, dattr_ret, &dattr);
+ return (0);
+ }
+
+ /*
+ * For some reason, if dp is a symlink, the error
+ * returned is supposed to be NFSERR_SYMLINK and not NFSERR_NOTDIR.
+ */
+ if (dp->v_type == VLNK && (nd->nd_flag & ND_NFSV4)) {
+ nd->nd_repstat = NFSERR_SYMLINK;
+ vrele(dp);
+ return (0);
+ }
+
+ NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
+ LOCKLEAF | SAVESTART);
+ nfsvno_setpathbuf(&named, &bufp, &hashp);
+ error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
+ if (error) {
+ vrele(dp);
+ nfsvno_relpathbuf(&named);
+ return (error);
+ }
+ if (!nd->nd_repstat) {
+ nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
+ } else {
+ vrele(dp);
+ nfsvno_relpathbuf(&named);
+ }
+ if (nd->nd_repstat) {
+ if (dirp) {
+ if (nd->nd_flag & ND_NFSV3)
+ dattr_ret = nfsvno_getattr(dirp, &dattr,
+ nd->nd_cred, p);
+ vrele(dirp);
+ }
+ if (nd->nd_flag & ND_NFSV3)
+ nfsrv_postopattr(nd, dattr_ret, &dattr);
+ return (0);
+ }
+ if (named.ni_startdir)
+ vrele(named.ni_startdir);
+ nfsvno_relpathbuf(&named);
+ vp = named.ni_vp;
+ nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
+ if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
+ nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p);
+ if (vpp) {
+ NFSVOPUNLOCK(vp, 0, p);
+ *vpp = vp;
+ } else {
+ vput(vp);
+ }
+ if (dirp) {
+ if (nd->nd_flag & ND_NFSV3)
+ dattr_ret = nfsvno_getattr(dirp, &dattr, nd->nd_cred,
+ p);
+ vrele(dirp);
+ }
+ if (nd->nd_repstat) {
+ if (nd->nd_flag & ND_NFSV3)
+ nfsrv_postopattr(nd, dattr_ret, &dattr);
+ return (0);
+ }
+ if (nd->nd_flag & ND_NFSV2) {
+ (void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
+ nfsrv_fillattr(nd, &nva);
+ } else if (nd->nd_flag & ND_NFSV3) {
+ (void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
+ nfsrv_postopattr(nd, 0, &nva);
+ nfsrv_postopattr(nd, dattr_ret, &dattr);
+ }
+ return (0);
+}
+
+/*
+ * nfs readlink service
+ */
+APPLESTATIC int
+nfsrvd_readlink(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ mbuf_t mp = NULL, mpend = NULL;
+ int getret = 1, len;
+ struct nfsvattr nva;
+
+ if (nd->nd_repstat) {
+ nfsrv_postopattr(nd, getret, &nva);
+ return (0);
+ }
+ if (vnode_vtype(vp) != VLNK) {
+ if (nd->nd_flag & ND_NFSV2)
+ nd->nd_repstat = ENXIO;
+ else
+ nd->nd_repstat = EINVAL;
+ }
+ if (!nd->nd_repstat)
+ nd->nd_repstat = nfsvno_readlink(vp, nd->nd_cred, p,
+ &mp, &mpend, &len);
+ if (nd->nd_flag & ND_NFSV3)
+ getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p);
+ vput(vp);
+ if (nd->nd_flag & ND_NFSV3)
+ nfsrv_postopattr(nd, getret, &nva);
+ if (nd->nd_repstat)
+ return (0);
+ NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
+ *tl = txdr_unsigned(len);
+ mbuf_setnext(nd->nd_mb, mp);
+ nd->nd_mb = mpend;
+ nd->nd_bpos = NFSMTOD(mpend, caddr_t) + mbuf_len(mpend);
+ return (0);
+}
+
+/*
+ * nfs read service
+ */
+APPLESTATIC int
+nfsrvd_read(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ int error = 0, cnt, len, getret = 1, reqlen, eof = 0;
+ mbuf_t m2, m3;
+ struct nfsvattr nva;
+ off_t off = 0x0;
+ struct nfsstate st, *stp = &st;
+ struct nfslock lo, *lop = &lo;
+ nfsv4stateid_t stateid;
+ nfsquad_t clientid;
+
+ if (nd->nd_repstat) {
+ nfsrv_postopattr(nd, getret, &nva);
+ return (0);
+ }
+ if (nd->nd_flag & ND_NFSV2) {
+ NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+ off = (off_t)fxdr_unsigned(u_int32_t, *tl++);
+ reqlen = fxdr_unsigned(int, *tl);
+ } else if (nd->nd_flag & ND_NFSV3) {
+ NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
+ off = fxdr_hyper(tl);
+ tl += 2;
+ reqlen = fxdr_unsigned(int, *tl);
+ } else {
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3*NFSX_UNSIGNED);
+ reqlen = fxdr_unsigned(int, *(tl + 6));
+ }
+ if (reqlen > NFS_SRVMAXDATA(nd)) {
+ reqlen = NFS_SRVMAXDATA(nd);
+ } else if (reqlen < 0) {
+ error = EBADRPC;
+ goto nfsmout;
+ }
+ if (nd->nd_flag & ND_NFSV4) {
+ stp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS);
+ lop->lo_flags = NFSLCK_READ;
+ stp->ls_ownerlen = 0;
+ stp->ls_op = NULL;
+ stp->ls_uid = nd->nd_cred->cr_uid;
+ stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
+ clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
+ clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
+ if (nd->nd_flag & ND_IMPLIEDCLID) {
+ if (nd->nd_clientid.qval != clientid.qval)
+ printf("EEK! multiple clids\n");
+ } else {
+ nd->nd_flag |= ND_IMPLIEDCLID;
+ nd->nd_clientid.qval = clientid.qval;
+ }
+ stp->ls_stateid.other[2] = *tl++;
+ off = fxdr_hyper(tl);
+ lop->lo_first = off;
+ tl += 2;
+ lop->lo_end = off + reqlen;
+ /*
+ * Paranoia, just in case it wraps around.
+ */
+ if (lop->lo_end < off)
+ lop->lo_end = NFS64BITSSET;
+ }
+ if (vnode_vtype(vp) != VREG) {
+ if (nd->nd_flag & ND_NFSV3)
+ nd->nd_repstat = EINVAL;
+ else
+ nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR :
+ EINVAL;
+ }
+ getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p);
+ if (!nd->nd_repstat)
+ nd->nd_repstat = getret;
+ if (!nd->nd_repstat &&
+ (nva.na_uid != nd->nd_cred->cr_uid ||
+ NFSVNO_EXSTRICTACCESS(exp))) {
+ nd->nd_repstat = nfsvno_accchk(vp, NFSV4ACE_READDATA,
+ nd->nd_cred, exp, p,
+ NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED);
+ if (nd->nd_repstat)
+ nd->nd_repstat = nfsvno_accchk(vp, NFSV4ACE_EXECUTE,
+ nd->nd_cred, exp, p,
+ NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED);
+ }
+ if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
+ nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
+ &stateid, exp, nd, p);
+ if (nd->nd_repstat) {
+ vput(vp);
+ if (nd->nd_flag & ND_NFSV3)
+ nfsrv_postopattr(nd, getret, &nva);
+ return (0);
+ }
+ if (off >= nva.na_size) {
+ cnt = 0;
+ eof = 1;
+ } else if (reqlen == 0)
+ cnt = 0;
+ else if ((off + reqlen) > nva.na_size)
+ cnt = nva.na_size - off;
+ else
+ cnt = reqlen;
+ len = NFSM_RNDUP(cnt);
+ m3 = NULL;
+ if (cnt > 0) {
+ nd->nd_repstat = nfsvno_read(vp, off, cnt, nd->nd_cred, p,
+ &m3, &m2);
+ if (!(nd->nd_flag & ND_NFSV4)) {
+ getret = nfsvno_getattr(vp, &nva, nd->nd_cred, p);
+ if (!nd->nd_repstat)
+ nd->nd_repstat = getret;
+ }
+ if (nd->nd_repstat) {
+ vput(vp);
+ if (m3)
+ mbuf_freem(m3);
+ if (nd->nd_flag & ND_NFSV3)
+ nfsrv_postopattr(nd, getret, &nva);
+ return (0);
+ }
+ }
+ vput(vp);
+ if (nd->nd_flag & ND_NFSV2) {
+ nfsrv_fillattr(nd, &nva);
+ NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
+ } else {
+ if (nd->nd_flag & ND_NFSV3) {
+ nfsrv_postopattr(nd, getret, &nva);
+ NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(cnt);
+ } else
+ NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+ if (len < reqlen || eof)
+ *tl++ = newnfs_true;
+ else
+ *tl++ = newnfs_false;
+ }
+ *tl = txdr_unsigned(cnt);
+ if (m3) {
+ mbuf_setnext(nd->nd_mb, m3);
+ nd->nd_mb = m2;
+ nd->nd_bpos = NFSMTOD(m2, caddr_t) + mbuf_len(m2);
+ }
+ return (0);
+nfsmout:
+ vput(vp);
+ return (error);
+}
+
+/*
+ * nfs write service
+ */
+APPLESTATIC int
+nfsrvd_write(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
+{
+ int i, cnt;
+ u_int32_t *tl;
+ mbuf_t mp;
+ struct nfsvattr nva, forat;
+ int aftat_ret = 1, retlen, len, error = 0, forat_ret = 1;
+ int stable = NFSWRITE_FILESYNC;
+ off_t off;
+ struct nfsstate st, *stp = &st;
+ struct nfslock lo, *lop = &lo;
+ nfsv4stateid_t stateid;
+ nfsquad_t clientid;
+
+ if (nd->nd_repstat) {
+ nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
+ return (0);
+ }
+ if (nd->nd_flag & ND_NFSV2) {
+ NFSM_DISSECT(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
+ off = (off_t)fxdr_unsigned(u_int32_t, *++tl);
+ tl += 2;
+ retlen = len = fxdr_unsigned(int32_t, *tl);
+ } else if (nd->nd_flag & ND_NFSV3) {
+ NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
+ off = fxdr_hyper(tl);
+ tl += 3;
+ stable = fxdr_unsigned(int, *tl++);
+ retlen = len = fxdr_unsigned(int32_t, *tl);
+ } else {
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 4 * NFSX_UNSIGNED);
+ stp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS);
+ lop->lo_flags = NFSLCK_WRITE;
+ stp->ls_ownerlen = 0;
+ stp->ls_op = NULL;
+ stp->ls_uid = nd->nd_cred->cr_uid;
+ stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
+ clientid.lval[0] = stp->ls_stateid.other[0] = *tl++;
+ clientid.lval[1] = stp->ls_stateid.other[1] = *tl++;
+ if (nd->nd_flag & ND_IMPLIEDCLID) {
+ if (nd->nd_clientid.qval != clientid.qval)
+ printf("EEK! multiple clids\n");
+ } else {
+ nd->nd_flag |= ND_IMPLIEDCLID;
+ nd->nd_clientid.qval = clientid.qval;
+ }
+ stp->ls_stateid.other[2] = *tl++;
+ off = fxdr_hyper(tl);
+ lop->lo_first = off;
+ tl += 2;
+ stable = fxdr_unsigned(int, *tl++);
+ retlen = len = fxdr_unsigned(int32_t, *tl);
+ lop->lo_end = off + len;
+ /*
+ * Paranoia, just in case it wraps around, which shouldn't
+ * ever happen anyhow.
+ */
+ if (lop->lo_end < lop->lo_first)
+ lop->lo_end = NFS64BITSSET;
+ }
+
+ /*
+ * Loop through the mbuf chain, counting how many mbufs are a
+ * part of this write operation, so the iovec size is known.
+ */
+ cnt = 0;
+ mp = nd->nd_md;
+ i = NFSMTOD(mp, caddr_t) + mbuf_len(mp) - nd->nd_dpos;
+ while (len > 0) {
+ if (i > 0) {
+ len -= i;
+ cnt++;
+ }
+ mp = mbuf_next(mp);
+ if (!mp) {
+ if (len > 0) {
+ error = EBADRPC;
+ goto nfsmout;
+ }
+ } else
+ i = mbuf_len(mp);
+ }
+
+ if (retlen > NFS_MAXDATA || retlen < 0)
+ nd->nd_repstat = EIO;
+ if (vnode_vtype(vp) != VREG && !nd->nd_repstat) {
+ if (nd->nd_flag & ND_NFSV3)
+ nd->nd_repstat = EINVAL;
+ else
+ nd->nd_repstat = (vnode_vtype(vp) == VDIR) ? EISDIR :
+ EINVAL;
+ }
+ forat_ret = nfsvno_getattr(vp, &forat, nd->nd_cred, p);
+ if (!nd->nd_repstat)
+ nd->nd_repstat = forat_ret;
+ if (!nd->nd_repstat &&
+ (forat.na_uid != nd->nd_cred->cr_uid ||
+ NFSVNO_EXSTRICTACCESS(exp)))
+ nd->nd_repstat = nfsvno_accchk(vp, NFSV4ACE_WRITEDATA,
+ nd->nd_cred, exp, p,
+ NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED);
+ if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
+ nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
+ &stateid, exp, nd, p);
+ }
+ if (nd->nd_repstat) {
+ vput(vp);
+ if (nd->nd_flag & ND_NFSV3)
+ nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
+ return (0);
+ }
+
+ /*
+ * For NFS Version 2, it is not obvious what a write of zero length
+ * should do, but I might as well be consistent with Version 3,
+ * which is to return ok so long as there are no permission problems.
+ */
+ if (retlen > 0) {
+ nd->nd_repstat = nfsvno_write(vp, off, retlen, cnt, stable,
+ nd->nd_md, nd->nd_dpos, nd->nd_cred, p);
+ error = nfsm_advance(nd, NFSM_RNDUP(retlen), -1);
+ if (error)
+ panic("nfsrv_write mbuf");
+ }
+ if (nd->nd_flag & ND_NFSV4)
+ aftat_ret = 0;
+ else
+ aftat_ret = nfsvno_getattr(vp, &nva, nd->nd_cred, p);
+ vput(vp);
+ if (!nd->nd_repstat)
+ nd->nd_repstat = aftat_ret;
+ if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4)) {
+ if (nd->nd_flag & ND_NFSV3)
+ nfsrv_wcc(nd, forat_ret, &forat, aftat_ret, &nva);
+ if (nd->nd_repstat)
+ return (0);
+ NFSM_BUILD(tl, u_int32_t *, 4 * NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(retlen);
+ if (stable == NFSWRITE_UNSTABLE)
+ *tl++ = txdr_unsigned(stable);
+ else
+ *tl++ = txdr_unsigned(NFSWRITE_FILESYNC);
+ /*
+ * Actually, there is no need to txdr these fields,
+ * but it may make the values more human readable,
+ * for debugging purposes.
+ */
+ *tl++ = txdr_unsigned(nfsboottime.tv_sec);
+ *tl = txdr_unsigned(nfsboottime.tv_usec);
+ } else if (!nd->nd_repstat)
+ nfsrv_fillattr(nd, &nva);
+ return (0);
+nfsmout:
+ vput(vp);
+ return (error);
+}
+
+/*
+ * nfs create service (creates regular files for V2 and V3. Spec. files for V2.)
+ * now does a truncate to 0 length via. setattr if it already exists
+ * The core creation routine has been extracted out into nfsrv_creatsub(),
+ * so it can also be used by nfsrv_open() for V4.
+ */
+APPLESTATIC int
+nfsrvd_create(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
+{
+ struct nfsvattr nva, dirfor, diraft;
+ struct nfsv2_sattr *sp;
+ struct nameidata named;
+ u_int32_t *tl;
+ int error = 0, tsize, dirfor_ret = 1, diraft_ret = 1;
+ int how = NFSCREATE_UNCHECKED, exclusive_flag = 0;
+ NFSDEV_T rdev = 0;
+ vnode_t vp = NULL, dirp = NULL;
+ u_char cverf[NFSX_VERF], *cp;
+ fhandle_t fh;
+ char *bufp;
+ u_long *hashp;
+ enum vtype vtyp;
+
+ if (nd->nd_repstat) {
+ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
+ return (0);
+ }
+ NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
+ LOCKPARENT | LOCKLEAF | SAVESTART);
+ nfsvno_setpathbuf(&named, &bufp, &hashp);
+ error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
+ if (error) {
+ vput(dp);
+ nfsvno_relpathbuf(&named);
+ return (error);
+ }
+ if (!nd->nd_repstat) {
+ NFSVNO_ATTRINIT(&nva);
+ if (nd->nd_flag & ND_NFSV2) {
+ NFSM_DISSECT(sp, struct nfsv2_sattr *, NFSX_V2SATTR);
+ vtyp = IFTOVT(fxdr_unsigned(u_int32_t, sp->sa_mode));
+ if (vtyp == VNON)
+ vtyp = VREG;
+ NFSVNO_SETATTRVAL(&nva, type, vtyp);
+ NFSVNO_SETATTRVAL(&nva, mode,
+ nfstov_mode(sp->sa_mode));
+ switch (nva.na_type) {
+ case VREG:
+ tsize = fxdr_unsigned(int32_t, sp->sa_size);
+ if (tsize != -1)
+ NFSVNO_SETATTRVAL(&nva, size,
+ (u_quad_t)tsize);
+ break;
+ case VCHR:
+ case VBLK:
+ case VFIFO:
+ rdev = fxdr_unsigned(NFSDEV_T, sp->sa_size);
+ break;
+ default:
+ break;
+ };
+ } else {
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
+ how = fxdr_unsigned(int, *tl);
+ switch (how) {
+ case NFSCREATE_GUARDED:
+ case NFSCREATE_UNCHECKED:
+ error = nfsrv_sattr(nd, &nva, NULL, NULL, p);
+ if (error)
+ goto nfsmout;
+ break;
+ case NFSCREATE_EXCLUSIVE:
+ NFSM_DISSECT(cp, u_char *, NFSX_VERF);
+ NFSBCOPY(cp, cverf, NFSX_VERF);
+ exclusive_flag = 1;
+ break;
+ };
+ NFSVNO_SETATTRVAL(&nva, type, VREG);
+ }
+ }
+ if (nd->nd_repstat) {
+ nfsvno_relpathbuf(&named);
+ if (nd->nd_flag & ND_NFSV3) {
+ dirfor_ret = nfsvno_getattr(dp, &dirfor, nd->nd_cred,
+ p);
+ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
+ &diraft);
+ }
+ vput(dp);
+ return (0);
+ }
+
+ nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
+ if (dirp) {
+ if (nd->nd_flag & ND_NFSV2) {
+ vrele(dirp);
+ dirp = NULL;
+ } else {
+ dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred,
+ p);
+ }
+ }
+ if (nd->nd_repstat) {
+ if (nd->nd_flag & ND_NFSV3)
+ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
+ &diraft);
+ if (dirp)
+ vrele(dirp);
+ return (0);
+ }
+
+ if (!(nd->nd_flag & ND_NFSV2)) {
+ switch (how) {
+ case NFSCREATE_GUARDED:
+ if (named.ni_vp)
+ nd->nd_repstat = EEXIST;
+ break;
+ case NFSCREATE_UNCHECKED:
+ break;
+ case NFSCREATE_EXCLUSIVE:
+ if (named.ni_vp == NULL)
+ NFSVNO_SETATTRVAL(&nva, mode, 0);
+ break;
+ };
+ }
+
+ /*
+ * Iff doesn't exist, create it
+ * otherwise just truncate to 0 length
+ * should I set the mode too ?
+ */
+ nd->nd_repstat = nfsvno_createsub(nd, &named, &vp, &nva,
+ &exclusive_flag, cverf, rdev, p, exp);
+
+ if (!nd->nd_repstat) {
+ nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
+ if (!nd->nd_repstat)
+ nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred,
+ p);
+ vput(vp);
+ }
+ if (nd->nd_flag & ND_NFSV2) {
+ if (!nd->nd_repstat) {
+ (void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
+ nfsrv_fillattr(nd, &nva);
+ }
+ } else {
+ if (exclusive_flag && !nd->nd_repstat &&
+ NFSBCMP(cverf, (caddr_t)&nva.na_atime, NFSX_VERF))
+ nd->nd_repstat = EEXIST;
+ diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p);
+ vrele(dirp);
+ if (!nd->nd_repstat) {
+ (void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 1);
+ nfsrv_postopattr(nd, 0, &nva);
+ }
+ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
+ }
+ return (0);
+nfsmout:
+ vput(dp);
+ nfsvno_relpathbuf(&named);
+ return (error);
+}
+
+/*
+ * nfs v3 mknod service (and v4 create)
+ */
+APPLESTATIC int
+nfsrvd_mknod(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
+ struct nfsexstuff *exp)
+{
+ struct nfsvattr nva, dirfor, diraft;
+ u_int32_t *tl;
+ struct nameidata named;
+ int error = 0, dirfor_ret = 1, diraft_ret = 1, pathlen;
+ u_int32_t major, minor;
+ enum vtype vtyp = VNON;
+ nfstype nfs4type = NFNON;
+ vnode_t vp, dirp = NULL;
+ nfsattrbit_t attrbits;
+ char *bufp = NULL, *pathcp = NULL;
+ u_long *hashp, cnflags;
+ NFSACL_T *aclp = NULL;
+
+ NFSVNO_ATTRINIT(&nva);
+ cnflags = (LOCKPARENT | SAVESTART);
+ if (nd->nd_repstat) {
+ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
+ return (0);
+ }
+#ifdef NFS4_ACL_EXTATTR_NAME
+ aclp = acl_alloc();
+ aclp->acl_cnt = 0;
+#endif
+
+ /*
+ * For V4, the creation stuff is here, Yuck!
+ */
+ if (nd->nd_flag & ND_NFSV4) {
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
+ vtyp = nfsv34tov_type(*tl);
+ nfs4type = fxdr_unsigned(nfstype, *tl);
+ switch (nfs4type) {
+ case NFLNK:
+ error = nfsvno_getsymlink(nd, &nva, p, &pathcp,
+ &pathlen);
+ if (error) {
+ vrele(dp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ return (error);
+ }
+ break;
+ case NFCHR:
+ case NFBLK:
+ NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+ major = fxdr_unsigned(u_int32_t, *tl++);
+ minor = fxdr_unsigned(u_int32_t, *tl);
+ nva.na_rdev = NFSMAKEDEV(major, minor);
+ break;
+ case NFSOCK:
+ case NFFIFO:
+ break;
+ case NFDIR:
+ cnflags = LOCKPARENT;
+ break;
+ default:
+ nd->nd_repstat = NFSERR_BADTYPE;
+ vrele(dp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ return (0);
+ };
+ }
+ NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, cnflags);
+ nfsvno_setpathbuf(&named, &bufp, &hashp);
+ error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
+ if (error) {
+ vrele(dp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ nfsvno_relpathbuf(&named);
+ if (pathcp)
+ FREE(pathcp, M_TEMP);
+ return (error);
+ }
+ if (!nd->nd_repstat) {
+ if (nd->nd_flag & ND_NFSV3) {
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
+ vtyp = nfsv34tov_type(*tl);
+ }
+ error = nfsrv_sattr(nd, &nva, &attrbits, aclp, p);
+ if (error) {
+ vrele(dp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ nfsvno_relpathbuf(&named);
+ if (pathcp)
+ FREE(pathcp, M_TEMP);
+ return (error);
+ }
+ nva.na_type = vtyp;
+ if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV3) &&
+ (vtyp == VCHR || vtyp == VBLK)) {
+ NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+ major = fxdr_unsigned(u_int32_t, *tl++);
+ minor = fxdr_unsigned(u_int32_t, *tl);
+ nva.na_rdev = NFSMAKEDEV(major, minor);
+ }
+ }
+
+ dirfor_ret = nfsvno_getattr(dp, &dirfor, nd->nd_cred, p);
+ if (!nd->nd_repstat && (nd->nd_flag & ND_NFSV4)) {
+ if (!dirfor_ret && NFSVNO_ISSETGID(&nva) &&
+ dirfor.na_gid == nva.na_gid)
+ NFSVNO_UNSET(&nva, gid);
+ nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
+ }
+ if (nd->nd_repstat) {
+ vrele(dp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ nfsvno_relpathbuf(&named);
+ if (pathcp)
+ FREE(pathcp, M_TEMP);
+ if (nd->nd_flag & ND_NFSV3)
+ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
+ &diraft);
+ return (0);
+ }
+
+ /*
+ * Yuck! For V4, mkdir and link are here and some V4 clients don't fill
+ * in va_mode, so we'll have to set a default here.
+ */
+ if (NFSVNO_NOTSETMODE(&nva)) {
+ if (vtyp == VLNK)
+ nva.na_mode = 0755;
+ else
+ nva.na_mode = 0400;
+ }
+
+ if (vtyp == VDIR)
+ named.ni_cnd.cn_flags |= WILLBEDIR;
+ nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
+ if (nd->nd_repstat) {
+ if (dirp) {
+ if (nd->nd_flag & ND_NFSV3)
+ dirfor_ret = nfsvno_getattr(dirp, &dirfor,
+ nd->nd_cred, p);
+ vrele(dirp);
+ }
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ if (nd->nd_flag & ND_NFSV3)
+ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
+ &diraft);
+ return (0);
+ }
+ if (dirp)
+ dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p);
+
+ if ((nd->nd_flag & ND_NFSV4) && (vtyp == VDIR || vtyp == VLNK)) {
+ if (vtyp == VDIR) {
+ nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp,
+ &dirfor, &diraft, &diraft_ret, &attrbits, aclp, p,
+ exp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ return (0);
+ } else if (vtyp == VLNK) {
+ nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
+ &dirfor, &diraft, &diraft_ret, &attrbits,
+ aclp, p, exp, pathcp, pathlen);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ FREE(pathcp, M_TEMP);
+ return (0);
+ }
+ }
+
+ nd->nd_repstat = nfsvno_mknod(&named, &nva, nd->nd_cred, p);
+ if (!nd->nd_repstat) {
+ vp = named.ni_vp;
+ nfsrv_fixattr(nd, vp, &nva, aclp, p, &attrbits, exp);
+ nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
+ if ((nd->nd_flag & ND_NFSV3) && !nd->nd_repstat)
+ nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred,
+ p);
+ if (vpp) {
+ NFSVOPUNLOCK(vp, 0, p);
+ *vpp = vp;
+ } else {
+ vput(vp);
+ }
+ }
+
+ diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p);
+ vrele(dirp);
+ if (!nd->nd_repstat) {
+ if (nd->nd_flag & ND_NFSV3) {
+ (void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
+ nfsrv_postopattr(nd, 0, &nva);
+ } else {
+ NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
+ *tl++ = newnfs_false;
+ txdr_hyper(dirfor.na_filerev, tl);
+ tl += 2;
+ txdr_hyper(diraft.na_filerev, tl);
+ (void) nfsrv_putattrbit(nd, &attrbits);
+ }
+ }
+ if (nd->nd_flag & ND_NFSV3)
+ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ return (0);
+nfsmout:
+ vrele(dp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ if (bufp)
+ nfsvno_relpathbuf(&named);
+ if (pathcp)
+ FREE(pathcp, M_TEMP);
+ return (error);
+}
+
+/*
+ * nfs remove service
+ */
+APPLESTATIC int
+nfsrvd_remove(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
+{
+ struct nameidata named;
+ u_int32_t *tl;
+ int error, dirfor_ret = 1, diraft_ret = 1;
+ vnode_t dirp = NULL;
+ struct nfsvattr dirfor, diraft;
+ char *bufp;
+ u_long *hashp;
+
+ if (nd->nd_repstat) {
+ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
+ return (0);
+ }
+ NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, DELETE,
+ LOCKPARENT | LOCKLEAF);
+ nfsvno_setpathbuf(&named, &bufp, &hashp);
+ error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
+ if (error) {
+ vput(dp);
+ nfsvno_relpathbuf(&named);
+ return (error);
+ }
+ if (!nd->nd_repstat) {
+ nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
+ } else {
+ vput(dp);
+ nfsvno_relpathbuf(&named);
+ }
+ if (dirp) {
+ if (!(nd->nd_flag & ND_NFSV2)) {
+ dirfor_ret = nfsvno_getattr(dirp, &dirfor,
+ nd->nd_cred, p);
+ } else {
+ vrele(dirp);
+ dirp = NULL;
+ }
+ }
+ if (!nd->nd_repstat) {
+ if (nd->nd_flag & ND_NFSV4) {
+ if (vnode_vtype(named.ni_vp) == VDIR)
+ nd->nd_repstat = nfsvno_rmdirsub(&named, 1,
+ nd->nd_cred, p, exp);
+ else
+ nd->nd_repstat = nfsvno_removesub(&named, 1,
+ nd->nd_cred, p, exp);
+ } else if (nd->nd_procnum == NFSPROC_RMDIR) {
+ nd->nd_repstat = nfsvno_rmdirsub(&named, 0,
+ nd->nd_cred, p, exp);
+ } else {
+ nd->nd_repstat = nfsvno_removesub(&named, 0,
+ nd->nd_cred, p, exp);
+ }
+ }
+ if (!(nd->nd_flag & ND_NFSV2)) {
+ if (dirp) {
+ diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred,
+ p);
+ vrele(dirp);
+ }
+ if (nd->nd_flag & ND_NFSV3) {
+ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
+ &diraft);
+ } else if (!nd->nd_repstat) {
+ NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
+ *tl++ = newnfs_false;
+ txdr_hyper(dirfor.na_filerev, tl);
+ tl += 2;
+ txdr_hyper(diraft.na_filerev, tl);
+ }
+ }
+ return (0);
+}
+
+/*
+ * nfs rename service
+ */
+APPLESTATIC int
+nfsrvd_rename(struct nfsrv_descript *nd, int isdgram,
+ vnode_t dp, vnode_t todp, NFSPROC_T *p, struct nfsexstuff *exp,
+ struct nfsexstuff *toexp)
+{
+ u_int32_t *tl;
+ int error, fdirfor_ret = 1, fdiraft_ret = 1;
+ int tdirfor_ret = 1, tdiraft_ret = 1;
+ struct nameidata fromnd, tond;
+ vnode_t fdirp = NULL, tdirp = NULL, tdp = NULL;
+ struct nfsvattr fdirfor, fdiraft, tdirfor, tdiraft;
+ struct nfsexstuff tnes;
+ struct nfsrvfh tfh;
+ mount_t mp = NULL;
+ char *bufp, *tbufp = NULL;
+ u_long *hashp;
+
+ if (nd->nd_repstat) {
+ nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
+ nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
+ return (0);
+ }
+ if (!(nd->nd_flag & ND_NFSV2))
+ fdirfor_ret = nfsvno_getattr(dp, &fdirfor, nd->nd_cred, p);
+ tond.ni_cnd.cn_nameiop = 0;
+ tond.ni_startdir = NULL;
+ NFSNAMEICNDSET(&fromnd.ni_cnd, nd->nd_cred, DELETE, WANTPARENT | SAVESTART);
+ nfsvno_setpathbuf(&fromnd, &bufp, &hashp);
+ error = nfsrv_parsename(nd, bufp, hashp, &fromnd.ni_pathlen);
+ if (error) {
+ vput(dp);
+ if (todp)
+ vrele(todp);
+ nfsvno_relpathbuf(&fromnd);
+ return (error);
+ }
+ if (nd->nd_flag & ND_NFSV4) {
+ tdp = todp;
+ tnes = *toexp;
+ tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred, p);
+ } else {
+ error = nfsrv_mtofh(nd, &tfh);
+ if (error) {
+ vput(dp);
+ /* todp is always NULL except NFSv4 */
+ nfsvno_relpathbuf(&fromnd);
+ return (error);
+ }
+ nd->nd_cred->cr_uid = nd->nd_saveduid;
+ /* Won't lock vfs if already locked, mp == NULL */
+ tnes.nes_vfslocked = exp->nes_vfslocked;
+ nfsd_fhtovp(nd, &tfh, &tdp, &tnes, &mp, 0, p);
+ if (tdp) {
+ tdirfor_ret = nfsvno_getattr(tdp, &tdirfor, nd->nd_cred,
+ p);
+ NFSVOPUNLOCK(tdp, 0, p);
+ }
+ }
+ NFSNAMEICNDSET(&tond.ni_cnd, nd->nd_cred, RENAME, LOCKPARENT | LOCKLEAF | NOCACHE | SAVESTART);
+ nfsvno_setpathbuf(&tond, &tbufp, &hashp);
+ if (!nd->nd_repstat) {
+ error = nfsrv_parsename(nd, tbufp, hashp, &tond.ni_pathlen);
+ if (error) {
+ if (tdp) {
+ if (tnes.nes_vfslocked && !exp->nes_vfslocked &&
+ !(nd->nd_flag & ND_NFSV4))
+ nfsvno_unlockvfs(mp);
+ vrele(tdp);
+ }
+ vput(dp);
+ nfsvno_relpathbuf(&fromnd);
+ nfsvno_relpathbuf(&tond);
+ return (error);
+ }
+ }
+ if (nd->nd_repstat) {
+ if (nd->nd_flag & ND_NFSV3) {
+ nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
+ &fdiraft);
+ nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
+ &tdiraft);
+ }
+ if (tdp) {
+ if (tnes.nes_vfslocked && !exp->nes_vfslocked &&
+ !(nd->nd_flag & ND_NFSV4))
+ nfsvno_unlockvfs(mp);
+ vrele(tdp);
+ }
+ vput(dp);
+ nfsvno_relpathbuf(&fromnd);
+ nfsvno_relpathbuf(&tond);
+ return (0);
+ }
+
+ /*
+ * Done parsing, now down to business.
+ */
+ nd->nd_repstat = nfsvno_namei(nd, &fromnd, dp, 1, exp, p, &fdirp);
+ if (nd->nd_repstat) {
+ if (nd->nd_flag & ND_NFSV3) {
+ nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret,
+ &fdiraft);
+ nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret,
+ &tdiraft);
+ }
+ if (fdirp)
+ vrele(fdirp);
+ if (tdp) {
+ if (tnes.nes_vfslocked && !exp->nes_vfslocked &&
+ !(nd->nd_flag & ND_NFSV4))
+ nfsvno_unlockvfs(mp);
+ vrele(tdp);
+ }
+ nfsvno_relpathbuf(&tond);
+ return (0);
+ }
+ if (vnode_vtype(fromnd.ni_vp) == VDIR)
+ tond.ni_cnd.cn_flags |= WILLBEDIR;
+ nd->nd_repstat = nfsvno_namei(nd, &tond, tdp, 0, &tnes, p, &tdirp);
+ nd->nd_repstat = nfsvno_rename(&fromnd, &tond, nd->nd_repstat,
+ nd->nd_flag, nd->nd_cred, p);
+ if (fdirp)
+ fdiraft_ret = nfsvno_getattr(fdirp, &fdiraft, nd->nd_cred, p);
+ if (tdirp)
+ tdiraft_ret = nfsvno_getattr(tdirp, &tdiraft, nd->nd_cred, p);
+ if (tnes.nes_vfslocked && !exp->nes_vfslocked &&
+ !(nd->nd_flag & ND_NFSV4))
+ nfsvno_unlockvfs(mp);
+ if (fdirp)
+ vrele(fdirp);
+ if (tdirp)
+ vrele(tdirp);
+ if (nd->nd_flag & ND_NFSV3) {
+ nfsrv_wcc(nd, fdirfor_ret, &fdirfor, fdiraft_ret, &fdiraft);
+ nfsrv_wcc(nd, tdirfor_ret, &tdirfor, tdiraft_ret, &tdiraft);
+ } else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
+ NFSM_BUILD(tl, u_int32_t *, 10 * NFSX_UNSIGNED);
+ *tl++ = newnfs_false;
+ txdr_hyper(fdirfor.na_filerev, tl);
+ tl += 2;
+ txdr_hyper(fdiraft.na_filerev, tl);
+ tl += 2;
+ *tl++ = newnfs_false;
+ txdr_hyper(tdirfor.na_filerev, tl);
+ tl += 2;
+ txdr_hyper(tdiraft.na_filerev, tl);
+ }
+ return (0);
+}
+
+/*
+ * nfs link service
+ */
+APPLESTATIC int
+nfsrvd_link(struct nfsrv_descript *nd, int isdgram,
+ vnode_t vp, vnode_t tovp, NFSPROC_T *p, struct nfsexstuff *exp,
+ struct nfsexstuff *toexp)
+{
+ struct nameidata named;
+ u_int32_t *tl;
+ int error = 0, dirfor_ret = 1, diraft_ret = 1, getret = 1;
+ vnode_t dirp = NULL, dp = NULL;
+ struct nfsvattr dirfor, diraft, at;
+ struct nfsexstuff tnes;
+ struct nfsrvfh dfh;
+ mount_t mp = NULL;
+ char *bufp;
+ u_long *hashp;
+
+ if (nd->nd_repstat) {
+ nfsrv_postopattr(nd, getret, &at);
+ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
+ return (0);
+ }
+ NFSVOPUNLOCK(vp, 0, p);
+ if (vnode_vtype(vp) == VDIR) {
+ if (nd->nd_flag & ND_NFSV4)
+ nd->nd_repstat = NFSERR_ISDIR;
+ else
+ nd->nd_repstat = NFSERR_INVAL;
+ if (tovp)
+ vrele(tovp);
+ } else if (vnode_vtype(vp) == VLNK) {
+ if (nd->nd_flag & ND_NFSV2)
+ nd->nd_repstat = NFSERR_INVAL;
+ else
+ nd->nd_repstat = NFSERR_NOTSUPP;
+ if (tovp)
+ vrele(tovp);
+ }
+ if (!nd->nd_repstat) {
+ if (nd->nd_flag & ND_NFSV4) {
+ dp = tovp;
+ tnes = *toexp;
+ } else {
+ error = nfsrv_mtofh(nd, &dfh);
+ if (error) {
+ vrele(vp);
+ /* tovp is always NULL unless NFSv4 */
+ return (error);
+ }
+ /* Won't lock vfs if already locked, mp == NULL */
+ tnes.nes_vfslocked = exp->nes_vfslocked;
+ nfsd_fhtovp(nd, &dfh, &dp, &tnes, &mp, 0, p);
+ if (dp)
+ NFSVOPUNLOCK(dp, 0, p);
+ }
+ }
+ NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, LOCKPARENT);
+ if (!nd->nd_repstat) {
+ nfsvno_setpathbuf(&named, &bufp, &hashp);
+ error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
+ if (error) {
+ vrele(vp);
+ if (dp) {
+ if (tnes.nes_vfslocked && !exp->nes_vfslocked &&
+ !(nd->nd_flag & ND_NFSV4))
+ nfsvno_unlockvfs(mp);
+ vrele(dp);
+ }
+ nfsvno_relpathbuf(&named);
+ return (error);
+ }
+ if (!nd->nd_repstat) {
+ nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, &tnes,
+ p, &dirp);
+ } else {
+ if (dp)
+ vrele(dp);
+ nfsvno_relpathbuf(&named);
+ }
+ }
+ if (dirp) {
+ if (nd->nd_flag & ND_NFSV2) {
+ vrele(dirp);
+ dirp = NULL;
+ } else {
+ dirfor_ret = nfsvno_getattr(dirp, &dirfor,
+ nd->nd_cred, p);
+ }
+ }
+ if (!nd->nd_repstat)
+ nd->nd_repstat = nfsvno_link(&named, vp, nd->nd_cred, p, exp);
+ if (nd->nd_flag & ND_NFSV3)
+ getret = nfsvno_getattr(vp, &at, nd->nd_cred, p);
+ if (dirp) {
+ diraft_ret = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p);
+ vrele(dirp);
+ }
+ if (tnes.nes_vfslocked && !exp->nes_vfslocked &&
+ !(nd->nd_flag & ND_NFSV4))
+ nfsvno_unlockvfs(mp);
+ vrele(vp);
+ if (nd->nd_flag & ND_NFSV3) {
+ nfsrv_postopattr(nd, getret, &at);
+ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
+ } else if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
+ NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
+ *tl++ = newnfs_false;
+ txdr_hyper(dirfor.na_filerev, tl);
+ tl += 2;
+ txdr_hyper(diraft.na_filerev, tl);
+ }
+ return (0);
+}
+
+/*
+ * nfs symbolic link service
+ */
+APPLESTATIC int
+nfsrvd_symlink(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
+ struct nfsexstuff *exp)
+{
+ struct nfsvattr nva, dirfor, diraft;
+ struct nameidata named;
+ int error, dirfor_ret = 1, diraft_ret = 1, pathlen;
+ vnode_t dirp = NULL;
+ char *bufp, *pathcp = NULL;
+ u_long *hashp;
+
+ if (nd->nd_repstat) {
+ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
+ return (0);
+ }
+ if (vpp)
+ *vpp = NULL;
+ NFSVNO_ATTRINIT(&nva);
+ NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
+ LOCKPARENT | SAVESTART);
+ nfsvno_setpathbuf(&named, &bufp, &hashp);
+ error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
+ if (!error && !nd->nd_repstat)
+ error = nfsvno_getsymlink(nd, &nva, p, &pathcp, &pathlen);
+ if (error) {
+ vrele(dp);
+ nfsvno_relpathbuf(&named);
+ return (error);
+ }
+ if (!nd->nd_repstat) {
+ nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
+ } else {
+ vrele(dp);
+ nfsvno_relpathbuf(&named);
+ }
+ if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
+ vrele(dirp);
+ dirp = NULL;
+ }
+
+ /*
+ * And call nfsrvd_symlinksub() to do the common code. It will
+ * return EBADRPC upon a parsing error, 0 otherwise.
+ */
+ if (!nd->nd_repstat) {
+ if (dirp != NULL)
+ dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred,
+ p);
+ nfsrvd_symlinksub(nd, &named, &nva, fhp, vpp, dirp,
+ &dirfor, &diraft, &diraft_ret, NULL, NULL, p, exp,
+ pathcp, pathlen);
+ } else if (dirp != NULL) {
+ dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p);
+ vrele(dirp);
+ }
+ if (pathcp)
+ FREE(pathcp, M_TEMP);
+
+ if (nd->nd_flag & ND_NFSV3) {
+ if (!nd->nd_repstat) {
+ (void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
+ nfsrv_postopattr(nd, 0, &nva);
+ }
+ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
+ }
+ return (0);
+}
+
+/*
+ * Common code for creating a symbolic link.
+ */
+static void
+nfsrvd_symlinksub(struct nfsrv_descript *nd, struct nameidata *ndp,
+ struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
+ vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
+ int *diraft_retp, nfsattrbit_t *attrbitp,
+ NFSACL_T *aclp, NFSPROC_T *p, struct nfsexstuff *exp, char *pathcp,
+ int pathlen)
+{
+ u_int32_t *tl;
+
+ nd->nd_repstat = nfsvno_symlink(ndp, nvap, pathcp, pathlen,
+ !(nd->nd_flag & ND_NFSV2), nd->nd_saveduid, nd->nd_cred, p, exp);
+ if (!nd->nd_repstat && !(nd->nd_flag & ND_NFSV2)) {
+ nfsrv_fixattr(nd, ndp->ni_vp, nvap, aclp, p, attrbitp, exp);
+ if (nd->nd_flag & ND_NFSV3) {
+ nd->nd_repstat = nfsvno_getfh(ndp->ni_vp, fhp, p);
+ if (!nd->nd_repstat)
+ nd->nd_repstat = nfsvno_getattr(ndp->ni_vp,
+ nvap, nd->nd_cred, p);
+ }
+ if (vpp) {
+ NFSVOPUNLOCK(ndp->ni_vp, 0, p);
+ *vpp = ndp->ni_vp;
+ } else {
+ vput(ndp->ni_vp);
+ }
+ }
+ if (dirp) {
+ *diraft_retp = nfsvno_getattr(dirp, diraftp, nd->nd_cred, p);
+ vrele(dirp);
+ }
+ if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
+ NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
+ *tl++ = newnfs_false;
+ txdr_hyper(dirforp->na_filerev, tl);
+ tl += 2;
+ txdr_hyper(diraftp->na_filerev, tl);
+ (void) nfsrv_putattrbit(nd, attrbitp);
+ }
+}
+
+/*
+ * nfs mkdir service
+ */
+APPLESTATIC int
+nfsrvd_mkdir(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t dp, vnode_t *vpp, fhandle_t *fhp, NFSPROC_T *p,
+ struct nfsexstuff *exp)
+{
+ struct nfsvattr nva, dirfor, diraft;
+ struct nameidata named;
+ u_int32_t *tl;
+ int error, dirfor_ret = 1, diraft_ret = 1;
+ vnode_t dirp = NULL;
+ char *bufp;
+ u_long *hashp;
+
+ if (nd->nd_repstat) {
+ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
+ return (0);
+ }
+ NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE, LOCKPARENT);
+ nfsvno_setpathbuf(&named, &bufp, &hashp);
+ error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
+ if (error) {
+ vrele(dp);
+ nfsvno_relpathbuf(&named);
+ return (error);
+ }
+ if (!nd->nd_repstat) {
+ NFSVNO_ATTRINIT(&nva);
+ if (nd->nd_flag & ND_NFSV3) {
+ error = nfsrv_sattr(nd, &nva, NULL, NULL, p);
+ if (error) {
+ vrele(dp);
+ nfsvno_relpathbuf(&named);
+ return (error);
+ }
+ } else {
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
+ nva.na_mode = nfstov_mode(*tl++);
+ }
+ }
+ if (!nd->nd_repstat) {
+ nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp, p, &dirp);
+ } else {
+ vrele(dp);
+ nfsvno_relpathbuf(&named);
+ }
+ if (dirp != NULL && !(nd->nd_flag & ND_NFSV3)) {
+ vrele(dirp);
+ dirp = NULL;
+ }
+ if (nd->nd_repstat) {
+ if (dirp != NULL) {
+ dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred,
+ p);
+ vrele(dirp);
+ }
+ if (nd->nd_flag & ND_NFSV3)
+ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret,
+ &diraft);
+ return (0);
+ }
+ if (dirp != NULL)
+ dirfor_ret = nfsvno_getattr(dirp, &dirfor, nd->nd_cred, p);
+
+ /*
+ * Call nfsrvd_mkdirsub() for the code common to V4 as well.
+ */
+ nfsrvd_mkdirsub(nd, &named, &nva, fhp, vpp, dirp, &dirfor, &diraft,
+ &diraft_ret, NULL, NULL, p, exp);
+
+ if (nd->nd_flag & ND_NFSV3) {
+ if (!nd->nd_repstat) {
+ (void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 1);
+ nfsrv_postopattr(nd, 0, &nva);
+ }
+ nfsrv_wcc(nd, dirfor_ret, &dirfor, diraft_ret, &diraft);
+ } else if (!nd->nd_repstat) {
+ (void) nfsm_fhtom(nd, (u_int8_t *)fhp, 0, 0);
+ nfsrv_fillattr(nd, &nva);
+ }
+ return (0);
+nfsmout:
+ vrele(dp);
+ nfsvno_relpathbuf(&named);
+ return (error);
+}
+
+/*
+ * Code common to mkdir for V2,3 and 4.
+ */
+static void
+nfsrvd_mkdirsub(struct nfsrv_descript *nd, struct nameidata *ndp,
+ struct nfsvattr *nvap, fhandle_t *fhp, vnode_t *vpp,
+ vnode_t dirp, struct nfsvattr *dirforp, struct nfsvattr *diraftp,
+ int *diraft_retp, nfsattrbit_t *attrbitp, NFSACL_T *aclp,
+ NFSPROC_T *p, struct nfsexstuff *exp)
+{
+ vnode_t vp;
+ u_int32_t *tl;
+
+ NFSVNO_SETATTRVAL(nvap, type, VDIR);
+ nd->nd_repstat = nfsvno_mkdir(ndp, nvap, nd->nd_saveduid,
+ nd->nd_cred, p, exp);
+ if (!nd->nd_repstat) {
+ vp = ndp->ni_vp;
+ nfsrv_fixattr(nd, vp, nvap, aclp, p, attrbitp, exp);
+ nd->nd_repstat = nfsvno_getfh(vp, fhp, p);
+ if (!(nd->nd_flag & ND_NFSV4) && !nd->nd_repstat)
+ nd->nd_repstat = nfsvno_getattr(vp, nvap, nd->nd_cred,
+ p);
+ if (vpp && !nd->nd_repstat) {
+ NFSVOPUNLOCK(vp, 0, p);
+ *vpp = vp;
+ } else {
+ vput(vp);
+ }
+ }
+ if (dirp) {
+ *diraft_retp = nfsvno_getattr(dirp, diraftp, nd->nd_cred, p);
+ vrele(dirp);
+ }
+ if ((nd->nd_flag & ND_NFSV4) && !nd->nd_repstat) {
+ NFSM_BUILD(tl, u_int32_t *, 5 * NFSX_UNSIGNED);
+ *tl++ = newnfs_false;
+ txdr_hyper(dirforp->na_filerev, tl);
+ tl += 2;
+ txdr_hyper(diraftp->na_filerev, tl);
+ (void) nfsrv_putattrbit(nd, attrbitp);
+ }
+}
+
+/*
+ * nfs commit service
+ */
+APPLESTATIC int
+nfsrvd_commit(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+ struct nfsvattr bfor, aft;
+ u_int32_t *tl;
+ int error = 0, for_ret = 1, aft_ret = 1, cnt;
+ u_int64_t off;
+
+ if (nd->nd_repstat) {
+ nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
+ return (0);
+ }
+ NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
+ /*
+ * XXX At this time VOP_FSYNC() does not accept offset and byte
+ * count parameters, so these arguments are useless (someday maybe).
+ */
+ off = fxdr_hyper(tl);
+ tl += 2;
+ cnt = fxdr_unsigned(int, *tl);
+ if (nd->nd_flag & ND_NFSV3)
+ for_ret = nfsvno_getattr(vp, &bfor, nd->nd_cred, p);
+ nd->nd_repstat = nfsvno_fsync(vp, off, cnt, nd->nd_cred, p);
+ if (nd->nd_flag & ND_NFSV3) {
+ aft_ret = nfsvno_getattr(vp, &aft, nd->nd_cred, p);
+ nfsrv_wcc(nd, for_ret, &bfor, aft_ret, &aft);
+ }
+ vput(vp);
+ if (!nd->nd_repstat) {
+ NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
+ *tl++ = txdr_unsigned(nfsboottime.tv_sec);
+ *tl = txdr_unsigned(nfsboottime.tv_usec);
+ }
+ return (0);
+nfsmout:
+ vput(vp);
+ return (error);
+}
+
+/*
+ * nfs statfs service
+ */
+APPLESTATIC int
+nfsrvd_statfs(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+ struct statfs *sf;
+ u_int32_t *tl;
+ int getret = 1;
+ struct nfsvattr at;
+ struct statfs sfs;
+ u_quad_t tval;
+
+ if (nd->nd_repstat) {
+ nfsrv_postopattr(nd, getret, &at);
+ return (0);
+ }
+ sf = &sfs;
+ nd->nd_repstat = nfsvno_statfs(vp, sf, nd->nd_cred, p);
+ getret = nfsvno_getattr(vp, &at, nd->nd_cred, p);
+ vput(vp);
+ if (nd->nd_flag & ND_NFSV3)
+ nfsrv_postopattr(nd, getret, &at);
+ if (nd->nd_repstat)
+ return (0);
+ if (nd->nd_flag & ND_NFSV2) {
+ NFSM_BUILD(tl, u_int32_t *, NFSX_V2STATFS);
+ *tl++ = txdr_unsigned(NFS_V2MAXDATA);
+ *tl++ = txdr_unsigned(sf->f_bsize);
+ *tl++ = txdr_unsigned(sf->f_blocks);
+ *tl++ = txdr_unsigned(sf->f_bfree);
+ *tl = txdr_unsigned(sf->f_bavail);
+ } else {
+ NFSM_BUILD(tl, u_int32_t *, NFSX_V3STATFS);
+ tval = (u_quad_t)sf->f_blocks;
+ tval *= (u_quad_t)sf->f_bsize;
+ txdr_hyper(tval, tl); tl += 2;
+ tval = (u_quad_t)sf->f_bfree;
+ tval *= (u_quad_t)sf->f_bsize;
+ txdr_hyper(tval, tl); tl += 2;
+ tval = (u_quad_t)sf->f_bavail;
+ tval *= (u_quad_t)sf->f_bsize;
+ txdr_hyper(tval, tl); tl += 2;
+ tval = (u_quad_t)sf->f_files;
+ txdr_hyper(tval, tl); tl += 2;
+ tval = (u_quad_t)sf->f_ffree;
+ txdr_hyper(tval, tl); tl += 2;
+ tval = (u_quad_t)sf->f_ffree;
+ txdr_hyper(tval, tl); tl += 2;
+ *tl = 0;
+ }
+ return (0);
+}
+
+/*
+ * nfs fsinfo service
+ */
+APPLESTATIC int
+nfsrvd_fsinfo(struct nfsrv_descript *nd, int isdgram,
+ vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ struct nfsfsinfo fs;
+ int getret = 1;
+ struct nfsvattr at;
+
+ if (nd->nd_repstat) {
+ nfsrv_postopattr(nd, getret, &at);
+ return (0);
+ }
+ getret = nfsvno_getattr(vp, &at, nd->nd_cred, p);
+ nfsvno_getfs(&fs, isdgram);
+ vput(vp);
+ nfsrv_postopattr(nd, getret, &at);
+ NFSM_BUILD(tl, u_int32_t *, NFSX_V3FSINFO);
+ *tl++ = txdr_unsigned(fs.fs_rtmax);
+ *tl++ = txdr_unsigned(fs.fs_rtpref);
+ *tl++ = txdr_unsigned(fs.fs_rtmult);
+ *tl++ = txdr_unsigned(fs.fs_wtmax);
+ *tl++ = txdr_unsigned(fs.fs_wtpref);
+ *tl++ = txdr_unsigned(fs.fs_wtmult);
+ *tl++ = txdr_unsigned(fs.fs_dtpref);
+ txdr_hyper(fs.fs_maxfilesize, tl);
+ tl += 2;
+ txdr_nfsv3time(&fs.fs_timedelta, tl);
+ tl += 2;
+ *tl = txdr_unsigned(fs.fs_properties);
+ return (0);
+}
+
+/*
+ * nfs pathconf service
+ */
+APPLESTATIC int
+nfsrvd_pathconf(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+ struct nfsv3_pathconf *pc;
+ int getret = 1;
+ register_t linkmax, namemax, chownres, notrunc;
+ struct nfsvattr at;
+
+ if (nd->nd_repstat) {
+ nfsrv_postopattr(nd, getret, &at);
+ return (0);
+ }
+ nd->nd_repstat = nfsvno_pathconf(vp, _PC_LINK_MAX, &linkmax,
+ nd->nd_cred, p);
+ if (!nd->nd_repstat)
+ nd->nd_repstat = nfsvno_pathconf(vp, _PC_NAME_MAX, &namemax,
+ nd->nd_cred, p);
+ if (!nd->nd_repstat)
+ nd->nd_repstat=nfsvno_pathconf(vp, _PC_CHOWN_RESTRICTED,
+ &chownres, nd->nd_cred, p);
+ if (!nd->nd_repstat)
+ nd->nd_repstat = nfsvno_pathconf(vp, _PC_NO_TRUNC, &notrunc,
+ nd->nd_cred, p);
+ getret = nfsvno_getattr(vp, &at, nd->nd_cred, p);
+ vput(vp);
+ nfsrv_postopattr(nd, getret, &at);
+ if (!nd->nd_repstat) {
+ NFSM_BUILD(pc, struct nfsv3_pathconf *, NFSX_V3PATHCONF);
+ pc->pc_linkmax = txdr_unsigned(linkmax);
+ pc->pc_namemax = txdr_unsigned(namemax);
+ pc->pc_notrunc = txdr_unsigned(notrunc);
+ pc->pc_chownrestricted = txdr_unsigned(chownres);
+
+ /*
+ * These should probably be supported by VOP_PATHCONF(), but
+ * until msdosfs is exportable (why would you want to?), the
+ * Unix defaults should be ok.
+ */
+ pc->pc_caseinsensitive = newnfs_false;
+ pc->pc_casepreserving = newnfs_true;
+ }
+ return (0);
+}
+
+/*
+ * nfsv4 lock service
+ */
+APPLESTATIC int
+nfsrvd_lock(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ int i;
+ struct nfsstate *stp = NULL;
+ struct nfslock *lop;
+ struct nfslockconflict cf;
+ int error = 0;
+ u_short flags = NFSLCK_LOCK, lflags;
+ u_int64_t offset, len;
+ nfsv4stateid_t stateid;
+ nfsquad_t clientid;
+
+ NFSM_DISSECT(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
+ i = fxdr_unsigned(int, *tl++);
+ switch (i) {
+ case NFSV4LOCKT_READW:
+ flags |= NFSLCK_BLOCKING;
+ case NFSV4LOCKT_READ:
+ lflags = NFSLCK_READ;
+ break;
+ case NFSV4LOCKT_WRITEW:
+ flags |= NFSLCK_BLOCKING;
+ case NFSV4LOCKT_WRITE:
+ lflags = NFSLCK_WRITE;
+ break;
+ default:
+ nd->nd_repstat = NFSERR_BADXDR;
+ goto nfsmout;
+ };
+ if (*tl++ == newnfs_true)
+ flags |= NFSLCK_RECLAIM;
+ offset = fxdr_hyper(tl);
+ tl += 2;
+ len = fxdr_hyper(tl);
+ tl += 2;
+ if (*tl == newnfs_true)
+ flags |= NFSLCK_OPENTOLOCK;
+ if (flags & NFSLCK_OPENTOLOCK) {
+ NFSM_DISSECT(tl, u_int32_t *, 5 * NFSX_UNSIGNED + NFSX_STATEID);
+ i = fxdr_unsigned(int, *(tl+4+(NFSX_STATEID / NFSX_UNSIGNED)));
+ MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
+ M_NFSDSTATE, M_WAITOK);
+ stp->ls_ownerlen = i;
+ stp->ls_op = nd->nd_rp;
+ stp->ls_seq = fxdr_unsigned(int, *tl++);
+ stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
+ NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
+ NFSX_STATEIDOTHER);
+ tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
+ stp->ls_opentolockseq = fxdr_unsigned(int, *tl++);
+ clientid.lval[0] = *tl++;
+ clientid.lval[1] = *tl++;
+ if (nd->nd_flag & ND_IMPLIEDCLID) {
+ if (nd->nd_clientid.qval != clientid.qval)
+ printf("EEK! multiple clids\n");
+ } else {
+ nd->nd_flag |= ND_IMPLIEDCLID;
+ nd->nd_clientid.qval = clientid.qval;
+ }
+ error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
+ if (error)
+ goto nfsmout;
+ } else {
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
+ MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate),
+ M_NFSDSTATE, M_WAITOK);
+ stp->ls_ownerlen = 0;
+ stp->ls_op = nd->nd_rp;
+ stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
+ NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
+ NFSX_STATEIDOTHER);
+ tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
+ stp->ls_seq = fxdr_unsigned(int, *tl);
+ clientid.lval[0] = stp->ls_stateid.other[0];
+ clientid.lval[1] = stp->ls_stateid.other[1];
+ if (nd->nd_flag & ND_IMPLIEDCLID) {
+ if (nd->nd_clientid.qval != clientid.qval)
+ printf("EEK! multiple clids\n");
+ } else {
+ nd->nd_flag |= ND_IMPLIEDCLID;
+ nd->nd_clientid.qval = clientid.qval;
+ }
+ }
+ MALLOC(lop, struct nfslock *, sizeof (struct nfslock),
+ M_NFSDLOCK, M_WAITOK);
+ lop->lo_first = offset;
+ if (len == NFS64BITSSET) {
+ lop->lo_end = NFS64BITSSET;
+ } else {
+ lop->lo_end = offset + len;
+ if (lop->lo_end <= lop->lo_first)
+ nd->nd_repstat = NFSERR_INVAL;
+ }
+ lop->lo_flags = lflags;
+ stp->ls_flags = flags;
+ stp->ls_uid = nd->nd_cred->cr_uid;
+
+ /*
+ * Do basic access checking.
+ */
+ if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
+ if (vnode_vtype(vp) == VDIR)
+ nd->nd_repstat = NFSERR_ISDIR;
+ else
+ nd->nd_repstat = NFSERR_INVAL;
+ }
+ if (!nd->nd_repstat) {
+ if (lflags & NFSLCK_WRITE) {
+ nd->nd_repstat = nfsvno_accchk(vp, NFSV4ACE_WRITEDATA,
+ nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
+ NFSACCCHK_VPISLOCKED);
+ } else {
+ nd->nd_repstat = nfsvno_accchk(vp, NFSV4ACE_READDATA,
+ nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
+ NFSACCCHK_VPISLOCKED);
+ if (nd->nd_repstat)
+ nd->nd_repstat = nfsvno_accchk(vp, NFSV4ACE_EXECUTE,
+ nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
+ NFSACCCHK_VPISLOCKED);
+ }
+ }
+
+ /*
+ * We call nfsrv_lockctrl() even if nd_repstat set, so that the
+ * seqid# gets updated. nfsrv_lockctrl() will return the value
+ * of nd_repstat, if it gets that far.
+ */
+ nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
+ &stateid, exp, nd, p);
+ if (lop)
+ FREE((caddr_t)lop, M_NFSDLOCK);
+ if (stp)
+ FREE((caddr_t)stp, M_NFSDSTATE);
+ if (!nd->nd_repstat) {
+ NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
+ *tl++ = txdr_unsigned(stateid.seqid);
+ NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
+ } else if (nd->nd_repstat == NFSERR_DENIED) {
+ NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
+ txdr_hyper(cf.cl_first, tl);
+ tl += 2;
+ if (cf.cl_end == NFS64BITSSET)
+ len = NFS64BITSSET;
+ else
+ len = cf.cl_end - cf.cl_first;
+ txdr_hyper(len, tl);
+ tl += 2;
+ if (cf.cl_flags == NFSLCK_WRITE)
+ *tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
+ else
+ *tl++ = txdr_unsigned(NFSV4LOCKT_READ);
+ *tl++ = stateid.other[0];
+ *tl = stateid.other[1];
+ (void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
+ }
+ vput(vp);
+ return (0);
+nfsmout:
+ vput(vp);
+ if (stp)
+ free((caddr_t)stp, M_NFSDSTATE);
+ return (error);
+}
+
+/*
+ * nfsv4 lock test service
+ */
+APPLESTATIC int
+nfsrvd_lockt(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ int i;
+ struct nfsstate *stp = NULL;
+ struct nfslock lo, *lop = &lo;
+ struct nfslockconflict cf;
+ int error = 0;
+ nfsv4stateid_t stateid;
+ nfsquad_t clientid;
+ u_int64_t len;
+
+ NFSM_DISSECT(tl, u_int32_t *, 8 * NFSX_UNSIGNED);
+ i = fxdr_unsigned(int, *(tl + 7));
+ MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
+ M_NFSDSTATE, M_WAITOK);
+ stp->ls_ownerlen = i;
+ stp->ls_op = NULL;
+ stp->ls_flags = NFSLCK_TEST;
+ stp->ls_uid = nd->nd_cred->cr_uid;
+ i = fxdr_unsigned(int, *tl++);
+ switch (i) {
+ case NFSV4LOCKT_READW:
+ stp->ls_flags |= NFSLCK_BLOCKING;
+ case NFSV4LOCKT_READ:
+ lo.lo_flags = NFSLCK_READ;
+ break;
+ case NFSV4LOCKT_WRITEW:
+ stp->ls_flags |= NFSLCK_BLOCKING;
+ case NFSV4LOCKT_WRITE:
+ lo.lo_flags = NFSLCK_WRITE;
+ break;
+ default:
+ nd->nd_repstat = NFSERR_BADXDR;
+ goto nfsmout;
+ };
+ lo.lo_first = fxdr_hyper(tl);
+ tl += 2;
+ len = fxdr_hyper(tl);
+ if (len == NFS64BITSSET) {
+ lo.lo_end = NFS64BITSSET;
+ } else {
+ lo.lo_end = lo.lo_first + len;
+ if (lo.lo_end <= lo.lo_first)
+ nd->nd_repstat = NFSERR_INVAL;
+ }
+ tl += 2;
+ clientid.lval[0] = *tl++;
+ clientid.lval[1] = *tl;
+ if (nd->nd_flag & ND_IMPLIEDCLID) {
+ if (nd->nd_clientid.qval != clientid.qval)
+ printf("EEK! multiple clids\n");
+ } else {
+ nd->nd_flag |= ND_IMPLIEDCLID;
+ nd->nd_clientid.qval = clientid.qval;
+ }
+ error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
+ if (error)
+ goto nfsmout;
+ if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
+ if (vnode_vtype(vp) == VDIR)
+ nd->nd_repstat = NFSERR_ISDIR;
+ else
+ nd->nd_repstat = NFSERR_INVAL;
+ }
+ if (!nd->nd_repstat)
+ nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, &cf, clientid,
+ &stateid, exp, nd, p);
+ if (stp)
+ FREE((caddr_t)stp, M_NFSDSTATE);
+ if (nd->nd_repstat) {
+ if (nd->nd_repstat == NFSERR_DENIED) {
+ NFSM_BUILD(tl, u_int32_t *, 7 * NFSX_UNSIGNED);
+ txdr_hyper(cf.cl_first, tl);
+ tl += 2;
+ if (cf.cl_end == NFS64BITSSET)
+ len = NFS64BITSSET;
+ else
+ len = cf.cl_end - cf.cl_first;
+ txdr_hyper(len, tl);
+ tl += 2;
+ if (cf.cl_flags == NFSLCK_WRITE)
+ *tl++ = txdr_unsigned(NFSV4LOCKT_WRITE);
+ else
+ *tl++ = txdr_unsigned(NFSV4LOCKT_READ);
+ *tl++ = stp->ls_stateid.other[0];
+ *tl = stp->ls_stateid.other[1];
+ (void) nfsm_strtom(nd, cf.cl_owner, cf.cl_ownerlen);
+ }
+ }
+ vput(vp);
+ return (0);
+nfsmout:
+ vput(vp);
+ if (stp)
+ free((caddr_t)stp, M_NFSDSTATE);
+ return (error);
+}
+
+/*
+ * nfsv4 unlock service
+ */
+APPLESTATIC int
+nfsrvd_locku(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t vp, NFSPROC_T *p, struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ int i;
+ struct nfsstate *stp;
+ struct nfslock *lop;
+ int error = 0;
+ nfsv4stateid_t stateid;
+ nfsquad_t clientid;
+ u_int64_t len;
+
+ NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED + NFSX_STATEID);
+ MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate),
+ M_NFSDSTATE, M_WAITOK);
+ MALLOC(lop, struct nfslock *, sizeof (struct nfslock),
+ M_NFSDLOCK, M_WAITOK);
+ stp->ls_flags = NFSLCK_UNLOCK;
+ lop->lo_flags = NFSLCK_UNLOCK;
+ stp->ls_op = nd->nd_rp;
+ i = fxdr_unsigned(int, *tl++);
+ switch (i) {
+ case NFSV4LOCKT_READW:
+ stp->ls_flags |= NFSLCK_BLOCKING;
+ case NFSV4LOCKT_READ:
+ break;
+ case NFSV4LOCKT_WRITEW:
+ stp->ls_flags |= NFSLCK_BLOCKING;
+ case NFSV4LOCKT_WRITE:
+ break;
+ default:
+ nd->nd_repstat = NFSERR_BADXDR;
+ goto nfsmout;
+ };
+ stp->ls_ownerlen = 0;
+ stp->ls_uid = nd->nd_cred->cr_uid;
+ stp->ls_seq = fxdr_unsigned(int, *tl++);
+ stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
+ NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
+ NFSX_STATEIDOTHER);
+ tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
+ lop->lo_first = fxdr_hyper(tl);
+ tl += 2;
+ len = fxdr_hyper(tl);
+ if (len == NFS64BITSSET) {
+ lop->lo_end = NFS64BITSSET;
+ } else {
+ lop->lo_end = lop->lo_first + len;
+ if (lop->lo_end <= lop->lo_first)
+ nd->nd_repstat = NFSERR_INVAL;
+ }
+ clientid.lval[0] = stp->ls_stateid.other[0];
+ clientid.lval[1] = stp->ls_stateid.other[1];
+ if (nd->nd_flag & ND_IMPLIEDCLID) {
+ if (nd->nd_clientid.qval != clientid.qval)
+ printf("EEK! multiple clids\n");
+ } else {
+ nd->nd_flag |= ND_IMPLIEDCLID;
+ nd->nd_clientid.qval = clientid.qval;
+ }
+ if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
+ if (vnode_vtype(vp) == VDIR)
+ nd->nd_repstat = NFSERR_ISDIR;
+ else
+ nd->nd_repstat = NFSERR_INVAL;
+ }
+ /*
+ * Call nfsrv_lockctrl() even if nd_repstat is set, so that the
+ * seqid# gets incremented. nfsrv_lockctrl() will return the
+ * value of nd_repstat, if it gets that far.
+ */
+ nd->nd_repstat = nfsrv_lockctrl(vp, &stp, &lop, NULL, clientid,
+ &stateid, exp, nd, p);
+ if (stp)
+ FREE((caddr_t)stp, M_NFSDSTATE);
+ if (lop)
+ free((caddr_t)lop, M_NFSDLOCK);
+ if (!nd->nd_repstat) {
+ NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
+ *tl++ = txdr_unsigned(stateid.seqid);
+ NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
+ }
+nfsmout:
+ vput(vp);
+ return (error);
+}
+
+/*
+ * nfsv4 open service
+ */
+APPLESTATIC int
+nfsrvd_open(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t dp, vnode_t *vpp, __unused fhandle_t *fhp, NFSPROC_T *p,
+ struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ int i;
+ struct nfsstate *stp = NULL;
+ int error = 0, create, claim, exclusive_flag = 0;
+ u_int32_t rflags = NFSV4OPEN_LOCKTYPEPOSIX, acemask;
+ int how = NFSCREATE_UNCHECKED;
+ u_char cverf[NFSX_VERF];
+ vnode_t vp = NULL, dirp = NULL;
+ struct nfsvattr nva, dirfor, diraft;
+ struct nameidata named;
+ nfsv4stateid_t stateid, delegstateid;
+ nfsattrbit_t attrbits;
+ nfsquad_t clientid;
+ char *bufp = NULL;
+ u_long *hashp;
+ NFSACL_T *aclp = NULL;
+
+#ifdef NFS4_ACL_EXTATTR_NAME
+ aclp = acl_alloc();
+ aclp->acl_cnt = 0;
+#endif
+ NFSZERO_ATTRBIT(&attrbits);
+ named.ni_startdir = NULL;
+ named.ni_cnd.cn_nameiop = 0;
+ NFSM_DISSECT(tl, u_int32_t *, 6 * NFSX_UNSIGNED);
+ i = fxdr_unsigned(int, *(tl + 5));
+ MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + i,
+ M_NFSDSTATE, M_WAITOK);
+ stp->ls_ownerlen = i;
+ stp->ls_op = nd->nd_rp;
+ stp->ls_flags = NFSLCK_OPEN;
+ stp->ls_uid = nd->nd_cred->cr_uid;
+ stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
+ i = fxdr_unsigned(int, *tl++);
+ switch (i) {
+ case NFSV4OPEN_ACCESSREAD:
+ stp->ls_flags |= NFSLCK_READACCESS;
+ break;
+ case NFSV4OPEN_ACCESSWRITE:
+ stp->ls_flags |= NFSLCK_WRITEACCESS;
+ break;
+ case NFSV4OPEN_ACCESSBOTH:
+ stp->ls_flags |= (NFSLCK_READACCESS | NFSLCK_WRITEACCESS);
+ break;
+ default:
+ nd->nd_repstat = NFSERR_INVAL;
+ };
+ i = fxdr_unsigned(int, *tl++);
+ switch (i) {
+ case NFSV4OPEN_DENYNONE:
+ break;
+ case NFSV4OPEN_DENYREAD:
+ stp->ls_flags |= NFSLCK_READDENY;
+ break;
+ case NFSV4OPEN_DENYWRITE:
+ stp->ls_flags |= NFSLCK_WRITEDENY;
+ break;
+ case NFSV4OPEN_DENYBOTH:
+ stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
+ break;
+ default:
+ nd->nd_repstat = NFSERR_INVAL;
+ };
+ clientid.lval[0] = *tl++;
+ clientid.lval[1] = *tl;
+ if (nd->nd_flag & ND_IMPLIEDCLID) {
+ if (nd->nd_clientid.qval != clientid.qval)
+ printf("EEK! multiple clids\n");
+ } else {
+ nd->nd_flag |= ND_IMPLIEDCLID;
+ nd->nd_clientid.qval = clientid.qval;
+ }
+ error = nfsrv_mtostr(nd, stp->ls_owner, stp->ls_ownerlen);
+ if (error) {
+ vrele(dp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ FREE((caddr_t)stp, M_NFSDSTATE);
+ return (error);
+ }
+ NFSVNO_ATTRINIT(&nva);
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
+ create = fxdr_unsigned(int, *tl);
+ if (!nd->nd_repstat)
+ nd->nd_repstat = nfsvno_getattr(dp, &dirfor, nd->nd_cred, p);
+ if (create == NFSV4OPEN_CREATE) {
+ nva.na_type = VREG;
+ nva.na_mode = 0;
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
+ how = fxdr_unsigned(int, *tl);
+ switch (how) {
+ case NFSCREATE_UNCHECKED:
+ case NFSCREATE_GUARDED:
+ error = nfsv4_sattr(nd, &nva, &attrbits, aclp, p);
+ if (error) {
+ vrele(dp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ FREE((caddr_t)stp, M_NFSDSTATE);
+ return (error);
+ }
+ /*
+ * If the na_gid being set is the same as that of
+ * the directory it is going in, clear it, since
+ * that is what will be set by default. This allows
+ * a user that isn't in that group to do the create.
+ */
+ if (!nd->nd_repstat && NFSVNO_ISSETGID(&nva) &&
+ nva.na_gid == dirfor.na_gid)
+ NFSVNO_UNSET(&nva, gid);
+ if (!nd->nd_repstat)
+ nd->nd_repstat = nfsrv_checkuidgid(nd, &nva);
+ break;
+ case NFSCREATE_EXCLUSIVE:
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF);
+ NFSBCOPY((caddr_t)tl, cverf, NFSX_VERF);
+ break;
+ default:
+ nd->nd_repstat = NFSERR_BADXDR;
+ vrele(dp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ FREE((caddr_t)stp, M_NFSDSTATE);
+ return (0);
+ };
+ } else if (create != NFSV4OPEN_NOCREATE) {
+ nd->nd_repstat = NFSERR_BADXDR;
+ vrele(dp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ FREE((caddr_t)stp, M_NFSDSTATE);
+ return (0);
+ }
+
+ /*
+ * Now, handle the claim, which usually includes looking up a
+ * name in the directory referenced by dp. The exception is
+ * NFSV4OPEN_CLAIMPREVIOUS.
+ */
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
+ claim = fxdr_unsigned(int, *tl);
+ if (claim == NFSV4OPEN_CLAIMDELEGATECUR) {
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
+ stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
+ NFSBCOPY((caddr_t)tl,(caddr_t)stateid.other,NFSX_STATEIDOTHER);
+ stp->ls_flags |= NFSLCK_DELEGCUR;
+ } else if (claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
+ stp->ls_flags |= NFSLCK_DELEGPREV;
+ }
+ if (claim == NFSV4OPEN_CLAIMNULL || claim == NFSV4OPEN_CLAIMDELEGATECUR
+ || claim == NFSV4OPEN_CLAIMDELEGATEPREV) {
+ if (!nd->nd_repstat && create == NFSV4OPEN_CREATE &&
+ claim != NFSV4OPEN_CLAIMNULL)
+ nd->nd_repstat = NFSERR_INVAL;
+ if (nd->nd_repstat) {
+ nd->nd_repstat = nfsrv_opencheck(clientid,
+ &stateid, stp, NULL, nd, p, nd->nd_repstat);
+ vrele(dp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ FREE((caddr_t)stp, M_NFSDSTATE);
+ return (0);
+ }
+ if (create == NFSV4OPEN_CREATE)
+ NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, CREATE,
+ LOCKPARENT | LOCKLEAF | SAVESTART);
+ else
+ NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
+ LOCKLEAF | SAVESTART);
+ nfsvno_setpathbuf(&named, &bufp, &hashp);
+ error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
+ if (error) {
+ vrele(dp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ FREE((caddr_t)stp, M_NFSDSTATE);
+ nfsvno_relpathbuf(&named);
+ return (error);
+ }
+ if (!nd->nd_repstat) {
+ nd->nd_repstat = nfsvno_namei(nd, &named, dp, 0, exp,
+ p, &dirp);
+ } else {
+ vrele(dp);
+ nfsvno_relpathbuf(&named);
+ }
+ if (create == NFSV4OPEN_CREATE) {
+ switch (how) {
+ case NFSCREATE_UNCHECKED:
+ if (named.ni_vp) {
+ /*
+ * Clear the setable attribute bits, except
+ * for Size, if it is being truncated.
+ */
+ NFSZERO_ATTRBIT(&attrbits);
+ if (NFSVNO_ISSETSIZE(&nva))
+ NFSSETBIT_ATTRBIT(&attrbits,
+ NFSATTRBIT_SIZE);
+ }
+ break;
+ case NFSCREATE_GUARDED:
+ if (named.ni_vp && !nd->nd_repstat)
+ nd->nd_repstat = EEXIST;
+ break;
+ case NFSCREATE_EXCLUSIVE:
+ exclusive_flag = 1;
+ if (!named.ni_vp)
+ nva.na_mode = 0;
+ };
+ }
+ nfsvno_open(nd, &named, clientid, &stateid, stp,
+ &exclusive_flag, &nva, cverf, create, aclp, &attrbits,
+ nd->nd_cred, p, exp, &vp);
+ } else if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
+ i = fxdr_unsigned(int, *tl);
+ switch (i) {
+ case NFSV4OPEN_DELEGATEREAD:
+ stp->ls_flags |= NFSLCK_DELEGREAD;
+ break;
+ case NFSV4OPEN_DELEGATEWRITE:
+ stp->ls_flags |= NFSLCK_DELEGWRITE;
+ case NFSV4OPEN_DELEGATENONE:
+ break;
+ default:
+ nd->nd_repstat = NFSERR_BADXDR;
+ vrele(dp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ FREE((caddr_t)stp, M_NFSDSTATE);
+ return (0);
+ };
+ stp->ls_flags |= NFSLCK_RECLAIM;
+ vp = dp;
+ NFSVOPLOCK(vp, LK_EXCLUSIVE | LK_RETRY, p);
+ nd->nd_repstat = nfsrv_opencheck(clientid, &stateid, stp, vp,
+ nd, p, nd->nd_repstat);
+ } else {
+ nd->nd_repstat = NFSERR_BADXDR;
+ vrele(dp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ FREE((caddr_t)stp, M_NFSDSTATE);
+ return (0);
+ }
+
+ /*
+ * Do basic access checking.
+ */
+ if (!nd->nd_repstat && vnode_vtype(vp) != VREG) {
+ if (vnode_vtype(vp) == VDIR)
+ nd->nd_repstat = NFSERR_ISDIR;
+ else if (vnode_vtype(vp) == VLNK)
+ nd->nd_repstat = NFSERR_SYMLINK;
+ else
+ nd->nd_repstat = NFSERR_INVAL;
+ }
+ if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_WRITEACCESS))
+ nd->nd_repstat = nfsvno_accchk(vp, NFSV4ACE_WRITEDATA, nd->nd_cred,
+ exp, p, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED);
+ if (!nd->nd_repstat && (stp->ls_flags & NFSLCK_READACCESS)) {
+ nd->nd_repstat = nfsvno_accchk(vp, NFSV4ACE_READDATA, nd->nd_cred,
+ exp, p, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED);
+ if (nd->nd_repstat)
+ nd->nd_repstat = nfsvno_accchk(vp, NFSV4ACE_EXECUTE,
+ nd->nd_cred, exp, p, NFSACCCHK_ALLOWOWNER,
+ NFSACCCHK_VPISLOCKED);
+ }
+
+ if (!nd->nd_repstat)
+ nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p);
+ if (!nd->nd_repstat && exclusive_flag &&
+ NFSBCMP(cverf, (caddr_t)&nva.na_atime, NFSX_VERF))
+ nd->nd_repstat = EEXIST;
+ /*
+ * Do the open locking/delegation stuff.
+ */
+ if (!nd->nd_repstat)
+ nd->nd_repstat = nfsrv_openctrl(nd, vp, &stp, clientid, &stateid,
+ &delegstateid, &rflags, exp, p, nva.na_filerev);
+
+ /*
+ * vp must be unlocked before the call to nfsvno_getattr(dirp,...)
+ * below, to avoid a deadlock with the lookup in nfsvno_namei() above.
+ * (ie: Leave the NFSVOPUNLOCK() about here.)
+ */
+ if (vp)
+ NFSVOPUNLOCK(vp, 0, p);
+ if (stp)
+ FREE((caddr_t)stp, M_NFSDSTATE);
+ if (!nd->nd_repstat && dirp)
+ nd->nd_repstat = nfsvno_getattr(dirp, &diraft, nd->nd_cred, p);
+ if (!nd->nd_repstat) {
+ NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID + 6 * NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(stateid.seqid);
+ NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
+ tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
+ if (claim == NFSV4OPEN_CLAIMPREVIOUS) {
+ *tl++ = newnfs_true;
+ *tl++ = 0;
+ *tl++ = 0;
+ *tl++ = 0;
+ *tl++ = 0;
+ } else {
+ *tl++ = newnfs_false; /* Since dirp is not locked */
+ txdr_hyper(dirfor.na_filerev, tl);
+ tl += 2;
+ txdr_hyper(diraft.na_filerev, tl);
+ tl += 2;
+ }
+ *tl = txdr_unsigned(rflags & NFSV4OPEN_RFLAGS);
+ (void) nfsrv_putattrbit(nd, &attrbits);
+ NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
+ if (rflags & NFSV4OPEN_READDELEGATE)
+ *tl = txdr_unsigned(NFSV4OPEN_DELEGATEREAD);
+ else if (rflags & NFSV4OPEN_WRITEDELEGATE)
+ *tl = txdr_unsigned(NFSV4OPEN_DELEGATEWRITE);
+ else
+ *tl = txdr_unsigned(NFSV4OPEN_DELEGATENONE);
+ if (rflags & (NFSV4OPEN_READDELEGATE|NFSV4OPEN_WRITEDELEGATE)) {
+ NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID+NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(delegstateid.seqid);
+ NFSBCOPY((caddr_t)delegstateid.other, (caddr_t)tl,
+ NFSX_STATEIDOTHER);
+ tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
+ if (rflags & NFSV4OPEN_RECALL)
+ *tl = newnfs_true;
+ else
+ *tl = newnfs_false;
+ if (rflags & NFSV4OPEN_WRITEDELEGATE) {
+ NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(NFSV4OPEN_LIMITSIZE);
+ txdr_hyper(nva.na_size, tl);
+ }
+ NFSM_BUILD(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(NFSV4ACE_ALLOWEDTYPE);
+ *tl++ = txdr_unsigned(0x0);
+ acemask = NFSV4ACE_ALLFILESMASK;
+ if (nva.na_mode & S_IRUSR)
+ acemask |= NFSV4ACE_READMASK;
+ if (nva.na_mode & S_IWUSR)
+ acemask |= NFSV4ACE_WRITEMASK;
+ if (nva.na_mode & S_IXUSR)
+ acemask |= NFSV4ACE_EXECUTEMASK;
+ *tl = txdr_unsigned(acemask);
+ (void) nfsm_strtom(nd, "OWNER@", 6);
+ }
+ *vpp = vp;
+ } else if (vp) {
+ vrele(vp);
+ }
+ if (dirp)
+ vrele(dirp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ return (0);
+nfsmout:
+ vrele(dp);
+#ifdef NFS4_ACL_EXTATTR_NAME
+ acl_free(aclp);
+#endif
+ if (stp)
+ FREE((caddr_t)stp, M_NFSDSTATE);
+ return (error);
+}
+
+/*
+ * nfsv4 close service
+ */
+APPLESTATIC int
+nfsrvd_close(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ struct nfsstate st, *stp = &st;
+ int error = 0;
+ nfsv4stateid_t stateid;
+ nfsquad_t clientid;
+
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED + NFSX_STATEID);
+ stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
+ stp->ls_ownerlen = 0;
+ stp->ls_op = nd->nd_rp;
+ stp->ls_uid = nd->nd_cred->cr_uid;
+ stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
+ NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
+ NFSX_STATEIDOTHER);
+ stp->ls_flags = NFSLCK_CLOSE;
+ clientid.lval[0] = stp->ls_stateid.other[0];
+ clientid.lval[1] = stp->ls_stateid.other[1];
+ if (nd->nd_flag & ND_IMPLIEDCLID) {
+ if (nd->nd_clientid.qval != clientid.qval)
+ printf("EEK! multiple clids\n");
+ } else {
+ nd->nd_flag |= ND_IMPLIEDCLID;
+ nd->nd_clientid.qval = clientid.qval;
+ }
+ nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p);
+ vput(vp);
+ if (!nd->nd_repstat) {
+ NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
+ *tl++ = txdr_unsigned(stateid.seqid);
+ NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
+ }
+ return (0);
+nfsmout:
+ vput(vp);
+ return (error);
+}
+
+/*
+ * nfsv4 delegpurge service
+ */
+APPLESTATIC int
+nfsrvd_delegpurge(struct nfsrv_descript *nd, __unused int isdgram,
+ __unused vnode_t vp, __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ int error = 0;
+ nfsquad_t clientid;
+
+ NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+ clientid.lval[0] = *tl++;
+ clientid.lval[1] = *tl;
+ if (nd->nd_flag & ND_IMPLIEDCLID) {
+ if (nd->nd_clientid.qval != clientid.qval)
+ printf("EEK! multiple clids\n");
+ } else {
+ nd->nd_flag |= ND_IMPLIEDCLID;
+ nd->nd_clientid.qval = clientid.qval;
+ }
+ nd->nd_repstat = nfsrv_delegupdate(clientid, NULL, NULL,
+ NFSV4OP_DELEGPURGE, nd->nd_cred, p);
+nfsmout:
+ return (error);
+}
+
+/*
+ * nfsv4 delegreturn service
+ */
+APPLESTATIC int
+nfsrvd_delegreturn(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ int error = 0;
+ nfsv4stateid_t stateid;
+ nfsquad_t clientid;
+
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID);
+ stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
+ NFSBCOPY((caddr_t)tl, (caddr_t)stateid.other, NFSX_STATEIDOTHER);
+ clientid.lval[0] = stateid.other[0];
+ clientid.lval[1] = stateid.other[1];
+ if (nd->nd_flag & ND_IMPLIEDCLID) {
+ if (nd->nd_clientid.qval != clientid.qval)
+ printf("EEK! multiple clids\n");
+ } else {
+ nd->nd_flag |= ND_IMPLIEDCLID;
+ nd->nd_clientid.qval = clientid.qval;
+ }
+ nd->nd_repstat = nfsrv_delegupdate(clientid, &stateid, vp,
+ NFSV4OP_DELEGRETURN, nd->nd_cred, p);
+nfsmout:
+ vput(vp);
+ return (error);
+}
+
+/*
+ * nfsv4 get file handle service
+ */
+APPLESTATIC int
+nfsrvd_getfh(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+ fhandle_t fh;
+
+ nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
+ vput(vp);
+ if (!nd->nd_repstat)
+ (void) nfsm_fhtom(nd, (u_int8_t *)&fh, 0, 0);
+ return (0);
+}
+
+/*
+ * nfsv4 open confirm service
+ */
+APPLESTATIC int
+nfsrvd_openconfirm(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ struct nfsstate st, *stp = &st;
+ int error = 0;
+ nfsv4stateid_t stateid;
+ nfsquad_t clientid;
+
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + NFSX_UNSIGNED);
+ stp->ls_ownerlen = 0;
+ stp->ls_op = nd->nd_rp;
+ stp->ls_uid = nd->nd_cred->cr_uid;
+ stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
+ NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
+ NFSX_STATEIDOTHER);
+ tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
+ stp->ls_seq = fxdr_unsigned(u_int32_t, *tl);
+ stp->ls_flags = NFSLCK_CONFIRM;
+ clientid.lval[0] = stp->ls_stateid.other[0];
+ clientid.lval[1] = stp->ls_stateid.other[1];
+ if (nd->nd_flag & ND_IMPLIEDCLID) {
+ if (nd->nd_clientid.qval != clientid.qval)
+ printf("EEK! multiple clids\n");
+ } else {
+ nd->nd_flag |= ND_IMPLIEDCLID;
+ nd->nd_clientid.qval = clientid.qval;
+ }
+ nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid, nd, p);
+ if (!nd->nd_repstat) {
+ NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
+ *tl++ = txdr_unsigned(stateid.seqid);
+ NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
+ }
+nfsmout:
+ vput(vp);
+ return (error);
+}
+
+/*
+ * nfsv4 open downgrade service
+ */
+APPLESTATIC int
+nfsrvd_opendowngrade(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ int i;
+ struct nfsstate st, *stp = &st;
+ int error = 0;
+ nfsv4stateid_t stateid;
+ nfsquad_t clientid;
+
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_STATEID + 3 * NFSX_UNSIGNED);
+ stp->ls_ownerlen = 0;
+ stp->ls_op = nd->nd_rp;
+ stp->ls_uid = nd->nd_cred->cr_uid;
+ stp->ls_stateid.seqid = fxdr_unsigned(u_int32_t, *tl++);
+ NFSBCOPY((caddr_t)tl, (caddr_t)stp->ls_stateid.other,
+ NFSX_STATEIDOTHER);
+ tl += (NFSX_STATEIDOTHER / NFSX_UNSIGNED);
+ stp->ls_seq = fxdr_unsigned(u_int32_t, *tl++);
+ i = fxdr_unsigned(int, *tl++);
+ switch (i) {
+ case NFSV4OPEN_ACCESSREAD:
+ stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_DOWNGRADE);
+ break;
+ case NFSV4OPEN_ACCESSWRITE:
+ stp->ls_flags = (NFSLCK_WRITEACCESS | NFSLCK_DOWNGRADE);
+ break;
+ case NFSV4OPEN_ACCESSBOTH:
+ stp->ls_flags = (NFSLCK_READACCESS | NFSLCK_WRITEACCESS |
+ NFSLCK_DOWNGRADE);
+ break;
+ default:
+ nd->nd_repstat = NFSERR_BADXDR;
+ };
+ i = fxdr_unsigned(int, *tl);
+ switch (i) {
+ case NFSV4OPEN_DENYNONE:
+ break;
+ case NFSV4OPEN_DENYREAD:
+ stp->ls_flags |= NFSLCK_READDENY;
+ break;
+ case NFSV4OPEN_DENYWRITE:
+ stp->ls_flags |= NFSLCK_WRITEDENY;
+ break;
+ case NFSV4OPEN_DENYBOTH:
+ stp->ls_flags |= (NFSLCK_READDENY | NFSLCK_WRITEDENY);
+ break;
+ default:
+ nd->nd_repstat = NFSERR_BADXDR;
+ };
+
+ clientid.lval[0] = stp->ls_stateid.other[0];
+ clientid.lval[1] = stp->ls_stateid.other[1];
+ if (nd->nd_flag & ND_IMPLIEDCLID) {
+ if (nd->nd_clientid.qval != clientid.qval)
+ printf("EEK! multiple clids\n");
+ } else {
+ nd->nd_flag |= ND_IMPLIEDCLID;
+ nd->nd_clientid.qval = clientid.qval;
+ }
+ if (!nd->nd_repstat)
+ nd->nd_repstat = nfsrv_openupdate(vp, stp, clientid, &stateid,
+ nd, p);
+ if (!nd->nd_repstat) {
+ NFSM_BUILD(tl, u_int32_t *, NFSX_STATEID);
+ *tl++ = txdr_unsigned(stateid.seqid);
+ NFSBCOPY((caddr_t)stateid.other,(caddr_t)tl,NFSX_STATEIDOTHER);
+ }
+nfsmout:
+ vput(vp);
+ return (error);
+}
+
+/*
+ * nfsv4 renew lease service
+ */
+APPLESTATIC int
+nfsrvd_renew(struct nfsrv_descript *nd, __unused int isdgram,
+ __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ int error = 0;
+ nfsquad_t clientid;
+
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
+ clientid.lval[0] = *tl++;
+ clientid.lval[1] = *tl;
+ if (nd->nd_flag & ND_IMPLIEDCLID) {
+ if (nd->nd_clientid.qval != clientid.qval)
+ printf("EEK! multiple clids\n");
+ } else {
+ nd->nd_flag |= ND_IMPLIEDCLID;
+ nd->nd_clientid.qval = clientid.qval;
+ }
+ nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_RENEWOP|CLOPS_RENEW),
+ NULL, (nfsquad_t)((u_quad_t)0), nd, p);
+nfsmout:
+ return (error);
+}
+
+/*
+ * nfsv4 security info service
+ */
+APPLESTATIC int
+nfsrvd_secinfo(struct nfsrv_descript *nd, int isdgram,
+ vnode_t dp, NFSPROC_T *p, struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ int len;
+ struct nameidata named;
+ vnode_t dirp = NULL, vp;
+ struct nfsrvfh fh;
+ struct nfsexstuff retnes;
+ mount_t mp;
+ u_int32_t *sizp;
+ int error, savflag, i;
+ char *bufp;
+ u_long *hashp;
+
+ /*
+ * All this just to get the export flags for the name.
+ */
+ NFSNAMEICNDSET(&named.ni_cnd, nd->nd_cred, LOOKUP,
+ LOCKLEAF | SAVESTART);
+ nfsvno_setpathbuf(&named, &bufp, &hashp);
+ error = nfsrv_parsename(nd, bufp, hashp, &named.ni_pathlen);
+ if (error) {
+ vput(dp);
+ nfsvno_relpathbuf(&named);
+ return (error);
+ }
+ if (!nd->nd_repstat) {
+ nd->nd_repstat = nfsvno_namei(nd, &named, dp, 1, exp, p, &dirp);
+ } else {
+ vput(dp);
+ nfsvno_relpathbuf(&named);
+ }
+ if (dirp)
+ vrele(dirp);
+ if (nd->nd_repstat)
+ return (0);
+ vrele(named.ni_startdir);
+ nfsvno_relpathbuf(&named);
+ fh.nfsrvfh_len = NFSX_MYFH;
+ vp = named.ni_vp;
+ nd->nd_repstat = nfsvno_getfh(vp, (fhandle_t *)fh.nfsrvfh_data, p);
+ mp = vnode_mount(vp); /* so it won't try to re-lock filesys */
+ retnes.nes_vfslocked = exp->nes_vfslocked;
+ vput(vp);
+ savflag = nd->nd_flag;
+ nd->nd_flag |= ND_GSS; /* so nfsd_fhtovp() won't reply Wrongsec */
+ if (!nd->nd_repstat) {
+ nfsd_fhtovp(nd, &fh, &vp, &retnes, &mp, 0, p);
+ if (vp)
+ vput(vp);
+ }
+ nd->nd_flag = savflag;
+ if (nd->nd_repstat)
+ return (0);
+
+ /*
+ * Finally have the export flags for name, so we can create
+ * the security info.
+ */
+ len = 0;
+ NFSM_BUILD(sizp, u_int32_t *, NFSX_UNSIGNED);
+ if (!NFSVNO_EXGSSONLY(&retnes)) {
+ NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
+ *tl = txdr_unsigned(RPCAUTH_UNIX);
+ len++;
+ }
+ for (i = RPCAUTHGSS_SVCNONE; i <= RPCAUTHGSS_SVCPRIVACY; i++) {
+ NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(RPCAUTH_GSS);
+ (void) nfsm_strtom(nd, nfsgss_mechlist[KERBV_MECH].str,
+ nfsgss_mechlist[KERBV_MECH].len);
+ NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
+ *tl++ = txdr_unsigned(GSS_KERBV_QOP);
+ *tl = txdr_unsigned(i);
+ len++;
+ }
+ *sizp = txdr_unsigned(len);
+ return (0);
+}
+
+/*
+ * nfsv4 set client id service
+ */
+APPLESTATIC int
+nfsrvd_setclientid(struct nfsrv_descript *nd, __unused int isdgram,
+ __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ int i;
+ int error = 0, idlen;
+ struct nfsclient *clp = NULL;
+ struct sockaddr_in *rad;
+ u_char *verf, *ucp, *ucp2, addrbuf[24];
+ nfsquad_t clientid, confirm;
+
+ if ((!nfs_rootfhset && !nfsv4root_set) ||
+ (nd->nd_flag & (ND_GSS | ND_EXGSSONLY)) == ND_EXGSSONLY) {
+ nd->nd_repstat = NFSERR_WRONGSEC;
+ return (0);
+ }
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_VERF + NFSX_UNSIGNED);
+ verf = (u_char *)tl;
+ tl += (NFSX_VERF / NFSX_UNSIGNED);
+ i = fxdr_unsigned(int, *tl);
+ if (i > NFSV4_OPAQUELIMIT || i <= 0) {
+ nd->nd_repstat = NFSERR_BADXDR;
+ return (error);
+ }
+ idlen = i;
+ if (nd->nd_flag & ND_GSS)
+ i += nd->nd_princlen;
+ MALLOC(clp, struct nfsclient *, sizeof (struct nfsclient) + i,
+ M_NFSDCLIENT, M_WAITOK);
+ NFSBZERO((caddr_t)clp, sizeof (struct nfsclient) + i);
+ NFSINITSOCKMUTEX(&clp->lc_req.nr_mtx);
+ NFSSOCKADDRALLOC(clp->lc_req.nr_nam);
+ NFSSOCKADDRSIZE(clp->lc_req.nr_nam, sizeof (struct sockaddr_in));
+ clp->lc_req.nr_cred = NULL;
+ NFSBCOPY(verf, clp->lc_verf, NFSX_VERF);
+ clp->lc_idlen = idlen;
+ error = nfsrv_mtostr(nd, clp->lc_id, idlen);
+ if (error)
+ goto nfsmout;
+ if (nd->nd_flag & ND_GSS) {
+ clp->lc_flags = LCL_GSS;
+ if (nd->nd_flag & ND_GSSINTEGRITY)
+ clp->lc_flags |= LCL_GSSINTEGRITY;
+ else if (nd->nd_flag & ND_GSSPRIVACY)
+ clp->lc_flags |= LCL_GSSPRIVACY;
+ } else {
+ clp->lc_flags = 0;
+ }
+ if ((nd->nd_flag & ND_GSS) && nd->nd_princlen > 0) {
+ clp->lc_flags |= LCL_NAME;
+ clp->lc_namelen = nd->nd_princlen;
+ clp->lc_name = &clp->lc_id[idlen];
+ NFSBCOPY(nd->nd_principal, clp->lc_name, clp->lc_namelen);
+ } else {
+ clp->lc_uid = nd->nd_cred->cr_uid;
+ clp->lc_gid = nd->nd_cred->cr_gid;
+ }
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
+ clp->lc_program = fxdr_unsigned(u_int32_t, *tl);
+ error = nfsrv_getclientipaddr(nd, clp);
+ if (error)
+ goto nfsmout;
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
+ clp->lc_callback = fxdr_unsigned(u_int32_t, *tl);
+
+ /*
+ * nfsrv_setclient() does the actual work of adding it to the
+ * client list. If there is no error, the structure has been
+ * linked into the client list and clp should no longer be used
+ * here. When an error is returned, it has not been linked in,
+ * so it should be free'd.
+ */
+ nd->nd_repstat = nfsrv_setclient(nd, &clp, &clientid, &confirm, p);
+ if (nd->nd_repstat == NFSERR_CLIDINUSE) {
+ if (clp->lc_flags & LCL_TCPCALLBACK)
+ (void) nfsm_strtom(nd, "tcp", 3);
+ else
+ (void) nfsm_strtom(nd, "udp", 3);
+ rad = NFSSOCKADDR(clp->lc_req.nr_nam, struct sockaddr_in *);
+ ucp = (u_char *)&rad->sin_addr.s_addr;
+ ucp2 = (u_char *)&rad->sin_port;
+ sprintf(addrbuf, "%d.%d.%d.%d.%d.%d", ucp[0] & 0xff,
+ ucp[1] & 0xff, ucp[2] & 0xff, ucp[3] & 0xff,
+ ucp2[0] & 0xff, ucp2[1] & 0xff);
+ (void) nfsm_strtom(nd, addrbuf, strlen(addrbuf));
+ }
+ if (clp) {
+ NFSSOCKADDRFREE(clp->lc_req.nr_nam);
+ NFSFREEMUTEX(&clp->lc_req.nr_mtx);
+ free((caddr_t)clp, M_NFSDCLIENT);
+ }
+ if (!nd->nd_repstat) {
+ NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_HYPER);
+ *tl++ = clientid.lval[0];
+ *tl++ = clientid.lval[1];
+ *tl++ = confirm.lval[0];
+ *tl = confirm.lval[1];
+ }
+ return (0);
+nfsmout:
+ if (clp) {
+ NFSSOCKADDRFREE(clp->lc_req.nr_nam);
+ NFSFREEMUTEX(&clp->lc_req.nr_mtx);
+ free((caddr_t)clp, M_NFSDCLIENT);
+ }
+ return (error);
+}
+
+/*
+ * nfsv4 set client id confirm service
+ */
+APPLESTATIC int
+nfsrvd_setclientidcfrm(struct nfsrv_descript *nd,
+ __unused int isdgram, __unused vnode_t vp, NFSPROC_T *p,
+ __unused struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ int error = 0;
+ nfsquad_t clientid, confirm;
+
+ if ((!nfs_rootfhset && !nfsv4root_set) ||
+ (nd->nd_flag & (ND_GSS | ND_EXGSSONLY)) == ND_EXGSSONLY) {
+ nd->nd_repstat = NFSERR_WRONGSEC;
+ return (0);
+ }
+ NFSM_DISSECT(tl, u_int32_t *, 2 * NFSX_HYPER);
+ clientid.lval[0] = *tl++;
+ clientid.lval[1] = *tl++;
+ confirm.lval[0] = *tl++;
+ confirm.lval[1] = *tl;
+
+ /*
+ * nfsrv_getclient() searches the client list for a match and
+ * returns the appropriate NFSERR status.
+ */
+ nd->nd_repstat = nfsrv_getclient(clientid, (CLOPS_CONFIRM|CLOPS_RENEW),
+ NULL, confirm, nd, p);
+nfsmout:
+ return (error);
+}
+
+/*
+ * nfsv4 verify service
+ */
+APPLESTATIC int
+nfsrvd_verify(struct nfsrv_descript *nd, int isdgram,
+ vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+ int error = 0, ret, fhsize = NFSX_MYFH;
+ struct nfsvattr nva;
+ struct statfs sf;
+ struct nfsfsinfo fs;
+ fhandle_t fh;
+
+ nd->nd_repstat = nfsvno_getattr(vp, &nva, nd->nd_cred, p);
+ if (!nd->nd_repstat)
+ nd->nd_repstat = nfsvno_statfs(vp, &sf, nd->nd_cred, p);
+ if (!nd->nd_repstat)
+ nd->nd_repstat = nfsvno_getfh(vp, &fh, p);
+ if (!nd->nd_repstat) {
+ nfsvno_getfs(&fs, isdgram);
+ error = nfsv4_loadattr(nd, vp, &nva, NULL, &fh, fhsize, NULL,
+ &sf, NULL, &fs, NULL, 1, &ret, NULL, NULL, p, nd->nd_cred);
+ if (!error) {
+ if (nd->nd_procnum == NFSV4OP_NVERIFY) {
+ if (ret == 0)
+ nd->nd_repstat = NFSERR_SAME;
+ else if (ret != NFSERR_NOTSAME)
+ nd->nd_repstat = ret;
+ } else if (ret)
+ nd->nd_repstat = ret;
+ }
+ }
+ vput(vp);
+ return (error);
+}
+
+/*
+ * nfs openattr rpc
+ */
+APPLESTATIC int
+nfsrvd_openattr(struct nfsrv_descript *nd, __unused int isdgram,
+ vnode_t dp, __unused vnode_t *vpp, __unused fhandle_t *fhp,
+ __unused NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ int error = 0, createdir;
+
+ NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
+ createdir = fxdr_unsigned(int, *tl);
+ nd->nd_repstat = NFSERR_NOTSUPP;
+nfsmout:
+ vrele(dp);
+ return (error);
+}
+
+/*
+ * nfsv4 release lock owner service
+ */
+APPLESTATIC int
+nfsrvd_releaselckown(struct nfsrv_descript *nd, __unused int isdgram,
+ __unused vnode_t vp, NFSPROC_T *p, __unused struct nfsexstuff *exp)
+{
+ u_int32_t *tl;
+ struct nfsstate *stp = NULL;
+ int error = 0, len;
+ nfsquad_t clientid;
+
+ NFSM_DISSECT(tl, u_int32_t *, 3 * NFSX_UNSIGNED);
+ len = fxdr_unsigned(int, *(tl + 2));
+ MALLOC(stp, struct nfsstate *, sizeof (struct nfsstate) + len,
+ M_NFSDSTATE, M_WAITOK);
+ stp->ls_ownerlen = len;
+ stp->ls_op = NULL;
+ stp->ls_flags = NFSLCK_RELEASE;
+ stp->ls_uid = nd->nd_cred->cr_uid;
+ clientid.lval[0] = *tl++;
+ clientid.lval[1] = *tl;
+ if (nd->nd_flag & ND_IMPLIEDCLID) {
+ if (nd->nd_clientid.qval != clientid.qval)
+ printf("EEK! multiple clids\n");
+ } else {
+ nd->nd_flag |= ND_IMPLIEDCLID;
+ nd->nd_clientid.qval = clientid.qval;
+ }
+ error = nfsrv_mtostr(nd, stp->ls_owner, len);
+ if (error)
+ goto nfsmout;
+ nd->nd_repstat = nfsrv_releaselckown(stp, clientid, p);
+ FREE((caddr_t)stp, M_NFSDSTATE);
+ return (0);
+nfsmout:
+ if (stp)
+ free((caddr_t)stp, M_NFSDSTATE);
+ return (error);
+}
OpenPOWER on IntegriCloud