git: 4fae019c2b8f - main - nfscommon: Prepare the NFS common code for named attributes

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Fri, 25 Apr 2025 01:10:30 UTC
The branch main has been updated by rmacklem:

URL: https://cgit.FreeBSD.org/src/commit/?id=4fae019c2b8f23c6a67ea86b87c9feac5781ab25

commit 4fae019c2b8f23c6a67ea86b87c9feac5781ab25
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2025-04-25 01:09:24 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2025-04-25 01:09:24 +0000

    nfscommon: Prepare the NFS common code for named attributes
    
    Commit 2ec2ba7e232d added support for Solaris style extended
    attributes (called named attributes, which is the NFSv4
    terminology) to the VFS.
    
    This patch updates the NFS code common to the NFS client and
    server, to prepare it for the addition of named attribute
    support.
    
    The actual support for named attributes in the NFSv4 client
    and server will be done in future commits.
---
 sys/fs/nfs/nfs_commonsubs.c | 164 +++++++++++++++++++++++++++++++++++++++++---
 sys/fs/nfs/nfsport.h        |  14 ++--
 sys/fs/nfs/nfsproto.h       |   7 +-
 3 files changed, 171 insertions(+), 14 deletions(-)

diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c
index 7bb2df82272c..3e70eb50a54e 100644
--- a/sys/fs/nfs/nfs_commonsubs.c
+++ b/sys/fs/nfs/nfs_commonsubs.c
@@ -135,7 +135,7 @@ struct nfsv4_opflag nfsv4_opflag[NFSV42_NOPS] = {
 	{ 1, 2, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* Lookupp */
 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* NVerify */
 	{ 1, 1, 0, 1, LK_EXCLUSIVE, 1, 0 },		/* Open */
-	{ 1, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* OpenAttr */
+	{ 1, 1, 1, 1, LK_EXCLUSIVE, 1, 1 },		/* OpenAttr */
 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* OpenConfirm */
 	{ 0, 1, 0, 0, LK_EXCLUSIVE, 1, 0 },		/* OpenDowngrade */
 	{ 1, 0, 0, 0, LK_EXCLUSIVE, 1, 1 },		/* PutFH */
@@ -219,10 +219,11 @@ NFSD_VNET_DEFINE_STATIC(u_char *, nfsrv_dnsname) = NULL;
 static int nfs_bigreply[NFSV42_NPROCS] = { 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,
-    1, 0, 0, 1, 0, 0, 0, 0, 0 };
+    1, 0, 0, 1, 0, 0, 0, 0, 0, 0 };
 
 /* local functions */
 static int nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep);
+static bool nfs_test_namedattr(struct nfsrv_descript *nd, struct vnode *vp);
 static void nfsv4_wanted(struct nfsv4lock *lp);
 static uint32_t nfsv4_filesavail(struct statfs *, struct mount *);
 static int nfsrv_getuser(int procnum, uid_t uid, gid_t gid, char *name);
@@ -230,6 +231,8 @@ static void nfsrv_removeuser(struct nfsusrgrp *usrp, int isuser);
 static int nfsrv_getrefstr(struct nfsrv_descript *, u_char **, u_char **,
     int *, int *);
 static void nfsrv_refstrbigenough(int, u_char **, u_char **, int *);
+static uint32_t vtonfsv4_type(struct vattr *);
+static __enum_uint8(vtype) nfsv4tov_type(uint32_t, uint16_t *);
 
 static struct {
 	int	op;
@@ -307,6 +310,7 @@ static struct {
 	{ NFSV4OP_DEALLOCATE, 2, "Deallocate", 10, },
 	{ NFSV4OP_LAYOUTERROR, 1, "LayoutError", 11, },
 	{ NFSV4OP_VERIFY, 3, "AppendWrite", 11, },
+	{ NFSV4OP_OPENATTR, 3, "OpenAttr", 8, },
 };
 
 /*
@@ -316,7 +320,7 @@ static int nfs_bigrequest[NFSV42_NPROCS] = {
 	0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 	0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
-	0, 1
+	0, 1, 0
 };
 
 /*
@@ -985,6 +989,17 @@ nfsm_fhtom(struct nfsmount *nmp, struct nfsrv_descript *nd, u_int8_t *fhp,
 		    (nmp->nm_privflag & NFSMNTP_FAKEROOTFH) != 0) {
 			fhp = nmp->nm_fh;
 			size = nmp->nm_fhsize;
+		} else if (size >= NFSX_FHMAX + NFSX_V4NAMEDDIRFH &&
+		    size <= NFSX_FHMAX + NFSX_V4NAMEDATTRFH) {
+			size -= (NFSX_FHMAX - NFSX_MYFH);
+			NFSM_BUILD(tl, uint32_t *, NFSX_MYFH +
+			    2 * NFSX_UNSIGNED);
+			*tl++ = txdr_unsigned(size);
+			NFSBCOPY(fhp, tl, NFSX_MYFH);
+			tl += (NFSX_MYFH / NFSX_UNSIGNED);
+			*tl = 0;
+			bytesize = NFSX_MYFH + 2 * NFSX_UNSIGNED;
+			break;
 		}
 		fullsiz = NFSM_RNDUP(size);
 		if (set_true) {
@@ -1267,6 +1282,70 @@ nfsmout:
 	return (error);
 }
 
+/*
+ * Check to see if a named attribute exists for this file.
+ */
+static bool
+nfs_test_namedattr(struct nfsrv_descript *nd, struct vnode *vp)
+{
+	struct uio io;
+	struct iovec iv;
+	struct componentname cn;
+	struct vnode *dvp;
+	struct dirent *dp;
+	int eofflag, error;
+	char *buf, *cp, *endcp;
+	bool ret;
+
+	if (vp == NULL || (vp->v_mount->mnt_flag & MNT_NAMEDATTR) == 0)
+		return (false);
+	NFSNAMEICNDSET(&cn, nd->nd_cred, LOOKUP, OPENNAMED | ISLASTCN |
+	    NOFOLLOW | LOCKLEAF);
+	cn.cn_lkflags = LK_SHARED;
+	cn.cn_nameptr = ".";
+	cn.cn_namelen = 1;
+	error = VOP_LOOKUP(vp, &dvp, &cn);
+	if (error != 0)
+		return (false);
+
+	/* Now we have to read the directory, looking for a valid entry. */
+	buf = malloc(DIRBLKSIZ, M_TEMP, M_WAITOK);
+	ret = false;
+	io.uio_offset = 0;
+	io.uio_segflg = UIO_SYSSPACE;
+	io.uio_rw = UIO_READ;
+	io.uio_td = NULL;
+	do {
+		iv.iov_base = buf;
+		iv.iov_len = DIRBLKSIZ;
+		io.uio_iov = &iv;
+		io.uio_iovcnt = 1;
+		io.uio_resid = DIRBLKSIZ;
+		error = VOP_READDIR(dvp, &io, nd->nd_cred, &eofflag, NULL,
+		    NULL);
+		if (error != 0 || io.uio_resid == DIRBLKSIZ)
+			break;
+		cp = buf;
+		endcp = &buf[DIRBLKSIZ - io.uio_resid];
+		while (cp < endcp) {
+			dp = (struct dirent *)cp;
+			if (dp->d_fileno != 0 && dp->d_type != DT_WHT &&
+			    ((dp->d_namlen == 1 && dp->d_name[0] != '.') ||
+			     (dp->d_namlen == 2 && (dp->d_name[0] != '.' ||
+			      dp->d_name[1] != '.')) || dp->d_namlen > 2)) {
+				ret = true;
+				break;
+			}
+			cp += dp->d_reclen;
+		}
+		if (ret)
+			break;
+	} while (eofflag == 0);
+	vput(dvp);
+	free(buf, M_TEMP);
+	return (ret);
+}
+
 /*
  * Get the attributes for V4.
  * If the compare flag is true, test for any attribute changes,
@@ -1298,6 +1377,7 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
 	gid_t gid;
 	u_int32_t freenum = 0, tuint;
 	u_int64_t uquad = 0, thyp, thyp2;
+	uint16_t tui16;
 #ifdef QUOTA
 	struct dqblk dqb;
 	uid_t savuid;
@@ -1413,11 +1493,16 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
 			if (compare) {
 				if (!(*retcmpp)) {
-				    if (nap->na_type != nfsv34tov_type(*tl))
+				    tui16 = 0;
+				    if (nap->na_type != nfsv4tov_type(*tl,
+					&tui16) ||
+					((nap->na_bsdflags & SFBSD_NAMEDATTR) ^
+					 tui16) != 0)
 					*retcmpp = NFSERR_NOTSAME;
 				}
 			} else if (nap != NULL) {
-				nap->na_type = nfsv34tov_type(*tl);
+				nap->na_type = nfsv4tov_type(*tl,
+				    &nap->na_bsdflags);
 			}
 			attrsum += NFSX_UNSIGNED;
 			break;
@@ -1497,7 +1582,11 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
 		case NFSATTRBIT_NAMEDATTR:
 			NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED);
 			if (compare && !(*retcmpp)) {
-				if (*tl != newnfs_false)
+				bool named_attr;
+
+				named_attr = nfs_test_namedattr(nd, vp);
+				if ((named_attr && *tl != newnfs_true) ||
+				    (!named_attr && *tl != newnfs_false))
 					*retcmpp = NFSERR_NOTSAME;
 			}
 			attrsum += NFSX_UNSIGNED;
@@ -1672,6 +1761,8 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp,
 				goto nfsmout;
 			tfhsize = tnfhp->nfh_len;
 			if (compare) {
+				if (tfhsize > NFSX_MYFH)
+					tfhsize = NFSX_MYFH;
 				if (!(*retcmpp) &&
 				    !NFSRV_CMPFH(tnfhp->nfh_fh, tfhsize,
 				     fhp, fhsize))
@@ -2592,6 +2683,7 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
 	NFSACL_T *aclp, *naclp = NULL;
 	size_t atsiz;
 	bool xattrsupp;
+	short irflag;
 #ifdef QUOTA
 	struct dqblk dqb;
 	uid_t savuid;
@@ -2712,7 +2804,7 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
 			break;
 		case NFSATTRBIT_TYPE:
 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
-			*tl = vtonfsv34_type(vap->va_type);
+			*tl = vtonfsv4_type(vap);
 			retnum += NFSX_UNSIGNED;
 			break;
 		case NFSATTRBIT_FHEXPIRETYPE:
@@ -2748,7 +2840,10 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
 			break;
 		case NFSATTRBIT_NAMEDATTR:
 			NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
-			*tl = newnfs_false;
+			if (nfs_test_namedattr(nd, vp))
+				*tl = newnfs_true;
+			else
+				*tl = newnfs_false;
 			retnum += NFSX_UNSIGNED;
 			break;
 		case NFSATTRBIT_FSID:
@@ -2809,7 +2904,15 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
 			retnum += NFSX_UNSIGNED;
 			break;
 		case NFSATTRBIT_FILEHANDLE:
-			retnum += nfsm_fhtom(NULL, nd, (u_int8_t *)fhp, 0, 0);
+			siz = 0;
+			if (vp != NULL) {
+				irflag = vn_irflag_read(vp);
+				if ((irflag & VIRF_NAMEDDIR) != 0)
+					siz = NFSX_FHMAX + 2;
+				else if ((irflag & VIRF_NAMEDATTR) != 0)
+					siz = NFSX_FHMAX + 3;
+			}
+			retnum += nfsm_fhtom(NULL, nd, (u_int8_t *)fhp, siz, 0);
 			break;
 		case NFSATTRBIT_FILEID:
 			NFSM_BUILD(tl, u_int32_t *, NFSX_HYPER);
@@ -5178,3 +5281,46 @@ nfsrpc_destroysession(struct nfsmount *nmp, struct nfsclsession *tsep,
 	m_freem(nd->nd_mrep);
 	return (error);
 }
+
+/*
+ * Translate a vnode type into an NFSv4 type, including the named
+ * attribute types.
+ */
+static uint32_t
+vtonfsv4_type(struct vattr *vap)
+{
+	nfstype ntyp;
+
+	if (vap->va_type >= 9)
+		ntyp = NFNON;
+	else
+		ntyp = nfsv34_type[vap->va_type];
+	if ((vap->va_bsdflags & SFBSD_NAMEDATTR) != 0) {
+		if (ntyp == NFDIR)
+			ntyp = NFATTRDIR;
+		else if (ntyp == NFREG)
+			ntyp = NFNAMEDATTR;
+	}
+	return (txdr_unsigned((uint32_t)ntyp));
+}
+
+/*
+ * Translate an NFS type to a vnode type.
+ */
+static __enum_uint8(vtype)
+nfsv4tov_type(uint32_t ntyp, uint16_t *bsdflags)
+{
+	__enum_uint8(vtype) vtyp;
+
+	ntyp = fxdr_unsigned(uint32_t, ntyp) % (NFNAMEDATTR + 1);
+	if (ntyp == NFATTRDIR) {
+		vtyp = VDIR;
+		*bsdflags |= SFBSD_NAMEDATTR;
+	} else if (ntyp == NFNAMEDATTR) {
+		vtyp = VREG;
+		*bsdflags |= SFBSD_NAMEDATTR;
+	} else {
+		vtyp = nv34tov_type[ntyp];
+	}
+	return (vtyp);
+}
diff --git a/sys/fs/nfs/nfsport.h b/sys/fs/nfs/nfsport.h
index 0b16ba9b85a8..c30b46261df0 100644
--- a/sys/fs/nfs/nfsport.h
+++ b/sys/fs/nfs/nfsport.h
@@ -439,10 +439,13 @@
 /* Do an NFSv4 Verify+Write. */
 #define	NFSPROC_APPENDWRITE	69
 
+/* Do a NFSv4 Openattr. */
+#define	NFSPROC_OPENATTR	70
+
 /*
  * Must be defined as one higher than the last NFSv4.2 Proc# above.
  */
-#define	NFSV42_NPROCS		70
+#define	NFSV42_NPROCS		71
 
 /* Value of NFSV42_NPROCS for old nfsstats structure. (Always 69) */
 #define	NFSV42_OLDNPROCS	69
@@ -474,7 +477,7 @@ struct nfsstatsv1 {
 	uint64_t	readlink_bios;
 	uint64_t	biocache_readdirs;
 	uint64_t	readdir_bios;
-	uint64_t	rpccnt[NFSV42_NPROCS + 10];
+	uint64_t	rpccnt[NFSV42_NPROCS + 9];
 	uint64_t	rpcretries;
 	uint64_t	srvrpccnt[NFSV42_NOPS + NFSV4OP_FAKENOPS + 15];
 	uint64_t	srvlayouts;
@@ -690,6 +693,7 @@ struct nfsvattr {
 #define	na_bytes	na_vattr.va_bytes
 #define	na_filerev	na_vattr.va_filerev
 #define	na_vaflags	na_vattr.va_vaflags
+#define	na_bsdflags	na_vattr.va_bsdflags
 
 #include <fs/nfsclient/nfsnode.h>
 
@@ -1180,9 +1184,11 @@ struct nfsreq {
  */
 #ifdef VV_DISABLEDELEG
 #define	NFSVNO_DELEGOK(v)						\
-	((v) == NULL || ((v)->v_vflag & VV_DISABLEDELEG) == 0)
+	((v) == NULL || ((v)->v_vflag & VV_DISABLEDELEG) == 0 ||	\
+	 (vn_irflag_read(v) & VIRF_NAMEDATTR) == 0)
 #else
-#define	NFSVNO_DELEGOK(v)	(1)
+#define	NFSVNO_DELEGOK(v)						\
+	((v) == NULL || (vn_irflag_read(v) & VIRF_NAMEDATTR) == 0)
 #endif
 
 /*
diff --git a/sys/fs/nfs/nfsproto.h b/sys/fs/nfs/nfsproto.h
index 995754f42b15..d0660cafdedb 100644
--- a/sys/fs/nfs/nfsproto.h
+++ b/sys/fs/nfs/nfsproto.h
@@ -275,6 +275,8 @@
 #define	NFSX_V4SESSIONID	16
 #define	NFSX_V4DEVICEID		16
 #define	NFSX_V4PNFSFH		(sizeof(fhandle_t) + 1)
+#define	NFSX_V4NAMEDDIRFH	2
+#define	NFSX_V4NAMEDATTRFH	3
 #define	NFSX_V4FILELAYOUT	(4 * NFSX_UNSIGNED + NFSX_V4DEVICEID +	\
 				 NFSX_HYPER + NFSM_RNDUP(NFSX_V4PNFSFH))
 #define	NFSX_V4FLEXLAYOUT(m)	(NFSX_HYPER + 3 * NFSX_UNSIGNED +		\
@@ -406,10 +408,13 @@
 /* Do an NFSv4 Verify+Write. */
 #define	NFSPROC_APPENDWRITE	69
 
+/* Do a NFSv4 Openattr. */
+#define	NFSPROC_OPENATTR	70
+
 /*
  * Must be defined as one higher than the last NFSv4.2 Proc# above.
  */
-#define	NFSV42_NPROCS		70
+#define	NFSV42_NPROCS		71
 
 /* Value of NFSV42_NPROCS for old nfsstats structure. (Always 69) */
 #define	NFSV42_OLDNPROCS	69