[PATCH 2/4] NFS: Add support for CB_NOTIFY4_REMOVE_ENTRY
Anna Schumaker <[email protected]> Tue, 4 Aug 2026 16:59:53 -0400
| Newsgroups | gmane.linux.nfs |
|---|---|
| Message-ID | <[email protected]> |
From: Anna Schumaker <[email protected]> When the server tells us that a directory entry has been removed then we need to take that as an indication that our knowledge of the directory has changed and needs to be refreshed. Signed-off-by: Anna Schumaker <[email protected]> Signed-off-by: Anna Schumaker <anna.schumaker-F/[email protected]> --- fs/nfs/callback.h | 16 +++++++++- fs/nfs/callback_proc.c | 68 +++++++++++++++++++++++++++++++++++++++++- fs/nfs/callback_xdr.c | 59 ++++++++++++++++++++++++++++++++++++ fs/nfs/nfs4xdr.c | 3 +- include/linux/nfs4.h | 1 + 5 files changed, 144 insertions(+), 3 deletions(-) diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h index f7cc5b6931bf..3740c999bb82 100644 --- a/fs/nfs/callback.h +++ b/fs/nfs/callback.h @@ -143,8 +143,22 @@ struct cb_layoutrecallargs { extern __be32 nfs4_callback_layoutrecall(void *argp, void *resp, struct cb_process_state *cps); +struct cb_notify_entry { + u32 ne_namelen; + const char *ne_name; + struct nfs_fattr ne_attrs; +}; + +struct cb_notify_remove { + struct cb_notify_entry nrm_old_entry; + u64 nrm_old_entry_cookie; +}; + struct cb_notify_changes { - u32 notify_mask; + u32 notify_mask; + union { + struct cb_notify_remove notify_remove; + }; }; struct cb_notifyargs { diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c index 5c414b3b01d8..356fbd8428ec 100644 --- a/fs/nfs/callback_proc.c +++ b/fs/nfs/callback_proc.c @@ -9,6 +9,7 @@ #include <linux/errno.h> #include <linux/math.h> +#include <linux/namei.h> #include <linux/nfs4.h> #include <linux/nfs_fs.h> #include <linux/slab.h> @@ -368,13 +369,78 @@ static void pnfs_recall_all_layouts(struct nfs_client *clp, do_callback_layoutrecall(clp, &args, cps); } +static struct dentry *nfs4_cb_notify_lookup(struct dentry *parent, + struct cb_notify_entry *entry) +{ + struct qstr filename = QSTR_INIT(entry->ne_name, entry->ne_namelen); + return try_lookup_noperm(&filename, parent); +} + +static __be32 nfs4_cb_notify_remove(struct cb_process_state *cps, + struct dentry *parent, + struct cb_notify_remove *cb_remove) +{ + struct dentry *child; + + child = nfs4_cb_notify_lookup(parent, &cb_remove->nrm_old_entry); + if (IS_ERR_OR_NULL(child)) + return htonl(NFS4ERR_BADHANDLE); + + nfs_set_cache_invalid(parent->d_inode, NFS_INO_INVALID_DATA); + d_drop(child); + dput(child); + return 0; +} + __be32 nfs4_callback_notify(void *argp, void *resp, struct cb_process_state *cps) { struct cb_notifyargs *args = argp; + struct dentry *parent; + struct inode *inode; + unsigned int i; + __be32 res; + if (!cps->clp) { + res = htonl(NFS4ERR_OP_NOT_IN_SESSION); + goto out; + } + + inode = nfs_delegation_find_inode(cps->clp, &args->cna_fh); + if (IS_ERR(inode)) { + res = htonl(NFS4ERR_BADHANDLE); + goto out; + } + parent = d_find_alias(inode); + if (!parent) { + res = 0; + goto out_iput; + } + + for (i = 0; i < args->cna_n_changes; i++) { + struct cb_notify_changes *change = &args->cna_changes[i]; + + switch (change->notify_mask) { + case CB_NOTIFY4_REMOVE_ENTRY: + res = nfs4_cb_notify_remove(cps, parent, + &change->notify_remove); + break; + default: + res = htonl(NFS4ERR_NOTSUPP); + goto out_dput; + } + + if (res < 0) + break; + } + +out_dput: + dput(parent); +out_iput: + nfs_iput_and_deactive(inode); +out: kfree(args->cna_changes); - return 0; + return res; } __be32 nfs4_callback_devicenotify(void *argp, void *resp, diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index b96d0028fa3d..fd54f31e89ac 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -252,6 +252,50 @@ static __be32 decode_layoutrecall_args(struct svc_rqst *rqstp, return 0; } +static __be32 decode_notify_entry(struct xdr_stream *xdr, + struct cb_notify_entry *args) +{ + 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); + 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; +} + +static __be32 decode_notify_remove(struct xdr_stream *xdr, + struct cb_notify_remove *args) +{ + __be32 status; + __be32 *p; + + status = decode_notify_entry(xdr, &args->nrm_old_entry); + if (unlikely(status != 0)) + return status; + + p = xdr_inline_decode(xdr, 8); + if (unlikely(!p)) + return htonl(NFS4ERR_BADXDR); + xdr_decode_hyper(p, &args->nrm_old_entry_cookie); + return 0; +} + static __be32 decode_notify_args(struct svc_rqst *rqstp, struct xdr_stream *xdr, @@ -289,6 +333,21 @@ __be32 decode_notify_args(struct svc_rqst *rqstp, &change->notify_mask, 1); if (unlikely(res < 0)) goto err; + + /* Decode opaque size */ + p = xdr_inline_decode(xdr, 4); + if (unlikely(!p)) + goto err; + res = ntohl(*p); + + switch (change->notify_mask) { + case CB_NOTIFY4_REMOVE_ENTRY: + status = decode_notify_remove(xdr, + &change->notify_remove); + break; + default: + goto err; + } } return 0; diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c index 64b8fd061852..65f9e995fa62 100644 --- a/fs/nfs/nfs4xdr.c +++ b/fs/nfs/nfs4xdr.c @@ -2011,7 +2011,8 @@ static void encode_get_dir_delegation(struct xdr_stream *xdr, struct compound_hdr *hdr) { struct timespec64 ts = { 0, 0 }; - u32 notifications[1] = { CB_NOTIFY4_GFLAG_EXTEND }; + u32 notifications[1] = { CB_NOTIFY4_REMOVE_ENTRY | + CB_NOTIFY4_GFLAG_EXTEND }; u32 attributes[1] = { 0 }; __be32 *p; diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h index fcf0eee55c35..0f84be8e2fd8 100644 --- a/include/linux/nfs4.h +++ b/include/linux/nfs4.h @@ -483,6 +483,7 @@ enum { #define THRESHOLD_WR_IO (1UL << 3) /* Directory Delegation / CB_NOTIFY bits */ +#define CB_NOTIFY4_REMOVE_ENTRY (1UL << 2) #define CB_NOTIFY4_GFLAG_EXTEND (1UL << 6) #define NFSPROC4_NULL 0 -- 2.55.0