git: 2477e88b8d43 - main - nfs: Add support for the NFSv4.2 mode_umask attribute

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Wed, 21 Aug 2024 01:49:40 UTC
The branch main has been updated by rmacklem:

URL: https://cgit.FreeBSD.org/src/commit/?id=2477e88b8d4328535357bc62409f673a551be179

commit 2477e88b8d4328535357bc62409f673a551be179
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2024-08-21 01:48:19 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2024-08-21 01:48:19 +0000

    nfs: Add support for the NFSv4.2 mode_umask attribute
    
    RFC8275 defines a new attribute as an extension to NFSv4.2
    called MODE_UMASK.  This patch adds support for this attribute
    to the NFSv4.2 client and server.
    
    Since FreeBSD applies the umask above the VFS/VOP layer,
    this attribute does not actually have any effect on the
    handling of ACL inheritance, which is what it is designed for.
    However, future changes to NFSv4.2 require support of it,
    so this patch does that, resulting in behaviour identcal to
    the mode attribute already supported.
    
    MFC after:      2 months
---
 sys/fs/nfs/nfs_commonsubs.c     | 46 ++++++++++++++++++++++++++++-------------
 sys/fs/nfs/nfscl.h              |  9 ++++----
 sys/fs/nfs/nfsproto.h           | 12 +++++++----
 sys/fs/nfsclient/nfs_clrpcops.c | 18 +++++++++-------
 sys/fs/nfsserver/nfs_nfsdport.c | 17 +++++++++++++++
 5 files changed, 72 insertions(+), 30 deletions(-)

diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c
index 3c9af40253ad..f0469958a43f 100644
--- a/sys/fs/nfs/nfs_commonsubs.c
+++ b/sys/fs/nfs/nfs_commonsubs.c
@@ -610,8 +610,18 @@ nfscl_fillsattr(struct nfsrv_descript *nd, struct vattr *vap,
 		break;
 	case ND_NFSV4:
 		NFSZERO_ATTRBIT(&attrbits);
-		if (vap->va_mode != (mode_t)VNOVAL)
-			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_MODE);
+		np = NULL;
+		if (strcmp(vp->v_mount->mnt_vfc->vfc_name, "nfs") == 0)
+			np = VTONFS(vp);
+		if (vap->va_mode != (mode_t)VNOVAL) {
+			if ((flags & NFSSATTR_NEWFILE) != 0 && np != NULL &&
+			    NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr,
+			    NFSATTRBIT_MODEUMASK))
+				NFSSETBIT_ATTRBIT(&attrbits,
+				    NFSATTRBIT_MODEUMASK);
+			else
+				NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_MODE);
+		}
 		if ((flags & NFSSATTR_FULL) && vap->va_uid != (uid_t)VNOVAL)
 			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER);
 		if ((flags & NFSSATTR_FULL) && vap->va_gid != (gid_t)VNOVAL)
@@ -622,18 +632,14 @@ nfscl_fillsattr(struct nfsrv_descript *nd, struct vattr *vap,
 			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEACCESSSET);
 		if (vap->va_mtime.tv_sec != VNOVAL)
 			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMEMODIFYSET);
-		if (vap->va_birthtime.tv_sec != VNOVAL &&
-		    strcmp(vp->v_mount->mnt_vfc->vfc_name, "nfs") == 0) {
-			/*
-			 * We can only test for support of TimeCreate if
-			 * the "vp" argument is for an NFS vnode.
-			 */
-			np = VTONFS(vp);
-			if (NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr,
-			    NFSATTRBIT_TIMECREATE))
-				NFSSETBIT_ATTRBIT(&attrbits,
-				    NFSATTRBIT_TIMECREATE);
-		}
+		/*
+		 * We can only test for support of TimeCreate if
+		 * the "vp" argument is for an NFS vnode.
+		 */
+		if (vap->va_birthtime.tv_sec != VNOVAL && np != NULL &&
+		    NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr,
+		    NFSATTRBIT_TIMECREATE))
+			NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMECREATE);
 		(void) nfsv4_fillattr(nd, vp->v_mount, vp, NULL, vap, NULL, 0,
 		    &attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0, NULL);
 		break;
@@ -3109,6 +3115,18 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp,
 				*tl = newnfs_false;
 			retnum += NFSX_UNSIGNED;
 			break;
