* [RFC PATCH 0/2] add provenance time (ptime)
@ 2026-10-27 22:05 koraynilay
2026-10-27 22:05 ` [RFC PATCH 1/2] vfs: " koraynilay
2026-10-27 22:05 ` [RFC PATCH 2/2] btrfs: " koraynilay
0 siblings, 2 replies; 3+ messages in thread
From: koraynilay @ 2026-10-27 22:05 UTC (permalink / raw)
To: David Sterba, Alexander Viro, Christian Brauner
Cc: Chris Mason, Jan Kara, Darrick J . Wong, Sean Smith,
Andrey Albershteyn, linux-btrfs, linux-fsdevel, linux-kernel,
koraynilay
This is an initial implementation of a new timestamp to indicate the
time when a file/directory was initially created.
This patch series takes inspiration from the one by Sean Smith
<defendthedisabled@gmail.com> [1], but using
file_setattr()/file_getattr() as suggested by Darrick J. Wong
<djwong@kernel.org> in [2].
The semantics of this new timestamp should be at least these:
- it should be the same as btime on a newly created file
- it's user-settable, so it can be preserved for backups, filesystem
conversions, archives and any similar operation
- it should be preserved every time mtime gets preserved
and the correspondence with the other timestamps is as follows:
ctime <-> btime
mtime <-> ptime
Examples:
1) a .zsh_history file created in 2019, then after a few years the user
decides to switch to another filesystem, copying the .zsh_history to
the new home, in that case btime would have the timestamp of the copy
and ptime would have the 2019 timestamp (just like mtime would have
been preserved)
2) a symlink created in 2020 from the home directory to a /path going
through the same process as the .zsh_history, in this case it would
also make sense to sync the ptime to the 2020 timestamp instead of
keeping only the btime (so the inode creation time).
The proof-of-concept code (for testing) for stat(1) and cp(1) is
available at https://github.com/koraynilay/coreutils (master branch)
(Note: these coreutils code changes are a prototype and won't be
submitted upstream as-is, but they work as expected in simple stat and
cp -a/--preserve=ptime tests)
Lastly, the name "provenance" and "ptime" are not final, I personally
don't have a strong opinion on any name, but while testing with stat(1)
I noticed that "Provenance" is definitely too long for the normal
output, so another option which would fit well is "Origin time",
abbreviated as "ntime" (OrigiN time) and not as "otime", as that would
conflict with btrfs' internal naming and create confusion.
Thanks.
[1]: https://lore.kernel.org/20260405195007.1306-2-DefendTheDisabled@gmail.com
[2]: https://lore.kernel.org/20260407014129.GC6192@frogsfrogsfrogs
koraynilay (2):
vfs: add provenance time (ptime)
btrfs: add provenance time (ptime)
fs/btrfs/btrfs_inode.h | 4 ++++
fs/btrfs/delayed-inode.c | 6 ++++++
fs/btrfs/inode.c | 17 ++++++++++++++++-
fs/btrfs/ioctl.c | 12 +++++++++++-
fs/btrfs/print-tree.c | 1 +
fs/btrfs/tree-log.c | 3 +++
fs/file_attr.c | 24 ++++++++++++++++++++++--
fs/stat.c | 2 ++
include/linux/fileattr.h | 27 ++++++++++++++++++++++++---
include/linux/stat.h | 1 +
include/uapi/linux/btrfs_tree.h | 3 ++-
include/uapi/linux/fs.h | 8 +++++++-
include/uapi/linux/stat.h | 7 ++++++-
13 files changed, 105 insertions(+), 10 deletions(-)
base-commit: 21a7f44fd0765fcc5194130b28767786eba1d1e7
--
2.56.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [RFC PATCH 1/2] vfs: add provenance time (ptime)
2026-10-27 22:05 [RFC PATCH 0/2] add provenance time (ptime) koraynilay
@ 2026-10-27 22:05 ` koraynilay
2026-10-27 22:05 ` [RFC PATCH 2/2] btrfs: " koraynilay
1 sibling, 0 replies; 3+ messages in thread
From: koraynilay @ 2026-10-27 22:05 UTC (permalink / raw)
To: David Sterba, Alexander Viro, Christian Brauner
Cc: Chris Mason, Jan Kara, Darrick J . Wong, Sean Smith,
Andrey Albershteyn, linux-btrfs, linux-fsdevel, linux-kernel,
koraynilay
Add provenance time (ptime), indicating the time when the file
*contents* were created. This is user-settable via file_setattr() and
retrievable via statx(2) (using STATX_PTIME) or file_getattr().
The semantics of when to preserve this new timestamp are the same as
mtime, the idea being that there is a correspondence like this:
ctime <-> btime
mtime <-> ptime
so every operation (copying, moving, ...) that currently preserves mtime
should also preserve ptime.
Furthermore, in order to avoid the legacy FS_IOC_FSSETXATTR accepting
FS_XFLAG_PTIME, add an FS_FILE_ATTR_XFLAGS_MASK mask to support new
xflags, and use it in fileattr_to_file_attr()/file_attr_to_fileattr() to
allow ptime to be used with the file_setattr()/file_getattr() syscalls.
[ This is a re-implementation of the initial patch[1] by Sean Smith
<defendthedisabled@gmail.com>, following the advice[2] from Darrick J.
Wong <djwong@kernel.org> of using file_setattr() instead of updating
utimensat() ]
[1]: https://lore.kernel.org/20260405195007.1306-2-DefendTheDisabled@gmail.com
[2]: https://lore.kernel.org/20260407014129.GC6192@frogsfrogsfrogs
Assisted-by: Gemini:3.8-flash-high antigravity-cli-1.4.7
Signed-off-by: koraynilay <koray.fra@gmail.com>
---
fs/file_attr.c | 24 ++++++++++++++++++++++--
fs/stat.c | 2 ++
include/linux/fileattr.h | 5 +++++
include/linux/stat.h | 1 +
include/uapi/linux/fs.h | 8 +++++++-
include/uapi/linux/stat.h | 7 ++++++-
6 files changed, 43 insertions(+), 4 deletions(-)
diff --git a/fs/file_attr.c b/fs/file_attr.c
index 833e03810b24..14dddd459969 100644
--- a/fs/file_attr.c
+++ b/fs/file_attr.c
@@ -102,7 +102,7 @@ EXPORT_SYMBOL(vfs_fileattr_get);
static void fileattr_to_file_attr(const struct file_kattr *fa,
struct file_attr *fattr)
{
- __u32 mask = FS_XFLAGS_MASK;
+ __u32 mask = FS_FILE_ATTR_XFLAGS_MASK;
memset(fattr, 0, sizeof(struct file_attr));
fattr->fa_xflags = fa->fsx_xflags & mask;
@@ -110,6 +110,8 @@ static void fileattr_to_file_attr(const struct file_kattr *fa,
fattr->fa_nextents = fa->fsx_nextents;
fattr->fa_projid = fa->fsx_projid;
fattr->fa_cowextsize = fa->fsx_cowextsize;
+ fattr->fa_ptime_sec = fa->fsx_ptime.tv_sec;
+ fattr->fa_ptime_nsec = fa->fsx_ptime.tv_nsec;
}
/**
@@ -141,15 +143,20 @@ EXPORT_SYMBOL(copy_fsxattr_to_user);
static int file_attr_to_fileattr(const struct file_attr *fattr,
struct file_kattr *fa)
{
- __u64 mask = FS_XFLAGS_MASK;
+ __u64 mask = FS_FILE_ATTR_XFLAGS_MASK;
if (fattr->fa_xflags & ~mask)
return -EINVAL;
+ if (fattr->fa_pad != 0)
+ return -EINVAL;
+
fileattr_fill_xflags(fa, fattr->fa_xflags & ~FS_XFLAG_RDONLY_MASK);
fa->fsx_extsize = fattr->fa_extsize;
fa->fsx_projid = fattr->fa_projid;
fa->fsx_cowextsize = fattr->fa_cowextsize;
+ fa->fsx_ptime.tv_sec = fattr->fa_ptime_sec;
+ fa->fsx_ptime.tv_nsec = fattr->fa_ptime_nsec;
return 0;
}
@@ -232,6 +239,11 @@ static int fileattr_set_prepare(struct inode *inode,
!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode))
return -EINVAL;
+ /* Check provenance time's nanoseconds */
+ if ((fa->fsx_xflags & FS_XFLAG_PTIME) &&
+ (unsigned long)fa->fsx_ptime.tv_nsec >= NSEC_PER_SEC)
+ return -EINVAL;
+
/*
* It is only valid to set the DAX flag on regular files and
* directories on filesystems.
@@ -293,6 +305,7 @@ int vfs_fileattr_set(const struct mnt_idmap *idmap, struct dentry *dentry,
fa->fsx_nextents = old_ma.fsx_nextents;
fa->fsx_projid = old_ma.fsx_projid;
fa->fsx_cowextsize = old_ma.fsx_cowextsize;
+ fa->fsx_ptime = old_ma.fsx_ptime;
} else {
fa->flags |= old_ma.flags & ~FS_COMMON_FL;
}
@@ -423,6 +436,10 @@ SYSCALL_DEFINE5(file_getattr, int, dfd, const char __user *, filename,
return error;
fileattr_to_file_attr(&fa, &fattr);
+
+ if (usize < FILE_ATTR_SIZE_VER1)
+ fattr.fa_xflags &= ~FS_XFLAG_PTIME;
+
error = copy_struct_to_user(ufattr, usize, &fattr,
sizeof(struct file_attr), NULL);
@@ -459,6 +476,9 @@ SYSCALL_DEFINE5(file_setattr, int, dfd, const char __user *, filename,
if (error)
return error;
+ if ((fattr.fa_xflags & FS_XFLAG_PTIME) && usize < FILE_ATTR_SIZE_VER1)
+ return -EINVAL;
+
error = file_attr_to_fileattr(&fattr, &fa);
if (error)
return error;
diff --git a/fs/stat.c b/fs/stat.c
index a2e63c6c45b7..e426f41253f6 100644
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -728,6 +728,8 @@ cp_statx(const struct kstat *stat, struct statx __user *buffer)
tmp.stx_ctime.tv_nsec = stat->ctime.tv_nsec;
tmp.stx_mtime.tv_sec = stat->mtime.tv_sec;
tmp.stx_mtime.tv_nsec = stat->mtime.tv_nsec;
+ tmp.stx_ptime.tv_sec = stat->ptime.tv_sec;
+ tmp.stx_ptime.tv_nsec = stat->ptime.tv_nsec;
tmp.stx_rdev_major = MAJOR(stat->rdev);
tmp.stx_rdev_minor = MINOR(stat->rdev);
tmp.stx_dev_major = MAJOR(stat->dev);
diff --git a/include/linux/fileattr.h b/include/linux/fileattr.h
index 09e32b84e02a..0b93f9609284 100644
--- a/include/linux/fileattr.h
+++ b/include/linux/fileattr.h
@@ -35,6 +35,10 @@
(FS_XFLAG_COMMON | FS_XFLAG_RDONLY_MASK | FS_XFLAG_VALUES_MASK | \
FS_XFLAG_DIRONLY_MASK | FS_XFLAG_MISC_MASK)
+/* Flags only for fa_ fields */
+#define FS_FILE_ATTR_XFLAGS_MASK \
+ (FS_XFLAGS_MASK | FS_XFLAG_PTIME)
+
/*
* Merged interface for miscellaneous file attributes. 'flags' originates from
* ext* and 'fsx_flags' from xfs. There's some overlap between the two, which
@@ -49,6 +53,7 @@ struct file_kattr {
u32 fsx_nextents; /* nextents field value (get) */
u32 fsx_projid; /* project identifier (get/set) */
u32 fsx_cowextsize; /* CoW extsize field value (get/set)*/
+ struct timespec64 fsx_ptime; /* provenance (settable birth) time */
/* selectors: */
bool flags_valid:1;
bool fsx_valid:1;
diff --git a/include/linux/stat.h b/include/linux/stat.h
index 9c5709132862..69d2ecfd1bf2 100644
--- a/include/linux/stat.h
+++ b/include/linux/stat.h
@@ -48,6 +48,7 @@ struct kstat {
struct timespec64 mtime;
struct timespec64 ctime;
struct timespec64 btime; /* File creation time */
+ struct timespec64 ptime; /* File provenance (user-settable creation) time */
u64 blocks;
u64 mnt_id;
u64 change_cookie;
diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h
index a46c33692aa2..c00e72fcfa3e 100644
--- a/include/uapi/linux/fs.h
+++ b/include/uapi/linux/fs.h
@@ -229,10 +229,14 @@ struct file_attr {
__u32 fa_nextents; /* nextents field value (get) */
__u32 fa_projid; /* project identifier (get/set) */
__u32 fa_cowextsize; /* CoW extsize field value (get/set) */
+ __s64 fa_ptime_sec; /* provenance (settable birth) time (seconds) */
+ __u32 fa_ptime_nsec; /* provenance (settable birth) time (nanoseconds) */
+ __u32 fa_pad;
};
#define FILE_ATTR_SIZE_VER0 24
-#define FILE_ATTR_SIZE_LATEST FILE_ATTR_SIZE_VER0
+#define FILE_ATTR_SIZE_VER1 40
+#define FILE_ATTR_SIZE_LATEST FILE_ATTR_SIZE_VER1
/*
* Flags for the fsx_xflags field
@@ -261,6 +265,8 @@ struct file_attr {
*/
#define FS_XFLAG_CASEFOLD 0x00040000 /* case-insensitive lookups */
#define FS_XFLAG_CASENONPRESERVING 0x00080000 /* case not preserved */
+
+#define FS_XFLAG_PTIME 0x00100000 /* provenance time */
#define FS_XFLAG_HASATTR 0x80000000 /* no DIFLAG for this */
/* the read-only stuff doesn't really belong here, but any other place is
diff --git a/include/uapi/linux/stat.h b/include/uapi/linux/stat.h
index 1686861aae20..f3a1013ba022 100644
--- a/include/uapi/linux/stat.h
+++ b/include/uapi/linux/stat.h
@@ -187,7 +187,11 @@ struct statx {
__u32 __spare2[1];
/* 0xc0 */
- __u64 __spare3[8]; /* Spare space for future expansion */
+ /* Data provenance (creation) time */
+ struct statx_timestamp stx_ptime;
+
+ /* 0xd0 */
+ __u64 __spare3[6]; /* Spare space for future expansion */
/* 0x100 */
};
@@ -219,6 +223,7 @@ struct statx {
#define STATX_SUBVOL 0x00008000U /* Want/got stx_subvol */
#define STATX_WRITE_ATOMIC 0x00010000U /* Want/got atomic_write_* fields */
#define STATX_DIO_READ_ALIGN 0x00020000U /* Want/got dio read alignment info */
+#define STATX_PTIME 0x00040000U /* Want/got stx_ptime */
#define STATX__RESERVED 0x80000000U /* Reserved for future struct statx expansion */
--
2.56.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [RFC PATCH 2/2] btrfs: add provenance time (ptime)
2026-10-27 22:05 [RFC PATCH 0/2] add provenance time (ptime) koraynilay
2026-10-27 22:05 ` [RFC PATCH 1/2] vfs: " koraynilay
@ 2026-10-27 22:05 ` koraynilay
1 sibling, 0 replies; 3+ messages in thread
From: koraynilay @ 2026-10-27 22:05 UTC (permalink / raw)
To: David Sterba, Alexander Viro, Christian Brauner
Cc: Chris Mason, Jan Kara, Darrick J . Wong, Sean Smith,
Andrey Albershteyn, linux-btrfs, linux-fsdevel, linux-kernel,
koraynilay
Implement provenance time (ptime) in Btrfs, as introduced in the
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 add the fileattr_has_fsx_mask() helper to include/linux/fileattr.h
to let filesystems check for unsupported xflags while allowing supported
ones (like ptime). This helper can be used in the same way as
fileattr_has_fsx().
Assisted-by: Gemini:3.8-flash-high antigravity-cli-1.4.7
Signed-off-by: koraynilay <koray.fra@gmail.com>
---
fs/btrfs/btrfs_inode.h | 4 ++++
fs/btrfs/delayed-inode.c | 6 ++++++
fs/btrfs/inode.c | 17 ++++++++++++++++-
fs/btrfs/ioctl.c | 12 +++++++++++-
fs/btrfs/print-tree.c | 1 +
fs/btrfs/tree-log.c | 3 +++
include/linux/fileattr.h | 22 +++++++++++++++++++---
include/uapi/linux/btrfs_tree.h | 3 ++-
8 files changed, 62 insertions(+), 6 deletions(-)
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 0227789e911d..5afcf99b758c 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 1272e0d01485..7c3cd06113bc 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -4094,6 +4094,9 @@ 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);
+
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);
@@ -4286,6 +4289,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));
@@ -6025,6 +6031,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;
@@ -6751,6 +6759,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
@@ -8095,6 +8105,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);
@@ -8241,9 +8254,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)
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
index f3e2afe221be..48598a99bddb 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,10 @@ 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 +442,10 @@ int btrfs_fileattr_set(const struct mnt_idmap *idmap,
}
update_flags:
+ if (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/linux/fileattr.h b/include/linux/fileattr.h
index 0b93f9609284..f45377fdaf38 100644
--- a/include/linux/fileattr.h
+++ b/include/linux/fileattr.h
@@ -64,6 +64,24 @@ int copy_fsxattr_to_user(const struct file_kattr *fa, struct fsxattr __user *ufa
void fileattr_fill_xflags(struct file_kattr *fa, u32 xflags);
void fileattr_fill_flags(struct file_kattr *fa, u32 flags);
+
+/**
+ * fileattr_has_fsx_mask - check for extended flags/attributes
+ * @fa: fileattr pointer
+ * @allowed_xflags_mask: additional xflags permitted
+ *
+ * Return: true if any attributes are present that are not represented in
+ * ->flags or @allowed_xflags_mask.
+ */
+static inline bool fileattr_has_fsx_mask(const struct file_kattr *fa,
+ u32 allowed_xflags_mask)
+{
+ return fa->fsx_valid &&
+ ((fa->fsx_xflags & ~(FS_XFLAG_COMMON | allowed_xflags_mask)) ||
+ fa->fsx_extsize != 0 || fa->fsx_projid != 0 ||
+ fa->fsx_cowextsize != 0);
+}
+
/**
* fileattr_has_fsx - check for extended flags/attributes
* @fa: fileattr pointer
@@ -73,9 +91,7 @@ void fileattr_fill_flags(struct file_kattr *fa, u32 flags);
*/
static inline bool fileattr_has_fsx(const struct file_kattr *fa)
{
- return fa->fsx_valid &&
- ((fa->fsx_xflags & ~FS_XFLAG_COMMON) || fa->fsx_extsize != 0 ||
- fa->fsx_projid != 0 || fa->fsx_cowextsize != 0);
+ return fileattr_has_fsx_mask(fa, 0);
}
int vfs_fileattr_get(struct dentry *dentry, struct file_kattr *fa);
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
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-27 22:06 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-27 22:05 [RFC PATCH 0/2] add provenance time (ptime) koraynilay
2026-10-27 22:05 ` [RFC PATCH 1/2] vfs: " koraynilay
2026-10-27 22:05 ` [RFC PATCH 2/2] btrfs: " koraynilay
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox