Implement provenance time (ptime) in Btrfs, as introduced in a previous commit ("vfs: add provenance time (ptime)"). Update the on-disk format by claiming 12 bytes from the reserved[] space in btrfs_inode_item, putting ptime before other timestamps. Also, set .fileattr_[gs]et to btrfs_fileattr_[gs]et() for both symlinks and special files. Assisted-by: Gemini:3.8-flash-high antigravity-cli-1.4.7 Signed-off-by: koraynilay --- fs/btrfs/btrfs_inode.h | 4 ++++ fs/btrfs/delayed-inode.c | 6 ++++++ fs/btrfs/inode.c | 29 ++++++++++++++++++++++++++++- fs/btrfs/ioctl.c | 13 ++++++++++++- fs/btrfs/print-tree.c | 1 + fs/btrfs/tree-log.c | 3 +++ include/uapi/linux/btrfs_tree.h | 3 ++- 7 files changed, 56 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h index 0782f917fccc..c9822340567a 100644 --- a/fs/btrfs/btrfs_inode.h +++ b/fs/btrfs/btrfs_inode.h @@ -332,6 +332,10 @@ struct btrfs_inode { u64 i_otime_sec; u32 i_otime_nsec; + /* File *data* creation (provenance) time. */ + s64 i_ptime_sec; + u32 i_ptime_nsec; + /* Hook into fs_info->delayed_iputs */ struct list_head delayed_iput; diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index 333ce8b52489..eef73ebb4a52 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -1932,6 +1932,9 @@ static void fill_stack_inode_item(struct btrfs_trans_handle *trans, btrfs_set_stack_timespec_sec(&inode_item->otime, inode->i_otime_sec); btrfs_set_stack_timespec_nsec(&inode_item->otime, inode->i_otime_nsec); + + btrfs_set_stack_timespec_sec(&inode_item->ptime, inode->i_ptime_sec); + btrfs_set_stack_timespec_nsec(&inode_item->ptime, inode->i_ptime_nsec); } int btrfs_fill_inode(struct btrfs_inode *inode, u32 *rdev) @@ -1981,6 +1984,9 @@ int btrfs_fill_inode(struct btrfs_inode *inode, u32 *rdev) inode->i_otime_sec = btrfs_stack_timespec_sec(&inode_item->otime); inode->i_otime_nsec = btrfs_stack_timespec_nsec(&inode_item->otime); + inode->i_ptime_sec = btrfs_stack_timespec_sec(&inode_item->ptime); + inode->i_ptime_nsec = btrfs_stack_timespec_nsec(&inode_item->ptime); + vfs_inode->i_generation = inode->generation; if (S_ISDIR(vfs_inode->i_mode)) inode->index_cnt = (u64)-1; diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c index abaa33471456..6ac7fa774ccf 100644 --- a/fs/btrfs/inode.c +++ b/fs/btrfs/inode.c @@ -4057,6 +4057,17 @@ static int btrfs_read_locked_inode(struct btrfs_inode *inode, struct btrfs_path inode->i_otime_sec = btrfs_timespec_sec(leaf, &inode_item->otime); inode->i_otime_nsec = btrfs_timespec_nsec(leaf, &inode_item->otime); + inode->i_ptime_sec = btrfs_timespec_sec(leaf, &inode_item->ptime); + inode->i_ptime_nsec = btrfs_timespec_nsec(leaf, &inode_item->ptime); + /* Avoid old garbage on-disk data making btrfs load a bad value */ + if (unlikely(inode->i_ptime_nsec >= NSEC_PER_SEC)) { + btrfs_warn(fs_info, + "invalid ptime.nsec %u on ino %llu from disk, setting ptime to otime", + inode->i_ptime_nsec, btrfs_ino(inode)); + inode->i_ptime_sec = inode->i_otime_sec; + inode->i_ptime_nsec = inode->i_otime_nsec; + } + inode_set_bytes(vfs_inode, btrfs_inode_nbytes(leaf, inode_item)); inode->generation = btrfs_inode_generation(leaf, inode_item); inode->last_trans = btrfs_inode_transid(leaf, inode_item); @@ -4249,6 +4260,9 @@ static void fill_inode_item(struct btrfs_trans_handle *trans, btrfs_set_timespec_sec(leaf, &item->otime, BTRFS_I(inode)->i_otime_sec); btrfs_set_timespec_nsec(leaf, &item->otime, BTRFS_I(inode)->i_otime_nsec); + btrfs_set_timespec_sec(leaf, &item->ptime, BTRFS_I(inode)->i_ptime_sec); + btrfs_set_timespec_nsec(leaf, &item->ptime, BTRFS_I(inode)->i_ptime_nsec); + btrfs_set_inode_nbytes(leaf, item, inode_get_bytes(inode)); btrfs_set_inode_generation(leaf, item, BTRFS_I(inode)->generation); btrfs_set_inode_sequence(leaf, item, inode_peek_iversion(inode)); @@ -5988,6 +6002,8 @@ static struct btrfs_inode *new_simple_dir(struct inode *dir, inode_set_atime_to_ts(vfs_inode, inode_get_atime(dir)); inode->i_otime_sec = ts.tv_sec; inode->i_otime_nsec = ts.tv_nsec; + inode->i_ptime_sec = ts.tv_sec; + inode->i_ptime_nsec = ts.tv_nsec; vfs_inode->i_uid = dir->i_uid; vfs_inode->i_gid = dir->i_gid; @@ -6714,6 +6730,8 @@ int btrfs_create_new_inode(struct btrfs_trans_handle *trans, ts = simple_inode_init_ts(inode); BTRFS_I(inode)->i_otime_sec = ts.tv_sec; BTRFS_I(inode)->i_otime_nsec = ts.tv_nsec; + BTRFS_I(inode)->i_ptime_sec = ts.tv_sec; + BTRFS_I(inode)->i_ptime_nsec = ts.tv_nsec; /* * We're going to fill the inode item now, so at this point the inode @@ -8048,6 +8066,9 @@ struct inode *btrfs_alloc_inode(struct super_block *sb) ei->i_otime_sec = 0; ei->i_otime_nsec = 0; + ei->i_ptime_sec = 0; + ei->i_ptime_nsec = 0; + inode = &ei->vfs_inode; btrfs_extent_map_tree_init(&ei->extent_tree); @@ -8186,9 +8207,11 @@ static int btrfs_getattr(const struct mnt_idmap *idmap, u32 blocksize = btrfs_sb(inode->i_sb)->sectorsize; u32 bi_flags = BTRFS_I(inode)->flags; - stat->result_mask |= STATX_BTIME; + stat->result_mask |= (STATX_BTIME | STATX_PTIME); stat->btime.tv_sec = BTRFS_I(inode)->i_otime_sec; stat->btime.tv_nsec = BTRFS_I(inode)->i_otime_nsec; + stat->ptime.tv_sec = BTRFS_I(inode)->i_ptime_sec; + stat->ptime.tv_nsec = BTRFS_I(inode)->i_ptime_nsec; if (bi_flags & BTRFS_INODE_APPEND) stat->attributes |= STATX_ATTR_APPEND; if (bi_flags & BTRFS_INODE_COMPRESS) @@ -10779,6 +10802,8 @@ static const struct inode_operations btrfs_special_inode_operations = { .get_inode_acl = btrfs_get_acl, .set_acl = btrfs_set_acl, .update_time = btrfs_update_time, + .fileattr_get = btrfs_fileattr_get, + .fileattr_set = btrfs_fileattr_set, }; static const struct inode_operations btrfs_symlink_inode_operations = { .get_link = page_get_link, @@ -10787,6 +10812,8 @@ static const struct inode_operations btrfs_symlink_inode_operations = { .permission = btrfs_permission, .listxattr = btrfs_listxattr, .update_time = btrfs_update_time, + .fileattr_get = btrfs_fileattr_get, + .fileattr_set = btrfs_fileattr_set, }; const struct dentry_operations btrfs_dentry_operations = { diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index f3e2afe221be..dfcf710d828c 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -275,6 +275,9 @@ int btrfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa) const struct btrfs_inode *inode = BTRFS_I(d_inode(dentry)); fileattr_fill_flags(fa, btrfs_inode_flags_to_fsflags(inode)); + fa->fsx_ptime.tv_sec = inode->i_ptime_sec; + fa->fsx_ptime.tv_nsec = inode->i_ptime_nsec; + fa->fsx_xflags |= FS_XFLAG_PTIME; return 0; } @@ -294,7 +297,11 @@ int btrfs_fileattr_set(const struct mnt_idmap *idmap, if (btrfs_root_readonly(root)) return -EROFS; - if (fileattr_has_fsx(fa)) + /* + * We now support FS_XFLAG_PTIME, so exclude it from the unsupported + * check + */ + if (fileattr_has_fsx_mask(fa, FS_XFLAG_PTIME)) return -EOPNOTSUPP; fsflags = btrfs_mask_fsflags_for_type(&inode->vfs_inode, fa->flags); @@ -436,6 +443,10 @@ int btrfs_fileattr_set(const struct mnt_idmap *idmap, } update_flags: + if (!fa->flags_valid && (fa->fsx_xflags & FS_XFLAG_PTIME)) { + inode->i_ptime_sec = fa->fsx_ptime.tv_sec; + inode->i_ptime_nsec = fa->fsx_ptime.tv_nsec; + } inode->flags = inode_flags; btrfs_update_inode_mapping_flags(inode); btrfs_sync_inode_flags_to_i_flags(inode); diff --git a/fs/btrfs/print-tree.c b/fs/btrfs/print-tree.c index 53e726119ca7..ba385d0ed9ed 100644 --- a/fs/btrfs/print-tree.c +++ b/fs/btrfs/print-tree.c @@ -263,6 +263,7 @@ static void print_inode_item(const struct extent_buffer *eb, int i) print_timespec(eb, &ii->ctime, "\t\tctime ", "\n"); print_timespec(eb, &ii->mtime, "\t\tmtime ", "\n"); print_timespec(eb, &ii->otime, "\t\totime ", "\n"); + print_timespec(eb, &ii->ptime, "\t\tptime ", "\n"); } static void print_dir_item(const struct extent_buffer *eb, int i) diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 1d9dfb63fc5a..72a4a92b40b9 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -4631,6 +4631,9 @@ static void fill_inode_item(struct btrfs_trans_handle *trans, btrfs_set_timespec_sec(leaf, &item->otime, inode->i_otime_sec); btrfs_set_timespec_nsec(leaf, &item->otime, inode->i_otime_nsec); + btrfs_set_timespec_sec(leaf, &item->ptime, inode->i_ptime_sec); + btrfs_set_timespec_nsec(leaf, &item->ptime, inode->i_ptime_nsec); + /* * We do not need to set the nbytes field, in fact during a fast fsync * its value may not even be correct, since a fast fsync does not wait diff --git a/include/uapi/linux/btrfs_tree.h b/include/uapi/linux/btrfs_tree.h index 47ee52859b45..c360b3e95f45 100644 --- a/include/uapi/linux/btrfs_tree.h +++ b/include/uapi/linux/btrfs_tree.h @@ -900,7 +900,8 @@ struct btrfs_inode_item { * a little future expansion, for more than this we can * just grow the inode item and version it */ - __le64 reserved[4]; + __le32 reserved[5]; + struct btrfs_timespec ptime; struct btrfs_timespec atime; struct btrfs_timespec ctime; struct btrfs_timespec mtime; -- 2.56.0