aboutsummaryrefslogtreecommitdiff
path: root/sys/fs/nfsserver/nfs_nfsdport.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/fs/nfsserver/nfs_nfsdport.c')
-rw-r--r--sys/fs/nfsserver/nfs_nfsdport.c386
1 files changed, 306 insertions, 80 deletions
diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c
index 6f5b2855bcf0..4f0d5946d6b9 100644
--- a/sys/fs/nfsserver/nfs_nfsdport.c
+++ b/sys/fs/nfsserver/nfs_nfsdport.c
@@ -69,6 +69,7 @@ extern int nfsrv_maxpnfsmirror;
extern uint32_t nfs_srvmaxio;
extern int nfs_bufpackets;
extern u_long sb_max_adj;
+extern struct nfsv4lock nfsv4rootfs_lock;
NFSD_VNET_DECLARE(int, nfsrv_numnfsd);
NFSD_VNET_DECLARE(struct nfsrv_stablefirst, nfsrv_stablefirst);
@@ -121,7 +122,6 @@ extern struct nfsdevicehead nfsrv_devidhead;
/* Map d_type to vnode type. */
static uint8_t dtype_to_vnode[DT_WHT + 1] = { VNON, VFIFO, VCHR, VNON, VDIR,
VNON, VBLK, VNON, VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON };
-#define NFS_DTYPETOVTYPE(t) ((t) <= DT_WHT ? dtype_to_vnode[(t)] : VNON)
static int nfsrv_createiovec(int, struct mbuf **, struct mbuf **,
struct iovec **);
@@ -129,6 +129,7 @@ static int nfsrv_createiovec_extpgs(int, int, struct mbuf **,
struct mbuf **, struct iovec **);
static int nfsrv_createiovecw(int, struct mbuf *, char *, struct iovec **,
int *);
+static void nfs_dtypetovtype(struct nfsvattr *, struct vnode *, uint8_t);
static void nfsrv_pnfscreate(struct vnode *, struct vattr *, struct ucred *,
NFSPROC_T *);
static void nfsrv_pnfsremovesetup(struct vnode *, NFSPROC_T *, struct vnode **,
@@ -178,8 +179,6 @@ SYSCTL_INT(_vfs_nfsd, OID_AUTO, commit_miss, CTLFLAG_RW, &nfs_commit_miss,
0, "");
SYSCTL_INT(_vfs_nfsd, OID_AUTO, issue_delegations, CTLFLAG_RW,
&nfsrv_issuedelegs, 0, "Enable nfsd to issue delegations");
-SYSCTL_INT(_vfs_nfsd, OID_AUTO, enable_locallocks, CTLFLAG_RW,
- &nfsrv_dolocallocks, 0, "Enable nfsd to acquire local locks on files");
SYSCTL_INT(_vfs_nfsd, OID_AUTO, debuglevel, CTLFLAG_RW, &nfsd_debuglevel,
0, "Debug level for NFS server");
NFSD_VNET_DECLARE(int, nfsd_enable_stringtouid);
@@ -189,6 +188,10 @@ SYSCTL_INT(_vfs_nfsd, OID_AUTO, enable_stringtouid,
static int nfsrv_pnfsgetdsattr = 1;
SYSCTL_INT(_vfs_nfsd, OID_AUTO, pnfsgetdsattr, CTLFLAG_RW,
&nfsrv_pnfsgetdsattr, 0, "When set getattr gets DS attributes via RPC");
+static bool nfsrv_recalldeleg = false;
+SYSCTL_BOOL(_vfs_nfsd, OID_AUTO, recalldeleg, CTLFLAG_RW,
+ &nfsrv_recalldeleg, 0,
+ "When set remove/rename recalls delegations for same client");
/*
* nfsrv_dsdirsize can only be increased and only when the nfsd threads are
@@ -294,6 +297,38 @@ SYSCTL_PROC(_vfs_nfsd, OID_AUTO, srvmaxio,
CTLTYPE_UINT | CTLFLAG_MPSAFE | CTLFLAG_RW, NULL, 0,
sysctl_srvmaxio, "IU", "Maximum I/O size in bytes");
+static int
+sysctl_dolocallocks(SYSCTL_HANDLER_ARGS)
+{
+ int error, igotlock, newdolocallocks;
+
+ newdolocallocks = nfsrv_dolocallocks;
+ error = sysctl_handle_int(oidp, &newdolocallocks, 0, req);
+ if (error != 0 || req->newptr == NULL)
+ return (error);
+ if (newdolocallocks == nfsrv_dolocallocks)
+ return (0);
+ if (jailed(curthread->td_ucred))
+ return (EINVAL);
+
+ NFSLOCKV4ROOTMUTEX();
+ do {
+ igotlock = nfsv4_lock(&nfsv4rootfs_lock, 1, NULL,
+ NFSV4ROOTLOCKMUTEXPTR, NULL);
+ } while (!igotlock);
+ NFSUNLOCKV4ROOTMUTEX();
+
+ nfsrv_dolocallocks = newdolocallocks;
+
+ NFSLOCKV4ROOTMUTEX();
+ nfsv4_unlock(&nfsv4rootfs_lock, 0);
+ NFSUNLOCKV4ROOTMUTEX();
+ return (0);
+}
+SYSCTL_PROC(_vfs_nfsd, OID_AUTO, enable_locallocks,
+ CTLTYPE_INT | CTLFLAG_MPSAFE | CTLFLAG_RW, NULL, 0,
+ sysctl_dolocallocks, "IU", "Enable nfsd to acquire local locks on files");
+
#define MAX_REORDERED_RPC 16
#define NUM_HEURISTIC 1031
#define NHUSE_INIT 64
@@ -413,6 +448,8 @@ nfsvno_getattr(struct vnode *vp, struct nfsvattr *nvap,
gotattr = 1;
}
+ nvap->na_bsdflags = 0;
+ nvap->na_flags = 0;
error = VOP_GETATTR(vp, &nvap->na_vattr, nd->nd_cred);
if (lockedit != 0)
NFSVOPUNLOCK(vp);
@@ -1451,32 +1488,61 @@ nfsmout:
* Remove a non-directory object.
*/
int
-nfsvno_removesub(struct nameidata *ndp, int is_v4, struct ucred *cred,
+nfsvno_removesub(struct nameidata *ndp, bool is_v4, struct nfsrv_descript *nd,
struct thread *p, struct nfsexstuff *exp)
{
- struct vnode *vp, *dsdvp[NFSDEV_MAXMIRRORS];
- int error = 0, mirrorcnt;
+ struct vnode *vp, *dsdvp[NFSDEV_MAXMIRRORS], *newvp;
+ struct mount *mp;
+ int error = 0, mirrorcnt, ret;
char fname[PNFS_FILENAME_LEN + 1];
fhandle_t fh;
vp = ndp->ni_vp;
dsdvp[0] = NULL;
- if (vp->v_type == VDIR)
+ if (vp->v_type == VDIR) {
error = NFSERR_ISDIR;
- else if (is_v4)
- error = nfsrv_checkremove(vp, 1, NULL, (nfsquad_t)((u_quad_t)0),
- p);
+ } else if (is_v4) {
+ if (nfsrv_recalldeleg || (nd->nd_flag & ND_NFSV41) == 0)
+ error = nfsrv_checkremove(vp, 1, NULL,
+ (nfsquad_t)((u_quad_t)0), p);
+ else
+ error = nfsrv_checkremove(vp, 1, NULL, nd->nd_clientid,
+ p);
+ }
if (error == 0)
nfsrv_pnfsremovesetup(vp, p, dsdvp, &mirrorcnt, fname, &fh);
if (!error)
error = VOP_REMOVE(ndp->ni_dvp, vp, &ndp->ni_cnd);
if (error == 0 && dsdvp[0] != NULL)
nfsrv_pnfsremove(dsdvp, mirrorcnt, fname, &fh, p);
+ if (is_v4 && (nd->nd_flag & ND_NFSV41) != 0 && error == 0)
+ error = nfsvno_getfh(vp, &fh, p);
if (ndp->ni_dvp == vp)
vrele(ndp->ni_dvp);
else
vput(ndp->ni_dvp);
vput(vp);
+
+ /* Use ret to determine if the file still exists. */
+ if (is_v4 && (nd->nd_flag & ND_NFSV41) != 0 && error == 0) {
+ mp = vfs_busyfs(&fh.fh_fsid);
+ if (mp != NULL) {
+ /* Find out if the file still exists. */
+ ret = VFS_FHTOVP(mp, &fh.fh_fid, LK_SHARED, &newvp);
+ if (ret == 0)
+ vput(newvp);
+ else
+ ret = ESTALE;
+ vfs_unbusy(mp);
+ } else {
+ ret = ESTALE;
+ }
+ if (ret == ESTALE) {
+ /* Get rid of any delegation. */
+ nfsrv_removedeleg(&fh, nd, p);
+ }
+ }
+
nfsvno_relpathbuf(ndp);
NFSEXITCODE(error);
return (error);
@@ -1527,33 +1593,34 @@ out:
*/
int
nfsvno_rename(struct nameidata *fromndp, struct nameidata *tondp,
- u_int32_t ndstat, u_int32_t ndflag, struct ucred *cred, struct thread *p)
+ struct nfsrv_descript *nd, struct thread *p)
{
- struct vnode *fvp, *tvp, *tdvp, *dsdvp[NFSDEV_MAXMIRRORS];
- int error = 0, mirrorcnt;
+ struct vnode *fvp, *tvp, *tdvp, *dsdvp[NFSDEV_MAXMIRRORS], *newvp;
+ struct mount *mp;
+ int error = 0, mirrorcnt, ret;
char fname[PNFS_FILENAME_LEN + 1];
- fhandle_t fh;
+ fhandle_t fh, fh2;
dsdvp[0] = NULL;
fvp = fromndp->ni_vp;
- if (ndstat) {
+ if (nd->nd_repstat != 0) {
vrele(fromndp->ni_dvp);
vrele(fvp);
- error = ndstat;
+ error = nd->nd_repstat;
goto out1;
}
tdvp = tondp->ni_dvp;
tvp = tondp->ni_vp;
if (tvp != NULL) {
if (fvp->v_type == VDIR && tvp->v_type != VDIR) {
- error = (ndflag & ND_NFSV2) ? EISDIR : EEXIST;
+ error = (nd->nd_flag & ND_NFSV2) ? EISDIR : EEXIST;
goto out;
} else if (fvp->v_type != VDIR && tvp->v_type == VDIR) {
- error = (ndflag & ND_NFSV2) ? ENOTDIR : EEXIST;
+ error = (nd->nd_flag & ND_NFSV2) ? ENOTDIR : EEXIST;
goto out;
}
if (tvp->v_type == VDIR && tvp->v_mountedhere) {
- error = (ndflag & ND_NFSV2) ? ENOTEMPTY : EXDEV;
+ error = (nd->nd_flag & ND_NFSV2) ? ENOTEMPTY : EXDEV;
goto out;
}
@@ -1572,35 +1639,45 @@ nfsvno_rename(struct nameidata *fromndp, struct nameidata *tondp,
}
}
if (fvp->v_type == VDIR && fvp->v_mountedhere) {
- error = (ndflag & ND_NFSV2) ? ENOTEMPTY : EXDEV;
+ error = (nd->nd_flag & ND_NFSV2) ? ENOTEMPTY : EXDEV;
goto out;
}
if (fvp->v_mount != tdvp->v_mount) {
- error = (ndflag & ND_NFSV2) ? ENOTEMPTY : EXDEV;
+ error = (nd->nd_flag & ND_NFSV2) ? ENOTEMPTY : EXDEV;
goto out;
}
if (fvp == tdvp) {
- error = (ndflag & ND_NFSV2) ? ENOTEMPTY : EINVAL;
+ error = (nd->nd_flag & ND_NFSV2) ? ENOTEMPTY : EINVAL;
goto out;
}
if (fvp == tvp) {
/*
- * If source and destination are the same, there is nothing to
- * do. Set error to -1 to indicate this.
+ * If source and destination are the same, there is
+ * nothing to do. Set error to EJUSTRETURN to indicate
+ * this.
*/
- error = -1;
+ error = EJUSTRETURN;
goto out;
}
- if (ndflag & ND_NFSV4) {
+ if (nd->nd_flag & ND_NFSV4) {
if (NFSVOPLOCK(fvp, LK_EXCLUSIVE) == 0) {
- error = nfsrv_checkremove(fvp, 0, NULL,
- (nfsquad_t)((u_quad_t)0), p);
+ if (nfsrv_recalldeleg || (nd->nd_flag & ND_NFSV41) == 0)
+ error = nfsrv_checkremove(fvp, 0, NULL,
+ (nfsquad_t)((u_quad_t)0), p);
+ else
+ error = nfsrv_checkremove(fvp, 0, NULL,
+ nd->nd_clientid, p);
NFSVOPUNLOCK(fvp);
} else
error = EPERM;
- if (tvp && !error)
- error = nfsrv_checkremove(tvp, 1, NULL,
- (nfsquad_t)((u_quad_t)0), p);
+ if (tvp && !error) {
+ if (nfsrv_recalldeleg || (nd->nd_flag & ND_NFSV41) == 0)
+ error = nfsrv_checkremove(tvp, 1, NULL,
+ (nfsquad_t)((u_quad_t)0), p);
+ else
+ error = nfsrv_checkremove(tvp, 1, NULL,
+ nd->nd_clientid, p);
+ }
} else {
/*
* For NFSv2 and NFSv3, try to get rid of the delegation, so
@@ -1612,15 +1689,35 @@ nfsvno_rename(struct nameidata *fromndp, struct nameidata *tondp,
nfsd_recalldelegation(fvp, p);
}
if (error == 0 && tvp != NULL) {
- nfsrv_pnfsremovesetup(tvp, p, dsdvp, &mirrorcnt, fname, &fh);
+ if ((nd->nd_flag & ND_NFSV41) != 0)
+ error = nfsvno_getfh(tvp, &fh2, p);
+ if (error == 0)
+ nfsrv_pnfsremovesetup(tvp, p, dsdvp, &mirrorcnt, fname,
+ &fh);
NFSD_DEBUG(4, "nfsvno_rename: pnfsremovesetup"
" dsdvp=%p\n", dsdvp[0]);
}
out:
- if (!error) {
+ mp = NULL;
+ if (error == 0) {
+ error = VOP_GETWRITEMOUNT(tondp->ni_dvp, &mp);
+ if (error == 0) {
+ if (mp == NULL) {
+ error = ENOENT;
+ } else {
+ error = lockmgr(&mp->mnt_renamelock,
+ LK_EXCLUSIVE | LK_NOWAIT, NULL);
+ if (error != 0)
+ error = ERELOOKUP;
+ }
+ }
+ }
+ if (error == 0) {
error = VOP_RENAME(fromndp->ni_dvp, fromndp->ni_vp,
&fromndp->ni_cnd, tondp->ni_dvp, tondp->ni_vp,
&tondp->ni_cnd);
+ lockmgr(&mp->mnt_renamelock, LK_RELEASE, 0);
+ vfs_rel(mp);
} else {
if (tdvp == tvp)
vrele(tdvp);
@@ -1630,8 +1727,13 @@ out:
vput(tvp);
vrele(fromndp->ni_dvp);
vrele(fvp);
- if (error == -1)
+ if (error == EJUSTRETURN) {
error = 0;
+ } else if (error == ERELOOKUP && mp != NULL) {
+ lockmgr(&mp->mnt_renamelock, LK_EXCLUSIVE, 0);
+ lockmgr(&mp->mnt_renamelock, LK_RELEASE, 0);
+ vfs_rel(mp);
+ }
}
/*
@@ -1644,6 +1746,26 @@ out:
NFSD_DEBUG(4, "nfsvno_rename: pnfsremove\n");
}
+ /* Use ret to determine if the file still exists. */
+ if ((nd->nd_flag & ND_NFSV41) != 0 && error == 0) {
+ mp = vfs_busyfs(&fh2.fh_fsid);
+ if (mp != NULL) {
+ /* Find out if the file still exists. */
+ ret = VFS_FHTOVP(mp, &fh2.fh_fid, LK_SHARED, &newvp);
+ if (ret == 0)
+ vput(newvp);
+ else
+ ret = ESTALE;
+ vfs_unbusy(mp);
+ } else {
+ ret = ESTALE;
+ }
+ if (ret == ESTALE) {
+ /* Get rid of any delegation. */
+ nfsrv_removedeleg(&fh2, nd, p);
+ }
+ }
+
nfsvno_relpathbuf(tondp);
out1:
nfsvno_relpathbuf(fromndp);
@@ -1990,7 +2112,8 @@ int
nfsvno_fillattr(struct nfsrv_descript *nd, struct mount *mp, struct vnode *vp,
struct nfsvattr *nvap, fhandle_t *fhp, int rderror, nfsattrbit_t *attrbitp,
struct ucred *cred, struct thread *p, int isdgram, int reterr,
- int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno)
+ int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno,
+ bool xattrsupp, bool has_hiddensystem, bool has_namedattr)
{
struct statfs *sf;
int error;
@@ -2009,12 +2132,29 @@ nfsvno_fillattr(struct nfsrv_descript *nd, struct mount *mp, struct vnode *vp,
}
error = nfsv4_fillattr(nd, mp, vp, NULL, &nvap->na_vattr, fhp, rderror,
attrbitp, cred, p, isdgram, reterr, supports_nfsv4acls, at_root,
- mounted_on_fileno, sf);
+ mounted_on_fileno, sf, xattrsupp, has_hiddensystem, has_namedattr);
free(sf, M_TEMP);
NFSEXITCODE2(0, nd);
return (error);
}
+/*
+ * Convert a dirent d_type to a vnode type.
+ */
+static void nfs_dtypetovtype(struct nfsvattr *nvap, struct vnode *vp,
+ uint8_t dtype)
+{
+
+ if ((vn_irflag_read(vp) & VIRF_NAMEDDIR) != 0) {
+ nvap->na_type = VREG;
+ nvap->na_bsdflags |= SFBSD_NAMEDATTR;
+ } else if (dtype <= DT_WHT) {
+ nvap->na_type = dtype_to_vnode[dtype];
+ } else {
+ nvap->na_type = VNON;
+ }
+}
+
/* Since the Readdir vnode ops vary, put the entire functions in here. */
/*
* nfs readdir service
@@ -2309,7 +2449,7 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
struct nfsvattr nva, at, *nvap = &nva;
struct mbuf *mb0, *mb1;
struct nfsreferral *refp;
- int nlen, r, error = 0, getret = 1, usevget = 1;
+ int nlen, r, error = 0, getret = 1, ret, usevget = 1;
int siz, cnt, fullsiz, eofflag, ncookies, entrycnt;
caddr_t bpos0, bpos1;
u_int64_t off, toff, verf __unused;
@@ -2323,6 +2463,9 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
uint64_t mounted_on_fileno;
struct thread *p = curthread;
int bextpg0, bextpg1, bextpgsiz0, bextpgsiz1;
+ size_t atsiz;
+ long pathval;
+ bool has_hiddensystem, has_namedattr, xattrsupp;
if (nd->nd_repstat) {
nfsrv_postopattr(nd, getret, &at);
@@ -2634,6 +2777,10 @@ again:
LK_SHARED, &nvp);
else
r = EOPNOTSUPP;
+ if (r == 0 && (vn_irflag_read(vp) &
+ VIRF_NAMEDDIR) != 0)
+ vn_irflag_set_cond(nvp,
+ VIRF_NAMEDATTR);
if (r == EOPNOTSUPP) {
if (usevget) {
usevget = 0;
@@ -2648,6 +2795,10 @@ again:
cn.cn_namelen = nlen;
cn.cn_flags = ISLASTCN |
NOFOLLOW | LOCKLEAF;
+ if ((vn_irflag_read(vp) &
+ VIRF_NAMEDDIR) != 0)
+ cn.cn_flags |=
+ OPENNAMED;
if (nlen == 2 &&
dp->d_name[0] == '.' &&
dp->d_name[1] == '.')
@@ -2765,7 +2916,7 @@ again:
/* Only need Type and/or Fileid. */
VATTR_NULL(&nvap->na_vattr);
nvap->na_fileid = dp->d_fileno;
- nvap->na_type = NFS_DTYPETOVTYPE(dp->d_type);
+ nfs_dtypetovtype(nvap, vp, dp->d_type);
}
/*
@@ -2789,9 +2940,32 @@ again:
*tl++ = newnfs_true;
txdr_hyper(*cookiep, tl);
dirlen += nfsm_strtom(nd, dp->d_name, nlen);
+ xattrsupp = false;
+ has_hiddensystem = false;
+ has_namedattr = false;
if (nvp != NULL) {
supports_nfsv4acls =
nfs_supportsnfsv4acls(nvp);
+ if (NFSISSET_ATTRBIT(&attrbits,
+ NFSATTRBIT_XATTRSUPPORT)) {
+ ret = VOP_GETEXTATTR(nvp,
+ EXTATTR_NAMESPACE_USER,
+ "xxx", NULL, &atsiz,
+ nd->nd_cred, p);
+ xattrsupp = ret != EOPNOTSUPP;
+ }
+ if (VOP_PATHCONF(nvp,
+ _PC_HAS_HIDDENSYSTEM, &pathval) !=
+ 0)
+ pathval = 0;
+ has_hiddensystem = pathval > 0;
+ pathval = 0;
+ if (NFSISSET_ATTRBIT(&attrbits,
+ NFSATTRBIT_NAMEDATTR) &&
+ VOP_PATHCONF(nvp, _PC_HAS_NAMEDATTR,
+ &pathval) != 0)
+ pathval = 0;
+ has_namedattr = pathval > 0;
NFSVOPUNLOCK(nvp);
} else
supports_nfsv4acls = 0;
@@ -2811,13 +2985,15 @@ again:
nvp, nvap, &nfh, r, &rderrbits,
nd->nd_cred, p, isdgram, 0,
supports_nfsv4acls, at_root,
- mounted_on_fileno);
+ mounted_on_fileno, xattrsupp,
+ has_hiddensystem, has_namedattr);
} else {
dirlen += nfsvno_fillattr(nd, new_mp,
nvp, nvap, &nfh, r, &attrbits,
nd->nd_cred, p, isdgram, 0,
supports_nfsv4acls, at_root,
- mounted_on_fileno);
+ mounted_on_fileno, xattrsupp,
+ has_hiddensystem, has_namedattr);
}
if (nvp != NULL)
vrele(nvp);
@@ -2995,12 +3171,17 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
/*
* Loop around getting the setable attributes. If an unsupported
* one is found, set nd_repstat == NFSERR_ATTRNOTSUPP and return.
+ * Once nd_repstat != 0, do not set the attribute value, but keep
+ * parsing the attribute(s).
*/
if (retnotsup) {
nd->nd_repstat = NFSERR_ATTRNOTSUPP;
bitpos = NFSATTRBIT_MAX;
} else {
bitpos = 0;
+ if (NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_HIDDEN) ||
+ NFSISSET_ATTRBIT(attrbitp, NFSATTRBIT_SYSTEM))
+ nvap->na_flags = 0;
}
moderet = 0;
for (; bitpos < NFSATTRBIT_MAX; bitpos++) {
@@ -3012,12 +3193,13 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
switch (bitpos) {
case NFSATTRBIT_SIZE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_HYPER);
- if (vp != NULL && vp->v_type != VREG) {
- error = (vp->v_type == VDIR) ? NFSERR_ISDIR :
- NFSERR_INVAL;
- goto nfsmout;
+ if (!nd->nd_repstat) {
+ if (vp != NULL && vp->v_type != VREG)
+ nd->nd_repstat = (vp->v_type == VDIR) ?
+ NFSERR_ISDIR : NFSERR_INVAL;
+ else
+ nvap->na_size = fxdr_hyper(tl);
}
- nvap->na_size = fxdr_hyper(tl);
attrsum += NFSX_HYPER;
break;
case NFSATTRBIT_ACL:
@@ -3036,9 +3218,11 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_HIDDEN:
- NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
- if (!nd->nd_repstat)
- nd->nd_repstat = NFSERR_ATTRNOTSUPP;
+ NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
+ if (nd->nd_repstat == 0) {
+ if (*tl == newnfs_true)
+ nvap->na_flags |= UF_HIDDEN;
+ }
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_MIMETYPE:
@@ -3054,7 +3238,8 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
case NFSATTRBIT_MODE:
moderet = NFSERR_INVAL; /* Can't do MODESETMASKED. */
NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
- nvap->na_mode = nfstov_mode(*tl);
+ if (!nd->nd_repstat)
+ nvap->na_mode = nfstov_mode(*tl);
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_OWNER:
@@ -3112,9 +3297,11 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
attrsum += (NFSX_UNSIGNED + NFSM_RNDUP(j));
break;
case NFSATTRBIT_SYSTEM:
- NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
- if (!nd->nd_repstat)
- nd->nd_repstat = NFSERR_ATTRNOTSUPP;
+ NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED);
+ if (nd->nd_repstat == 0) {
+ if (*tl == newnfs_true)
+ nvap->na_flags |= UF_SYSTEM;
+ }
attrsum += NFSX_UNSIGNED;
break;
case NFSATTRBIT_TIMEACCESSSET:
@@ -3122,10 +3309,11 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
attrsum += NFSX_UNSIGNED;
if (fxdr_unsigned(int, *tl)==NFSV4SATTRTIME_TOCLIENT) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
- fxdr_nfsv4time(tl, &nvap->na_atime);
+ if (!nd->nd_repstat)
+ fxdr_nfsv4time(tl, &nvap->na_atime);
toclient = 1;
attrsum += NFSX_V4TIME;
- } else {
+ } else if (!nd->nd_repstat) {
vfs_timestamp(&nvap->na_atime);
nvap->na_vaflags |= VA_UTIMES_NULL;
}
@@ -3138,7 +3326,8 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
break;
case NFSATTRBIT_TIMECREATE:
NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
- fxdr_nfsv4time(tl, &nvap->na_btime);
+ if (!nd->nd_repstat)
+ fxdr_nfsv4time(tl, &nvap->na_btime);
attrsum += NFSX_V4TIME;
break;
case NFSATTRBIT_TIMEMODIFYSET:
@@ -3146,10 +3335,11 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
attrsum += NFSX_UNSIGNED;
if (fxdr_unsigned(int, *tl)==NFSV4SATTRTIME_TOCLIENT) {
NFSM_DISSECT(tl, u_int32_t *, NFSX_V4TIME);
- fxdr_nfsv4time(tl, &nvap->na_mtime);
+ if (!nd->nd_repstat)
+ fxdr_nfsv4time(tl, &nvap->na_mtime);
nvap->na_vaflags &= ~VA_UTIMES_NULL;
attrsum += NFSX_V4TIME;
- } else {
+ } else if (!nd->nd_repstat) {
vfs_timestamp(&nvap->na_mtime);
if (!toclient)
nvap->na_vaflags |= VA_UTIMES_NULL;
@@ -3167,18 +3357,40 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
* specified and this attribute cannot be done in the
* same Setattr operation.
*/
- if ((nd->nd_flag & ND_NFSV41) == 0)
- nd->nd_repstat = NFSERR_ATTRNOTSUPP;
- else if ((mode & ~07777) != 0 || (mask & ~07777) != 0 ||
- vp == NULL)
- nd->nd_repstat = NFSERR_INVAL;
- else if (moderet == 0)
- moderet = VOP_GETATTR(vp, &va, nd->nd_cred);
- if (moderet == 0)
- nvap->na_mode = (mode & mask) |
- (va.va_mode & ~mask);
- else
- nd->nd_repstat = moderet;
+ if (!nd->nd_repstat) {
+ if ((nd->nd_flag & ND_NFSV41) == 0)
+ nd->nd_repstat = NFSERR_ATTRNOTSUPP;
+ else if ((mode & ~07777) != 0 ||
+ (mask & ~07777) != 0 || vp == NULL)
+ nd->nd_repstat = NFSERR_INVAL;
+ else if (moderet == 0)
+ moderet = VOP_GETATTR(vp, &va,
+ nd->nd_cred);
+ if (moderet == 0)
+ nvap->na_mode = (mode & mask) |
+ (va.va_mode & ~mask);
+ else
+ nd->nd_repstat = moderet;
+ }
+ attrsum += 2 * NFSX_UNSIGNED;
+ break;
+ case NFSATTRBIT_MODEUMASK:
+ NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+ mode = fxdr_unsigned(u_short, *tl++);
+ mask = fxdr_unsigned(u_short, *tl);
+ /*
+ * If moderet != 0, mode has already been done.
+ * If vp != NULL, this is not a file object creation.
+ */
+ if (!nd->nd_repstat) {
+ if ((nd->nd_flag & ND_NFSV42) == 0)
+ nd->nd_repstat = NFSERR_ATTRNOTSUPP;
+ else if ((mask & ~0777) != 0 || vp != NULL ||
+ moderet != 0)
+ nd->nd_repstat = NFSERR_INVAL;
+ else
+ nvap->na_mode = (mode & ~mask);
+ }
attrsum += 2 * NFSX_UNSIGNED;
break;
default:
@@ -3193,7 +3405,7 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
/*
* some clients pad the attrlist, so we need to skip over the
- * padding.
+ * padding. This also skips over unparsed non-supported attributes.
*/
if (attrsum > attrsize) {
error = NFSERR_BADXDR;
@@ -3251,7 +3463,11 @@ nfsd_excred(struct nfsrv_descript *nd, struct nfsexstuff *exp,
NFSVNO_EXPORTANON(exp) ||
(nd->nd_flag & ND_AUTHNONE) != 0) {
nd->nd_cred->cr_uid = credanon->cr_uid;
- nd->nd_cred->cr_gid = credanon->cr_gid;
+ /*
+ * 'credanon' is already a 'struct ucred' that was built
+ * internally with calls to crsetgroups_fallback(), so
+ * we don't need a fallback here.
+ */
crsetgroups(nd->nd_cred, credanon->cr_ngroups,
credanon->cr_groups);
} else if ((nd->nd_flag & ND_GSS) == 0) {
@@ -3398,6 +3614,15 @@ nfsd_fhtovp(struct nfsrv_descript *nd, struct nfsrvfh *nfp, int lktype,
&credanon);
vfs_unbusy(mp);
+ if (nd->nd_repstat == 0 &&
+ nfp->nfsrvfh_len >= NFSX_MYFH + NFSX_V4NAMEDDIRFH &&
+ nfp->nfsrvfh_len <= NFSX_MYFH + NFSX_V4NAMEDATTRFH) {
+ if (nfp->nfsrvfh_len == NFSX_MYFH + NFSX_V4NAMEDDIRFH)
+ vn_irflag_set_cond(*vpp, VIRF_NAMEDDIR);
+ else
+ vn_irflag_set_cond(*vpp, VIRF_NAMEDATTR);
+ }
+
/*
* For NFSv4 without a pseudo root fs, unexported file handles
* can be returned, so that Lookup works everywhere.
@@ -5464,7 +5689,7 @@ nfsrv_writedsdorpc(struct nfsmount *nmp, fhandle_t *fhp, off_t off, int len,
if ((nd->nd_flag & (ND_NOMOREDATA | ND_NFSV4 | ND_V4WCCATTR)) ==
(ND_NFSV4 | ND_V4WCCATTR)) {
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL,
- NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
NFSD_DEBUG(4, "nfsrv_writedsdorpc: wcc attr=%d\n", error);
if (error != 0)
goto nfsmout;
@@ -5495,7 +5720,7 @@ nfsrv_writedsdorpc(struct nfsmount *nmp, fhandle_t *fhp, off_t off, int len,
if (error == 0) {
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL,
- NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
}
NFSD_DEBUG(4, "nfsrv_writedsdorpc: aft loadattr=%d\n", error);
nfsmout:
@@ -5661,7 +5886,7 @@ nfsrv_allocatedsdorpc(struct nfsmount *nmp, fhandle_t *fhp, off_t off,
if (nd->nd_repstat == 0) {
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL,
- NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
} else
error = nd->nd_repstat;
NFSD_DEBUG(4, "nfsrv_allocatedsdorpc: aft loadattr=%d\n", error);
@@ -5828,7 +6053,7 @@ nfsrv_deallocatedsdorpc(struct nfsmount *nmp, fhandle_t *fhp, off_t off,
if ((nd->nd_flag & (ND_NOMOREDATA | ND_NFSV4 | ND_V4WCCATTR)) ==
(ND_NFSV4 | ND_V4WCCATTR)) {
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL,
- NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
NFSD_DEBUG(4, "nfsrv_deallocatedsdorpc: wcc attr=%d\n", error);
if (error != 0)
goto nfsmout;
@@ -5842,7 +6067,7 @@ nfsrv_deallocatedsdorpc(struct nfsmount *nmp, fhandle_t *fhp, off_t off,
if (nd->nd_repstat == 0) {
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL,
- NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
} else
error = nd->nd_repstat;
NFSD_DEBUG(4, "nfsrv_deallocatedsdorpc: aft loadattr=%d\n", error);
@@ -5990,7 +6215,7 @@ nfsrv_setattrdsdorpc(fhandle_t *fhp, struct ucred *cred, NFSPROC_T *p,
if ((nd->nd_flag & (ND_NOMOREDATA | ND_NFSV4 | ND_V4WCCATTR)) ==
(ND_NFSV4 | ND_V4WCCATTR)) {
error = nfsv4_loadattr(nd, NULL, dsnap, NULL, NULL, 0, NULL,
- NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL);
NFSD_DEBUG(4, "nfsrv_setattrdsdorpc: wcc attr=%d\n", error);
if (error != 0)
goto nfsmout;
@@ -6014,7 +6239,8 @@ nfsrv_setattrdsdorpc(fhandle_t *fhp, struct ucred *cred, NFSPROC_T *p,
if (error == 0) {
NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED);
error = nfsv4_loadattr(nd, NULL, dsnap, NULL, NULL, 0, NULL,
- NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL,
+ NULL);
}
NFSD_DEBUG(4, "nfsrv_setattrdsdorpc: aft setattr loadattr=%d\n", error);
nfsmout:
@@ -6159,7 +6385,7 @@ nfsrv_setacldsdorpc(fhandle_t *fhp, struct ucred *cred, NFSPROC_T *p,
* the same type (VREG).
*/
nfsv4_fillattr(nd, NULL, vp, aclp, NULL, NULL, 0, &attrbits, NULL,
- NULL, 0, 0, 0, 0, 0, NULL);
+ NULL, 0, 0, 0, 0, 0, NULL, false, false, false);
error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred,
NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL);
if (error != 0) {
@@ -6303,7 +6529,7 @@ nfsrv_getattrdsrpc(fhandle_t *fhp, struct ucred *cred, NFSPROC_T *p,
if (nd->nd_repstat == 0) {
error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0,
NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL,
- NULL, NULL);
+ NULL, NULL, NULL);
/*
* We can only save the updated values in the extended
* attribute if the vp is exclusively locked.