diff mbox series

[SRU,Bionic,2/4] (upstream) NFS: Clear the file access cache upon login

Message ID 20230121144713.39111-3-chengen.du@canonical.com
State New
Headers show
Series NFS: client permission error after adding user to permissible group | expand

Commit Message

Chengen Du Jan. 21, 2023, 2:47 p.m. UTC
From: Trond Myklebust <trond.myklebust@hammerspace.com>

BugLink: https://bugs.launchpad.net/bugs/2003053

POSIX typically only refreshes the user's supplementary group
information upon login. Since NFS servers may often refresh their
concept of the user supplementary group membership at their own cadence,
it is possible for the NFS client's access cache to become stale due to
the user's group membership changing on the server after the user has
already logged in on the client.
While it is reasonable to expect that such group membership changes are
rare, and that we do not want to optimise the cache to accommodate them,
it is also not unreasonable for the user to expect that if they log out
and log back in again, that the staleness would clear up.

Reviewed-by: Benjamin Coddington <bcodding@redhat.com>
Tested-by: Benjamin Coddington <bcodding@redhat.com>
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
(backported from commit 0eb43812c0270ee3d005ff32f91f7d0a6c4943af)
[chengen - adjust context and use current_cred() as the
argument to get login time]
Signed-off-by: Chengen Du <chengen.du@canonical.com>
---
 fs/nfs/dir.c           | 24 ++++++++++++++++++++++++
 include/linux/nfs_fs.h |  1 +
 2 files changed, 25 insertions(+)
diff mbox series

Patch

diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c
index 7d9cf9f92b56..3ff938164381 100644
--- a/fs/nfs/dir.c
+++ b/fs/nfs/dir.c
@@ -2294,9 +2294,29 @@  static struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, st
 	return NULL;
 }
 
+static u64 nfs_access_login_time(const struct task_struct *task,
+				 const struct cred *cred)
+{
+	const struct task_struct *parent;
+	u64 ret;
+
+	rcu_read_lock();
+	for (;;) {
+		parent = rcu_dereference(task->real_parent);
+		if (parent == task || cred_fscmp(parent->cred, cred) != 0)
+			break;
+		task = parent;
+	}
+	ret = task->start_time;
+	rcu_read_unlock();
+	return ret;
+}
+
 static int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, struct nfs_access_entry *res, bool may_block)
 {
 	struct nfs_inode *nfsi = NFS_I(inode);
+	const struct cred *cur_cred = current_cred();
+	u64 login_time = nfs_access_login_time(current, cur_cred);
 	struct nfs_access_entry *cache;
 	bool retry = true;
 	int err;
@@ -2324,6 +2344,9 @@  static int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, str
 		spin_lock(&inode->i_lock);
 		retry = false;
 	}
+	err = -ENOENT;
+	if ((s64)(login_time - cache->timestamp) > 0)
+		goto out;
 	res->cred = cache->cred;
 	res->mask = cache->mask;
 	list_move_tail(&cache->lru, &nfsi->access_cache_entry_lru);
@@ -2387,6 +2410,7 @@  static void nfs_access_add_rbtree(struct inode *inode, struct nfs_access_entry *
 		else
 			goto found;
 	}
+	set->timestamp = ktime_get_ns();
 	rb_link_node(&set->rb_node, parent, p);
 	rb_insert_color(&set->rb_node, root_node);
 	list_add_tail(&set->lru, &nfsi->access_cache_entry_lru);
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index 78ca796cd50e..21eeee96fa5a 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -52,6 +52,7 @@  struct nfs_access_entry {
 	struct rb_node		rb_node;
 	struct list_head	lru;
 	struct rpc_cred *	cred;
+	u64                     timestamp;
 	__u32			mask;
 	struct rcu_head		rcu_head;
 };