+		case NFSATTRBIT_MODEUMASK:
+			NFSM_BUILD(tl, uint32_t *, 2 * NFSX_UNSIGNED);
+			/*
+			 * Since FreeBSD applies the umask above the VFS/VOP,
+			 * there is no umask to handle here.  If FreeBSD
+			 * moves handling of umask to below the VFS/VOP,
+			 * this could change.
+			 */
+			*tl++ = vtonfsv34_mode(vap->va_mode);
+			*tl = 0;
+			retnum += 2 * NFSX_UNSIGNED;
+			break;
 		default:
 			printf("EEK! Bad V4 attribute bitpos=%d\n", bitpos);
 		}
diff --git a/sys/fs/nfs/nfscl.h b/sys/fs/nfs/nfscl.h
index a52b9e433145..3b1445e1923c 100644
--- a/sys/fs/nfs/nfscl.h
+++ b/sys/fs/nfs/nfscl.h
@@ -68,10 +68,11 @@ struct nfsv4node {
  * These flag bits are used for the argument to nfscl_fillsattr() to
  * indicate special handling of the attributes.
  */
-#define	NFSSATTR_FULL		0x1
-#define	NFSSATTR_SIZE0		0x2
-#define	NFSSATTR_SIZENEG1	0x4
-#define	NFSSATTR_SIZERDEV	0x8
+#define	NFSSATTR_FULL		0x01
+#define	NFSSATTR_SIZE0		0x02
+#define	NFSSATTR_SIZENEG1	0x04
+#define	NFSSATTR_SIZERDEV	0x08
+#define	NFSSATTR_NEWFILE	0x10
 
 /* Use this macro for debug printfs. */
 #define	NFSCL_DEBUG(level, ...)	do {					\
diff --git a/sys/fs/nfs/nfsproto.h b/sys/fs/nfs/nfsproto.h
index 0268940fd8a6..ce7acf102d41 100644
--- a/sys/fs/nfs/nfsproto.h
+++ b/sys/fs/nfs/nfsproto.h
@@ -1183,7 +1183,8 @@ struct nfsv3_sattr {
  */
 #define	NFSATTRBIT_SUPPSETONLY1	 (NFSATTRBM_TIMEACCESSSET |		\
 				 NFSATTRBM_TIMEMODIFYSET)
-#define	NFSATTRBIT_SUPPSETONLY2	(NFSATTRBM_MODESETMASKED)
+#define	NFSATTRBIT_SUPPSETONLY2	(NFSATTRBM_MODESETMASKED |		\
+				 NFSATTRBM_MODEUMASK)
 
 /*
  * NFSATTRBIT_SETABLE - SETABLE0 - bits 0<->31
@@ -1197,11 +1198,12 @@ struct nfsv3_sattr {
  	(NFSATTRBM_MODE |						\
  	NFSATTRBM_OWNER |						\
  	NFSATTRBM_OWNERGROUP |						\
- 	NFSATTRBM_TIMECREATE |					\
+ 	NFSATTRBM_TIMECREATE |						\
  	NFSATTRBM_TIMEACCESSSET |					\
  	NFSATTRBM_TIMEMODIFYSET)
 #define	NFSATTRBIT_SETABLE2						\
-	(NFSATTRBM_MODESETMASKED)
+	(NFSATTRBM_MODESETMASKED |					\
+	NFSATTRBM_MODEUMASK)
 
 /*
  * NFSATTRBIT_NFSV41 - Attributes only supported by NFSv4.1.
@@ -1218,7 +1220,9 @@ struct nfsv3_sattr {
 /*
  * NFSATTRBIT_NFSV42 - Attributes only supported by NFSv4.2.
  */
-#define	NFSATTRBIT_NFSV42_2	NFSATTRBM_XATTRSUPPORT
+#define	NFSATTRBIT_NFSV42_2						\
+	(NFSATTRBM_XATTRSUPPORT |					\
+	NFSATTRBM_MODEUMASK)
 
 /*
  * Set of attributes that the getattr vnode op needs.
diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c
index 8947b608b743..7540893ce63c 100644
--- a/sys/fs/nfsclient/nfs_clrpcops.c
+++ b/sys/fs/nfsclient/nfs_clrpcops.c
@@ -2423,7 +2423,7 @@ nfsrpc_mknod(vnode_t dvp, char *name, int namelen, struct vattr *vap,
 		*tl = vtonfsv34_type(vtyp);
 	}
 	if (nd->nd_flag & (ND_NFSV3 | ND_NFSV4))
-		nfscl_fillsattr(nd, vap, dvp, 0, 0);
+		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_NEWFILE, 0);
 	if ((nd->nd_flag & ND_NFSV3) &&
 	    (vtyp == VCHR || vtyp == VBLK)) {
 		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
@@ -2645,14 +2645,16 @@ nfsrpc_createv4(vnode_t dvp, char *name, int namelen, struct vattr *vap,
 			if (NFSHASSESSPERSIST(nmp)) {
 				/* Use GUARDED for persistent sessions. */
 				*tl = txdr_unsigned(NFSCREATE_GUARDED);
