NFS: Clear the file access cache upon login
authorTrond Myklebust <trond.myklebust@hammerspace.com>
Fri, 26 Aug 2022 23:44:44 +0000 (19:44 -0400)
committerTrond Myklebust <trond.myklebust@hammerspace.com>
Mon, 28 Nov 2022 03:09:59 +0000 (22:09 -0500)
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>
fs/nfs/dir.c
include/linux/nfs_fs.h

index f594dac436a7e9fafd7fd8624754dc4dcf0aca23..bccb04af38aee670fc50053ce028d516753dc4c2 100644 (file)
@@ -2948,9 +2948,28 @@ static struct nfs_access_entry *nfs_access_search_rbtree(struct inode *inode, co
        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_locked(struct inode *inode, const struct cred *cred, u32 *mask, bool may_block)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
+       u64 login_time = nfs_access_login_time(current, cred);
        struct nfs_access_entry *cache;
        bool retry = true;
        int err;
@@ -2978,6 +2997,9 @@ static int nfs_access_get_cached_locked(struct inode *inode, const struct cred *
                spin_lock(&inode->i_lock);
                retry = false;
        }
+       err = -ENOENT;
+       if ((s64)(login_time - cache->timestamp) > 0)
+               goto out;
        *mask = cache->mask;
        list_move_tail(&cache->lru, &nfsi->access_cache_entry_lru);
        err = 0;
@@ -3057,6 +3079,7 @@ static void nfs_access_add_rbtree(struct inode *inode,
                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);
index 7931fa47256129e26422531be56b65ec4fb39c50..d92fdfd2444c88e3c9d6b2d8893c8fc0d29237f3 100644 (file)
@@ -59,6 +59,7 @@ struct nfs_access_entry {
        kuid_t                  fsuid;
        kgid_t                  fsgid;
        struct group_info       *group_info;
+       u64                     timestamp;
        __u32                   mask;
        struct rcu_head         rcu_head;
 };