[PATCH 2/4] NFS: Add support for CB_NOTIFY4_REMOVE_ENTRY

Anna Schumaker <[email protected]>
Newsgroups org.kernel.vger.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 <[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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.