]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
nfsd: fix handling of delegated change attr in CB_GETATTR
authorJeff Layton <jlayton@kernel.org>
Mon, 9 Dec 2024 21:13:53 +0000 (16:13 -0500)
committerChuck Lever <chuck.lever@oracle.com>
Tue, 21 Jan 2025 20:29:40 +0000 (15:29 -0500)
RFC8881, section 10.4.3 has some specific guidance as to how the
delegated change attribute should be handled. We currently don't follow
that guidance properly.

In particular, when the file is modified, the server always reports the
initial change attribute + 1. Section 10.4.3 however indicates that it
should be incremented on every GETATTR request from other clients.

Only request the change attribute until the file has been modified. If
there is an outstanding delegation, then increment the cached change
attribute on every GETATTR.

Fixes: 6487a13b5c6b ("NFSD: add support for CB_GETATTR callback")
Signed-off-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
fs/nfsd/nfs4callback.c
fs/nfsd/nfs4xdr.c

index a8e33f561bb344d4bd64697e4c75e95a55e0e092..1457f61ae05107cc3686dd295727be4eb3a61082 100644 (file)
@@ -361,12 +361,14 @@ static void
 encode_cb_getattr4args(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr,
                        struct nfs4_cb_fattr *fattr)
 {
-       struct nfs4_delegation *dp =
-               container_of(fattr, struct nfs4_delegation, dl_cb_fattr);
+       struct nfs4_delegation *dp = container_of(fattr, struct nfs4_delegation, dl_cb_fattr);
        struct knfsd_fh *fh = &dp->dl_stid.sc_file->fi_fhandle;
+       struct nfs4_cb_fattr *ncf = &dp->dl_cb_fattr;
        u32 bmap[1];
 
-       bmap[0] = FATTR4_WORD0_CHANGE | FATTR4_WORD0_SIZE;
+       bmap[0] = FATTR4_WORD0_SIZE;
+       if (!ncf->ncf_file_modified)
+               bmap[0] |= FATTR4_WORD0_CHANGE;
 
        encode_nfs_cb_opnum4(xdr, OP_CB_GETATTR);
        encode_nfs_fh4(xdr, fh);
index 8d373d2edff147b89a6fbf0186c5b3d444492d21..31270e1a40eda779d9a54a939fe47e6d0511f533 100644 (file)
@@ -2920,6 +2920,7 @@ struct nfsd4_fattr_args {
        struct kstat            stat;
        struct kstatfs          statfs;
        struct nfs4_acl         *acl;
+       u64                     change_attr;
 #ifdef CONFIG_NFSD_V4_SECURITY_LABEL
        void                    *context;
        int                     contextlen;
@@ -3019,7 +3020,6 @@ static __be32 nfsd4_encode_fattr4_change(struct xdr_stream *xdr,
                                         const struct nfsd4_fattr_args *args)
 {
        const struct svc_export *exp = args->exp;
-       u64 c;
 
        if (unlikely(exp->ex_flags & NFSEXP_V4ROOT)) {
                u32 flush_time = convert_to_wallclock(exp->cd->flush_time);
@@ -3030,9 +3030,7 @@ static __be32 nfsd4_encode_fattr4_change(struct xdr_stream *xdr,
                        return nfserr_resource;
                return nfs_ok;
        }
-
-       c = nfsd4_change_attribute(&args->stat);
-       return nfsd4_encode_changeid4(xdr, c);
+       return nfsd4_encode_changeid4(xdr, args->change_attr);
 }
 
 static __be32 nfsd4_encode_fattr4_size(struct xdr_stream *xdr,
@@ -3557,11 +3555,16 @@ nfsd4_encode_fattr4(struct svc_rqst *rqstp, struct xdr_stream *xdr,
        if (dp) {
                struct nfs4_cb_fattr *ncf = &dp->dl_cb_fattr;
 
-               if (ncf->ncf_file_modified)
+               if (ncf->ncf_file_modified) {
+                       ++ncf->ncf_initial_cinfo;
                        args.stat.size = ncf->ncf_cur_fsize;
-
+               }
+               args.change_attr = ncf->ncf_initial_cinfo;
                nfs4_put_stid(&dp->dl_stid);
+       } else {
+               args.change_attr = nfsd4_change_attribute(&args.stat);
        }
+
        if (err)
                goto out_nfserr;