-				nfscl_fillsattr(nd, vap, dvp, 0, 0);
+				nfscl_fillsattr(nd, vap, dvp, NFSSATTR_NEWFILE,
+				    0);
 			} else {
 				/* Otherwise, use EXCLUSIVE4_1. */
 				*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE41);
 				NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
 				*tl++ = cverf.lval[0];
 				*tl = cverf.lval[1];
-				nfscl_fillsattr(nd, vap, dvp, 0, 0);
+				nfscl_fillsattr(nd, vap, dvp, NFSSATTR_NEWFILE,
+				    0);
 			}
 		} else {
 			/* NFSv4.0 */
@@ -2663,7 +2665,7 @@ nfsrpc_createv4(vnode_t dvp, char *name, int namelen, struct vattr *vap,
 		}
 	} else {
 		*tl = txdr_unsigned(NFSCREATE_UNCHECKED);
-		nfscl_fillsattr(nd, vap, dvp, 0, 0);
+		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_NEWFILE, 0);
 	}
 	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
 	*tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL);
@@ -3238,7 +3240,7 @@ nfsrpc_mkdir(vnode_t dvp, char *name, int namelen, struct vattr *vap,
 		*tl = txdr_unsigned(NFDIR);
 	}
 	(void) nfsm_strtom(nd, name, namelen);
-	nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZENEG1, 0);
+	nfscl_fillsattr(nd, vap, dvp, NFSSATTR_SIZENEG1 | NFSSATTR_NEWFILE, 0);
 	if (nd->nd_flag & ND_NFSV4) {
 		NFSGETATTR_ATTRBIT(&attrbits);
 		NFSM_BUILD(tl, u_int32_t *, 2 * NFSX_UNSIGNED);
@@ -8456,18 +8458,18 @@ nfsrpc_createlayout(vnode_t dvp, char *name, int namelen, struct vattr *vap,
 		if (NFSHASSESSPERSIST(nmp)) {
 			/* Use GUARDED for persistent sessions. */
 			*tl = txdr_unsigned(NFSCREATE_GUARDED);
-			nfscl_fillsattr(nd, vap, dvp, 0, 0);
+			nfscl_fillsattr(nd, vap, dvp, NFSSATTR_NEWFILE, 0);
 		} else {
 			/* Otherwise, use EXCLUSIVE4_1. */
 			*tl = txdr_unsigned(NFSCREATE_EXCLUSIVE41);
 			NFSM_BUILD(tl, u_int32_t *, NFSX_VERF);
 			*tl++ = cverf.lval[0];
 			*tl = cverf.lval[1];
-			nfscl_fillsattr(nd, vap, dvp, 0, 0);
+			nfscl_fillsattr(nd, vap, dvp, NFSSATTR_NEWFILE, 0);
 		}
 	} else {
 		*tl = txdr_unsigned(NFSCREATE_UNCHECKED);
-		nfscl_fillsattr(nd, vap, dvp, 0, 0);
+		nfscl_fillsattr(nd, vap, dvp, NFSSATTR_NEWFILE, 0);
 	}
 	NFSM_BUILD(tl, u_int32_t *, NFSX_UNSIGNED);
 	*tl = txdr_unsigned(NFSV4OPEN_CLAIMNULL);
diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c
index fa99a76f492e..12592d4c5c59 100644
--- a/sys/fs/nfsserver/nfs_nfsdport.c
+++ b/sys/fs/nfsserver/nfs_nfsdport.c
@@ -3212,6 +3212,23 @@ nfsv4_sattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsvattr *nvap,
 				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_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:
 			nd->nd_repstat = NFSERR_ATTRNOTSUPP;
 			/*