All of lore.kernel.org
 help / color / mirror / Atom feed
From: Anna Schumaker <anna@kernel.org>
To: linux-nfs@vger.kernel.org, trond.myklebust@hammerspace.com
Cc: anna@kernel.org
Subject: [PATCH v2 3/4] NFS: Add support for CB_NOTIFY4_ADD_ENTRY
Date: Tue, 25 Aug 2026 11:02:08 -0400	[thread overview]
Message-ID: <20260825150209.694646-4-anna@kernel.org> (raw)
In-Reply-To: <20260825150209.694646-1-anna@kernel.org>

From: Anna Schumaker <anna.schumaker@oracle.com>

When the server tells us that a directory entry has been created, we
need to instantiate a new dentry and add it to the dcache on the client.

Signed-off-by: Anna Schumaker <anna.schumaker@oracle.com>
Signed-off-by: Anna Schumaker <anna.schumaker@hammerspace.com>

---
v2:
 * Handle the na_have_old_entry case
 * Initialize fattr->valid to 0 before decoding
 * Properly check the return code from decode_fattr_cb()
 * Rewrite nfs4_cb_notify_add() to closer resemble nfs_prime_dcache()
---
 fs/nfs/callback.h      | 17 +++++++++++
 fs/nfs/callback_proc.c | 61 +++++++++++++++++++++++++++++++++++++
 fs/nfs/callback_xdr.c  | 69 ++++++++++++++++++++++++++++++++++--------
 fs/nfs/nfs4_fs.h       |  2 ++
 fs/nfs/nfs4xdr.c       | 29 ++++++++++++++++--
 include/linux/nfs4.h   |  1 +
 6 files changed, 164 insertions(+), 15 deletions(-)

diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h
index 3740c999bb82..d3cc3e5df776 100644
--- a/fs/nfs/callback.h
+++ b/fs/nfs/callback.h
@@ -147,6 +147,12 @@ struct cb_notify_entry {
 	u32			ne_namelen;
 	const char		*ne_name;
 	struct nfs_fattr	ne_attrs;
+	struct nfs_fh		ne_fh;
+};
+
+struct cb_notify_prev_entry {
+	struct cb_notify_entry	pe_prev_entry;
+	u64			pe_prev_entry_cookie;
 };
 
 struct cb_notify_remove {
@@ -154,10 +160,21 @@ struct cb_notify_remove {
 	u64			nrm_old_entry_cookie;
 };
 
+struct cb_notify_add {
+	bool			na_have_old_entry;
+	struct cb_notify_remove	na_old_entry;
+	struct cb_notify_entry	na_new_entry;
+	bool			na_have_new_entry_cookie;
+	u64			na_new_entry_cookie;
+	bool			na_have_prev_entry;
+	bool			na_last_entry;
+};
+
 struct cb_notify_changes {
 	u32	notify_mask;
 	union {
 		struct cb_notify_remove notify_remove;
+		struct cb_notify_add	notify_add;
 	};
 };
 
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index d8ad3cd792df..9ac786f258c2 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -390,6 +390,63 @@ static __be32 nfs4_cb_notify_remove(struct cb_process_state *cps,
 	return 0;
 }
 
+static __be32 nfs4_cb_notify_add(struct cb_process_state *cps,
+				 struct dentry *parent,
+				 struct cb_notify_add *cb_add)
+{
+	struct nfs_entry entry = {
+		.cookie = cb_add->na_new_entry_cookie,
+		.name = cb_add->na_new_entry.ne_name,
+		.len = cb_add->na_new_entry.ne_namelen,
+		.eof = cb_add->na_last_entry,
+		.fh = &cb_add->na_new_entry.ne_fh,
+		.fattr = &cb_add->na_new_entry.ne_attrs,
+		.server = NFS_SERVER(d_inode(parent)),
+	};
+	struct qstr filename = QSTR_INIT(entry.name, entry.len);
+	struct dentry *dentry, *alias;
+	struct inode *inode;
+
+	if (cb_add->na_have_old_entry)
+		nfs4_cb_notify_remove(cps, parent, &cb_add->na_old_entry);
+
+	filename.hash = full_name_hash(parent, filename.name, filename.len);
+	dentry = d_lookup(parent, &filename);
+	if (!dentry) {
+		dentry = d_alloc_parallel(parent, &filename);
+		if (IS_ERR(dentry))
+			goto out;
+	}
+
+	if (!d_in_lookup(dentry)) {
+		nfs_set_verifier(dentry, parent->d_time);
+		if (!nfs_refresh_inode(d_inode(dentry), entry.fattr))
+			nfs_setsecurity(d_inode(dentry), entry.fattr);
+		goto out;
+	}
+
+	if (!entry.fh->size) {
+		d_lookup_done(dentry);
+		goto out;
+	}
+
+	nfs_set_verifier(dentry, parent->d_time);
+	inode = nfs_fhget(parent->d_sb, entry.fh, entry.fattr);
+	alias = d_splice_alias(inode, dentry);
+	d_lookup_done(dentry);
+
+	if (IS_ERR_OR_NULL(alias))
+		goto out;
+	nfs_set_verifier(alias, parent->d_time);
+	dput(dentry);
+	dentry = alias;
+
+out:
+	nfs_set_cache_invalid(parent->d_inode, NFS_INO_INVALID_DATA);
+	dput(dentry);
+	return 0;
+}
+
 __be32 nfs4_callback_notify(void *argp, void *resp,
 			    struct cb_process_state *cps)
 {
@@ -421,6 +478,10 @@ __be32 nfs4_callback_notify(void *argp, void *resp,
 			res = nfs4_cb_notify_remove(cps, parent,
 						    &change->notify_remove);
 			break;
+		case CB_NOTIFY4_ADD_ENTRY:
+			res = nfs4_cb_notify_add(cps, parent,
+						 &change->notify_add);
+			break;
 		default:
 			res = htonl(NFS4ERR_NOTSUPP);
 			goto out_dput;
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c
index 612d215ac66e..39a879e82312 100644
--- a/fs/nfs/callback_xdr.c
+++ b/fs/nfs/callback_xdr.c
@@ -257,26 +257,20 @@ static __be32 decode_notify_entry(struct xdr_stream *xdr,
 {
 	uint32_t bitmap[3] = {0};
 	__be32 status;
-	u32 attrlen;
-	__be32 *p;
 
 	status = decode_string(xdr, &args->ne_namelen, &args->ne_name,
 				NFS4_OPAQUE_LIMIT);
 	if (unlikely(status != 0))
 		return status;
 
-	status = decode_bitmap(xdr, bitmap);
+	status = xdr_stream_decode_uint32_array(xdr, bitmap, 3);
+	if (unlikely(status == 0 || status > 3))
+		return htonl(NFS4ERR_BADXDR);
+
+	status = decode_fattr_cb(xdr, bitmap, &args->ne_attrs, &args->ne_fh);
 	if (unlikely(status != 0))
-		return status;
-
-	p = xdr_inline_decode(xdr, 4);
-	if (unlikely(!p))
 		return htonl(NFS4ERR_BADXDR);
-
-	attrlen = be32_to_cpup(p);
-	if (attrlen != 0)
-		return htonl(NFS4ERR_BADXDR);
-	return 0;
+	return status;
 }
 
 static __be32 decode_notify_remove(struct xdr_stream *xdr,
@@ -296,6 +290,54 @@ static __be32 decode_notify_remove(struct xdr_stream *xdr,
 	return 0;
 }
 
+static __be32 decode_notify_add(struct xdr_stream *xdr,
+				struct cb_notify_add *args)
+{
+	__be32 status;
+	__be32 *p;
+
+	p = xdr_inline_decode(xdr, 4);
+	if (unlikely(!p))
+		return htonl(NFS4ERR_BADXDR);
+	args->na_have_old_entry = ntohl(*p);
+
+	if (args->na_have_old_entry) {
+		status = decode_notify_remove(xdr, &args->na_old_entry);
+		if (unlikely(status != 0))
+			return status;
+	}
+
+	status = decode_notify_entry(xdr, &args->na_new_entry);
+	if (unlikely(status != 0))
+		return status;
+
+	p = xdr_inline_decode(xdr, 4);
+	if (unlikely(!p))
+		return htonl(NFS4ERR_BADXDR);
+	args->na_have_new_entry_cookie = ntohl(*p);
+
+	if (args->na_have_new_entry_cookie) {
+		p = xdr_inline_decode(xdr, 8);
+		if (unlikely(!p))
+			return htonl(NFS4ERR_BADXDR);
+
+		xdr_decode_hyper(p, &args->na_new_entry_cookie);
+	}
+
+	p = xdr_inline_decode(xdr, 4);
+	if (unlikely(!p))
+		return htonl(NFS4ERR_BADXDR);
+	args->na_have_prev_entry = ntohl(*p);
+
+	WARN_ONCE(args->na_have_prev_entry, "NFS: Have prev entry unimplemented\n");
+
+	p = xdr_inline_decode(xdr, 4);
+	if (unlikely(!p))
+		return htonl(NFS4ERR_BADXDR);
+	args->na_last_entry = ntohl(*p);
+	return 0;
+}
+
 static
 __be32 decode_notify_args(struct svc_rqst *rqstp,
 			  struct xdr_stream *xdr,
@@ -345,6 +387,9 @@ __be32 decode_notify_args(struct svc_rqst *rqstp,
 			status = decode_notify_remove(xdr,
 						      &change->notify_remove);
 			break;
+		case CB_NOTIFY4_ADD_ENTRY:
+			status = decode_notify_add(xdr, &change->notify_add);
+			break;
 		default:
 			goto err;
 		}
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index b48e5b87cb2a..b029c9cefc38 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -586,6 +586,8 @@ extern const u32 nfs42_maxlistxattrs_overhead;
 #endif
 
 struct nfs4_mount_data;
+int decode_fattr_cb(struct xdr_stream *xdr, uint32_t *bitmap,
+		    struct nfs_fattr *fattr, struct nfs_fh *fhandle);
 
 /* callback_xdr.c */
 extern const struct svc_version nfs4_callback_version1;
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index e275fbfd395e..9a7c4d23aea7 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -2013,8 +2013,11 @@ encode_get_dir_delegation(struct xdr_stream *xdr, struct compound_hdr *hdr)
 {
 	struct timespec64 ts = { 0, 0 };
 	u32 notifications[1] = { CB_NOTIFY4_REMOVE_ENTRY |
+				 CB_NOTIFY4_ADD_ENTRY |
 				 CB_NOTIFY4_GFLAG_EXTEND };
-	u32 attributes[1] = { 0 };
+	u32 child_attrs[1] = { FATTR4_WORD0_FSID | FATTR4_WORD0_FILEID |
+			       FATTR4_WORD0_TYPE | FATTR4_WORD0_FILEHANDLE };
+	u32 dir_attrs[1] = { 0 };
 	__be32 *p;
 
 	encode_op_hdr(xdr, OP_GET_DIR_DELEGATION, decode_get_dir_deleg_maxsz, hdr);
@@ -2030,10 +2033,10 @@ encode_get_dir_delegation(struct xdr_stream *xdr, struct compound_hdr *hdr)
 	xdr_encode_nfstime4(p, &ts);
 
 	/* Requested child attributes */
-	xdr_encode_bitmap4(xdr, attributes, ARRAY_SIZE(attributes));
+	xdr_encode_bitmap4(xdr, child_attrs, ARRAY_SIZE(child_attrs));
 
 	/* Requested dir attributes */
-	xdr_encode_bitmap4(xdr, attributes, ARRAY_SIZE(attributes));
+	xdr_encode_bitmap4(xdr, dir_attrs, ARRAY_SIZE(dir_attrs));
 }
 
 static void
@@ -4919,6 +4922,26 @@ static int decode_getfattr(struct xdr_stream *xdr, struct nfs_fattr *fattr,
 	return decode_getfattr_generic(xdr, fattr, NULL, NULL, server);
 }
 
+int decode_fattr_cb(struct xdr_stream *xdr, uint32_t *bitmap,
+			     struct nfs_fattr *fattr, struct nfs_fh *fhandle)
+{
+	unsigned int savep;
+	uint32_t attrlen;
+	int status;
+
+	fattr->valid = 0;
+
+	status = decode_attr_length(xdr, &attrlen, &savep);
+	if (status < 0)
+		return status;
+
+	status = decode_getfattr_attrs(xdr, bitmap, fattr, fhandle, NULL, NULL);
+	if (status < 0)
+		return status;
+
+	return verify_attr_len(xdr, savep, attrlen);
+}
+
 /*
  * Decode potentially multiple layout types.
  */
diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 8de3ea283767..4b507a93aa38 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -493,6 +493,7 @@ enum {
 
 /* Directory Delegation / CB_NOTIFY bits */
 #define CB_NOTIFY4_REMOVE_ENTRY		(1UL << 2)
+#define CB_NOTIFY4_ADD_ENTRY		(1UL << 3)
 #define CB_NOTIFY4_GFLAG_EXTEND		(1UL << 6)
 
 #define NFSPROC4_NULL 0
-- 
2.55.0


  parent reply	other threads:[~2026-08-25 15:02 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-25 15:02 [PATCH v2 0/4] NFS: Add directory delegation support for CB_NOTIFY Anna Schumaker
2026-08-25 15:02 ` [PATCH v2 1/4] NFS: Add an outline for the CB_NOTIFY operation Anna Schumaker
2026-08-25 15:02 ` [PATCH v2 2/4] NFS: Add support for CB_NOTIFY4_REMOVE_ENTRY Anna Schumaker
2026-08-25 16:11   ` Jeff Layton
2026-08-25 15:02 ` Anna Schumaker [this message]
2026-08-25 15:02 ` [PATCH v2 4/4] NFS: Add support for CB_NOTIFY4_RENAME_ENTRY Anna Schumaker

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260825150209.694646-4-anna@kernel.org \
    --to=anna@kernel.org \
    --cc=linux-nfs@vger.kernel.org \
    --cc=trond.myklebust@hammerspace.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.