diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 1b0761e6a65c5d..04ed40902ae526 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -329,8 +329,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags) fuse_change_attributes(inode, &ext_out.entry.attr, &sx, ATTR_TIMEOUT(&ext_out.entry), attr_version); - if ((ext_out.mask & STATX_BASIC_STATS) == STATX_BASIC_STATS) - fuse_change_entry_timeout(entry, &ext_out.entry); + fuse_change_entry_timeout(entry, &ext_out.entry); } else if (inode) { fi = get_fuse_inode(inode); if (flags & LOOKUP_RCU) { @@ -1640,7 +1639,7 @@ static int fuse_permission(struct mnt_idmap *idmap, struct fuse_inode *fi = get_fuse_inode(inode); if (perm_mask & READ_ONCE(fi->inval_mask) || - time_before64(fi->i_time, get_jiffies_64())) { + time_before64(fi->i_perm_time, get_jiffies_64())) { refreshed = true; err = fuse_perm_getattr(inode, mask, perm_mask); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index d78d752055f196..233ee25f43ab23 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -114,6 +114,15 @@ struct fuse_inode { /** Time in jiffies until the file attributes are valid */ u64 i_time; + /* + * Time in jiffies until mode/uid/gid (the permission-check subset of + * STATX_BASIC_STATS) are valid. Tracked separately from i_time so that + * a partial statx refresh covering only the perm bits can extend the + * permission-check cache without falsely advancing i_time for the + * other (un-refreshed) attributes. + */ + u64 i_perm_time; + /* Which attributes are invalid */ u32 inval_mask; diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c index f95f280f8f6f09..4904a9131f2c25 100644 --- a/fs/fuse/inode.c +++ b/fs/fuse/inode.c @@ -265,6 +265,17 @@ static void fuse_change_attributes_common_sx(struct inode *inode, fi->i_time = attr_valid; } + /* + * Permission-check cache: independent of i_time so that a partial + * refresh which covers only mode/uid/gid (e.g. fuse_perm_getattr()) + * still extends the window during which fuse_permission() can hit + * the cache. Requires all three perm bits because generic_permission() + * needs the full triple. + */ + if ((returned_attrs & (STATX_MODE | STATX_UID | STATX_GID)) == + (STATX_MODE | STATX_UID | STATX_GID)) + fi->i_perm_time = attr_valid; + /* * Only update inode fields for attributes that were actually returned. * TYPE is part of i_mode but already set during inode creation. @@ -379,6 +390,7 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, fi->attr_version = atomic64_inc_return(&fc->attr_version); wake_up_all(&fc->attr_version_waitq); fi->i_time = attr_valid; + fi->i_perm_time = attr_valid; inode->i_ino = fuse_squash_ino(attr->ino); inode->i_mode = (inode->i_mode & S_IFMT) | (attr->mode & 07777);