git: 5971781ee78b - stable/13 - nfsd: Make the pNFS server update Change for Setxattr/Rmxattr

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Wed, 16 Nov 2022 20:10:45 UTC
The branch stable/13 has been updated by rmacklem:

URL: https://cgit.FreeBSD.org/src/commit/?id=5971781ee78b0c5879c2696437b1f7629d140c30

commit 5971781ee78b0c5879c2696437b1f7629d140c30
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2022-10-18 22:47:07 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2022-11-16 20:09:33 +0000

    nfsd: Make the pNFS server update Change for Setxattr/Rmxattr
    
    When the NFS server does the Setxattr or Rmxattr operation,
    the Change attribute (va_filerev) needs to be updated.
    
    Without this patch, that was not happening for the
    pNFS server configuration.  This patch does a Setattr
    against the DS file to make the Change attribute
    change.
    
    This bug was discovered during a recent IETF NFSv4 testing
    event, where the Change attribute wasn't changed in the
    operation reply.
    
    (cherry picked from commit ae7816576e44412a8ae9daa92c210a6ba4f269dc)
---
 sys/fs/nfsserver/nfs_nfsdport.c | 49 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 47 insertions(+), 2 deletions(-)

diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c
index 258642bb1060..51c4d59e5380 100644
--- a/sys/fs/nfsserver/nfs_nfsdport.c
+++ b/sys/fs/nfsserver/nfs_nfsdport.c
@@ -101,6 +101,7 @@ struct callout nfsd_callout;
 
 static int nfssvc_srvcall(struct thread *, struct nfssvc_args *,
     struct ucred *);
+static void nfsvno_updateds(struct vnode *, struct ucred *, struct thread *);
 
 int nfsrv_enable_crossmntpt = 1;
 static int nfs_commit_blks;
@@ -6568,8 +6569,11 @@ nfsvno_setxattr(struct vnode *vp, char *name, int len, struct mbuf *m,
 	if (error == 0) {
 		error = VOP_SETEXTATTR(vp, EXTATTR_NAMESPACE_USER, name, uiop,
 		    cred, p);
-		if (error == 0)
+		if (error == 0) {
+			if (vp->v_type == VREG && nfsrv_devidcnt != 0)
+				nfsvno_updateds(vp, cred, p);
 			error = VOP_FSYNC(vp, MNT_WAIT, p);
+		}
 		free(iv, M_TEMP);
 	}
 
@@ -6578,6 +6582,44 @@ out:
 	return (error);
 }
 
+/*
+ * For a pNFS server, the DS file's ctime and
+ * va_filerev (TimeMetadata and Change) needs to
+ * be updated.  This is a hack, but works by
+ * flipping the S_ISGID bit in va_mode and then
+ * flipping it back.
+ * It does result in two MDS->DS RPCs, but creating
+ * a custom RPC just to do this seems overkill, since
+ * Setxattr/Rmxattr will not be done that frequently.
+ * If it fails part way through, that is not too
+ * serious, since the DS file is never executed.
+ */
+static void
+nfsvno_updateds(struct vnode *vp, struct ucred *cred, NFSPROC_T *p)
+{
+	struct nfsvattr nva;
+	int ret;
+	u_short tmode;
+
+	ret = VOP_GETATTR(vp, &nva.na_vattr, cred);
+	if (ret == 0) {
+		tmode = nva.na_mode;
+		NFSVNO_ATTRINIT(&nva);
+		tmode ^= S_ISGID;
+		NFSVNO_SETATTRVAL(&nva, mode, tmode);
+		ret = nfsrv_proxyds(vp, 0, 0, cred, p,
+		    NFSPROC_SETATTR, NULL, NULL, NULL, &nva,
+		    NULL, NULL, 0, NULL);
+		if (ret == 0) {
+			tmode ^= S_ISGID;
+			NFSVNO_SETATTRVAL(&nva, mode, tmode);
+			ret = nfsrv_proxyds(vp, 0, 0, cred, p,
+			    NFSPROC_SETATTR, NULL, NULL, NULL,
+			    &nva, NULL, NULL, 0, NULL);
+		}
+	}
+}
+
 /*
  * Remove Extended attribute vnode op.
  */
@@ -6605,8 +6647,11 @@ nfsvno_rmxattr(struct nfsrv_descript *nd, struct vnode *vp, char *name,
 	if (error == EOPNOTSUPP)
 		error = VOP_SETEXTATTR(vp, EXTATTR_NAMESPACE_USER, name, NULL,
 		    cred, p);
-	if (error == 0)
+	if (error == 0) {
+		if (vp->v_type == VREG && nfsrv_devidcnt != 0)
+			nfsvno_updateds(vp, cred, p);
 		error = VOP_FSYNC(vp, MNT_WAIT, p);
+	}
 out:
 	NFSEXITCODE(error);
 	return (error);