]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
fuse: cache btime
authorMiklos Szeredi <mszeredi@redhat.com>
Thu, 10 Aug 2023 10:45:05 +0000 (12:45 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Mon, 21 Aug 2023 10:14:59 +0000 (12:14 +0200)
Not all inode attributes are supported by all filesystems, but for the
basic stats (which are returned by stat(2) and friends) all of them will
have some value, even if that doesn't reflect a real attribute of the file.

Btime is different, in that filesystems are free to report or not report a
value in statx.  If the value is available, then STATX_BTIME bit is set in
stx_mask.

When caching the value of btime, remember the availability of the attribute
as well as the value (if available).  This is done by using the
FUSE_I_BTIME bit in fuse_inode->state to indicate availability, while using
fuse_inode->inval_mask & STATX_BTIME to indicate the state of the cache
itself (i.e. set if cache is invalid, and cleared if cache is valid).

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/fuse/dir.c
fs/fuse/fuse_i.h
fs/fuse/inode.c
fs/fuse/readdir.c

index e7f63e60118c8e85036733ca310813ff42859573..3db9d26d341e40a961db2fbf7e23f3686129baa2 100644 (file)
@@ -255,7 +255,7 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
                        goto invalid;
 
                forget_all_cached_acls(inode);
-               fuse_change_attributes(inode, &outarg.attr,
+               fuse_change_attributes(inode, &outarg.attr, NULL,
                                       ATTR_TIMEOUT(&outarg),
                                       attr_version);
                fuse_change_entry_timeout(entry, &outarg);
@@ -1213,8 +1213,8 @@ static int fuse_do_statx(struct inode *inode, struct file *file,
 
        fuse_statx_to_attr(&outarg.stat, &attr);
        if ((sx->mask & STATX_BASIC_STATS) == STATX_BASIC_STATS) {
-               fuse_change_attributes(inode, &attr, ATTR_TIMEOUT(&outarg),
-                                      attr_version);
+               fuse_change_attributes(inode, &attr, &outarg.stat,
+                                      ATTR_TIMEOUT(&outarg), attr_version);
        }
        stat->result_mask = sx->mask & (STATX_BASIC_STATS | STATX_BTIME);
        stat->btime.tv_sec = sx->btime.tv_sec;
@@ -1261,7 +1261,7 @@ static int fuse_do_getattr(struct inode *inode, struct kstat *stat,
                        fuse_make_bad(inode);
                        err = -EIO;
                } else {
-                       fuse_change_attributes(inode, &outarg.attr,
+                       fuse_change_attributes(inode, &outarg.attr, NULL,
                                               ATTR_TIMEOUT(&outarg),
                                               attr_version);
                        if (stat)
@@ -1316,6 +1316,10 @@ retry:
                generic_fillattr(&nop_mnt_idmap, inode, stat);
                stat->mode = fi->orig_i_mode;
                stat->ino = fi->orig_ino;
+               if (test_bit(FUSE_I_BTIME, &fi->state)) {
+                       stat->btime = fi->i_btime;
+                       stat->result_mask |= STATX_BTIME;
+               }
        }
 
        return err;
@@ -1952,7 +1956,7 @@ int fuse_do_setattr(struct dentry *dentry, struct iattr *attr,
                /* FIXME: clear I_DIRTY_SYNC? */
        }
 
-       fuse_change_attributes_common(inode, &outarg.attr,
+       fuse_change_attributes_common(inode, &outarg.attr, NULL,
                                      ATTR_TIMEOUT(&outarg),
                                      fuse_get_cache_mask(inode));
        oldsize = inode->i_size;
index 43c7b58d8ab745abab0fb76fd8d5a6173a230eee..bf0b85d0b95c7d21bebe30f88f0ac92b1088aeb3 100644 (file)
@@ -88,6 +88,9 @@ struct fuse_inode {
            preserve the original mode */
        umode_t orig_i_mode;
 
+       /* Cache birthtime */
+       struct timespec64 i_btime;
+
        /** 64 bit inode number */
        u64 orig_ino;
 
@@ -167,6 +170,8 @@ enum {
        FUSE_I_SIZE_UNSTABLE,
        /* Bad inode */
        FUSE_I_BAD,
+       /* Has btime */
+       FUSE_I_BTIME,
 };
 
 struct fuse_conn;
@@ -1064,9 +1069,11 @@ void fuse_init_symlink(struct inode *inode);
  * Change attributes of an inode
  */
 void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
+                           struct fuse_statx *sx,
                            u64 attr_valid, u64 attr_version);
 
 void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
+                                  struct fuse_statx *sx,
                                   u64 attr_valid, u32 cache_mask);
 
 u32 fuse_get_cache_mask(struct inode *inode);
index 2a4d192502e70bc58f9116d36b3f04d46e6ac59e..d47606206ec3facc1d483d3aaf5128e6cb67b744 100644 (file)
@@ -163,6 +163,7 @@ static ino_t fuse_squash_ino(u64 ino64)
 }
 
 void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
+                                  struct fuse_statx *sx,
                                   u64 attr_valid, u32 cache_mask)
 {
        struct fuse_conn *fc = get_fuse_conn(inode);
@@ -198,6 +199,25 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr,
                inode->i_ctime.tv_sec   = attr->ctime;
                inode->i_ctime.tv_nsec  = attr->ctimensec;
        }
+       if (sx) {
+               /* Sanitize nsecs */
+               sx->btime.tv_nsec =
+                       min_t(u32, sx->btime.tv_nsec, NSEC_PER_SEC - 1);
+
+               /*
+                * Btime has been queried, cache is valid (whether or not btime
+                * is available or not) so clear STATX_BTIME from inval_mask.
+                *
+                * Availability of the btime attribute is indicated in
+                * FUSE_I_BTIME
+                */
+               set_mask_bits(&fi->inval_mask, STATX_BTIME, 0);
+               if (sx->mask & STATX_BTIME) {
+                       set_bit(FUSE_I_BTIME, &fi->state);
+                       fi->i_btime.tv_sec = sx->btime.tv_sec;
+                       fi->i_btime.tv_nsec = sx->btime.tv_nsec;
+               }
+       }
 
        if (attr->blksize != 0)
                inode->i_blkbits = ilog2(attr->blksize);
@@ -237,6 +257,7 @@ u32 fuse_get_cache_mask(struct inode *inode)
 }
 
 void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
+                           struct fuse_statx *sx,
                            u64 attr_valid, u64 attr_version)
 {
        struct fuse_conn *fc = get_fuse_conn(inode);
@@ -271,7 +292,7 @@ void fuse_change_attributes(struct inode *inode, struct fuse_attr *attr,
        }
 
        old_mtime = inode->i_mtime;
-       fuse_change_attributes_common(inode, attr, attr_valid, cache_mask);
+       fuse_change_attributes_common(inode, attr, sx, attr_valid, cache_mask);
 
        oldsize = inode->i_size;
        /*
@@ -409,7 +430,7 @@ done:
        spin_lock(&fi->lock);
        fi->nlookup++;
        spin_unlock(&fi->lock);
-       fuse_change_attributes(inode, attr, attr_valid, attr_version);
+       fuse_change_attributes(inode, attr, NULL, attr_valid, attr_version);
 
        return inode;
 }
index c58447be5e4dc8bc730175a82fbb54cc6295e1c2..9e6d587b3e67311f231baf434979abb8e40f6776 100644 (file)
@@ -223,7 +223,7 @@ retry:
                spin_unlock(&fi->lock);
 
                forget_all_cached_acls(inode);
-               fuse_change_attributes(inode, &o->attr,
+               fuse_change_attributes(inode, &o->attr, NULL,
                                       ATTR_TIMEOUT(o),
                                       attr_version);
                /*