git: b1af05406b51 - main - nfs_nfsdserv.c: Fix setting of birthtime for some ZFS pools

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Thu, 18 Jun 2026 15:47:04 UTC
The branch main has been updated by rmacklem:

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

commit b1af05406b5117d76f567056fba0a023a6374465
Author:     Rick Macklem <rmacklem@FreeBSD.org>
AuthorDate: 2026-06-18 15:45:27 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2026-06-18 15:45:27 +0000

    nfs_nfsdserv.c: Fix setting of birthtime for some ZFS pools
    
    Some ZFS pools do not support va_birthtime and will return
    EINVAL when a VOP_SETATTR() of it is attempted.  The MacOS
    NFSv4 client sets va_birthtime (TimeCreate) in the same
    Setattr with ctime/mtime and other attributes after a new
    file is created.  The EINVAL failure leaves these new files
    messed up (mode == 0).
    
    This patch pretends the setting of TimeCreate succeeded if
    ctime/mtime were also set in the same Setattr RPC, which
    resolves the problem for the MacOS client.
    
    If this fix is not sufficient, a new pathconf name to detect
    if a file system supports birthtime may be needed.
    
    PR:     296066
    Tested by:      Will <freebsd.geography231@slmails.com>
    MFC after:      2 weeks
---
 sys/fs/nfsserver/nfs_nfsdserv.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/sys/fs/nfsserver/nfs_nfsdserv.c b/sys/fs/nfsserver/nfs_nfsdserv.c
index 9e5235f95ed1..fe7f0e217a74 100644
--- a/sys/fs/nfsserver/nfs_nfsdserv.c
+++ b/sys/fs/nfsserver/nfs_nfsdserv.c
@@ -579,6 +579,16 @@ nfsrvd_setattr(struct nfsrv_descript *nd, __unused int isdgram,
 		NFSVNO_SETATTRVAL(&nva2, btime, nva.na_btime);
 		nd->nd_repstat = nfsvno_setattr(vp, &nva2, nd->nd_cred, p,
 		    exp);
+		/*
+		 * ZFS stores with early versions do not support va_birthtime
+		 * and will reply EINVAL when setting is attempted.  This
+		 * breaks the MacOS NFSv4 client, so pretend it succeeded if
+		 * ctime and/or mtime were set as well.
+		 */
+		if (nd->nd_repstat == EINVAL &&
+		    (NFSISSET_ATTRBIT(&retbits, NFSATTRBIT_TIMEACCESSSET) ||
+		     NFSISSET_ATTRBIT(&retbits, NFSATTRBIT_TIMEMODIFYSET)))
+			nd->nd_repstat = 0;
 		if (!nd->nd_repstat)
 		    NFSSETBIT_ATTRBIT(&retbits, NFSATTRBIT_TIMECREATE);
 	    }