From: Sean Smith <defendthedisabled@gmail.com>
To: linux-fsdevel@vger.kernel.org
Cc: linux-ext4@vger.kernel.org, linux-btrfs@vger.kernel.org,
tytso@mit.edu, dsterba@suse.com, david@fromorbit.com,
brauner@kernel.org, osandov@osandov.com, almaz@kernel.org,
hirofumi@mail.parknet.co.jp, linkinjeon@kernel.org,
Sean Smith <DefendTheDisabled@gmail.com>
Subject: [PATCH 4/6] ext4: add dedicated ptime field alongside i_crtime
Date: Sun, 5 Apr 2026 14:50:00 -0500 [thread overview]
Message-ID: <20260405195007.1306-5-DefendTheDisabled@gmail.com> (raw)
In-Reply-To: <20260405195007.1306-1-DefendTheDisabled@gmail.com>
Add i_ptime (__le32) and i_ptime_extra (__le32) to the ext4 on-disk
inode structure after i_projid. Total: 8 bytes in the extended inode
area. i_crtime remains untouched as immutable birth time.
This is a native-ptime implementation: ptime and btime are separate
fields. On 256-byte inodes (modern default), both fit easily. On
128-byte inodes, ptime is silently unavailable (same graceful
degradation as i_crtime via EXT4_FITS_IN_INODE).
Uses existing EXT4_EINODE_GET_XTIME/SET_XTIME macros for read/write.
Rename-over: when a file with ptime=0 replaces a file with ptime set,
inherit target ptime (same zero-sentinel logic as Btrfs).
Signed-off-by: Sean Smith <DefendTheDisabled@gmail.com>
---
fs/ext4/ext4.h | 3 +++
fs/ext4/inode.c | 14 ++++++++++++++
fs/ext4/namei.c | 13 +++++++++++++
3 files changed, 30 insertions(+)
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index f1c476303..5c2812637 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -860,6 +860,8 @@ struct ext4_inode {
__le32 i_crtime_extra; /* extra FileCreationtime (nsec << 2 | epoch) */
__le32 i_version_hi; /* high 32 bits for 64-bit version */
__le32 i_projid; /* Project ID */
+ __le32 i_ptime; /* Provenance time */
+ __le32 i_ptime_extra; /* extra Provenance time (nsec << 2 | epoch) */
};
#define EXT4_EPOCH_BITS 2
@@ -1136,6 +1138,7 @@ struct ext4_inode_info {
* struct timespec64 i_{a,c,m}time in the generic inode.
*/
struct timespec64 i_crtime;
+ struct timespec64 i_ptime;
/* mballoc */
atomic_t i_prealloc_active;
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index 625cfbf61..15b6b6dc6 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -4753,6 +4753,7 @@ static int ext4_fill_raw_inode(struct inode *inode, struct ext4_inode *raw_inode
EXT4_INODE_SET_MTIME(inode, raw_inode);
EXT4_INODE_SET_ATIME(inode, raw_inode);
EXT4_EINODE_SET_XTIME(i_crtime, ei, raw_inode);
+ EXT4_EINODE_SET_XTIME(i_ptime, ei, raw_inode);
raw_inode->i_dtime = cpu_to_le32(ei->i_dtime);
raw_inode->i_flags = cpu_to_le32(ei->i_flags & 0xFFFFFFFF);
@@ -5409,6 +5410,7 @@ struct inode *__ext4_iget(struct super_block *sb, unsigned long ino,
EXT4_INODE_GET_ATIME(inode, raw_inode);
EXT4_INODE_GET_MTIME(inode, raw_inode);
EXT4_EINODE_GET_XTIME(i_crtime, ei, raw_inode);
+ EXT4_EINODE_GET_XTIME(i_ptime, ei, raw_inode);
if (likely(!test_opt2(inode->i_sb, HURD_COMPAT))) {
u64 ivers = le32_to_cpu(raw_inode->i_disk_version);
@@ -6061,6 +6063,9 @@ int ext4_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
if (!error) {
if (inc_ivers)
inode_inc_iversion(inode);
+ if (attr->ia_valid & ATTR_PTIME)
+ EXT4_I(inode)->i_ptime = attr->ia_ptime;
+
setattr_copy(idmap, inode, attr);
mark_inode_dirty(inode);
}
@@ -6114,6 +6119,15 @@ int ext4_getattr(struct mnt_idmap *idmap, const struct path *path,
stat->btime.tv_nsec = ei->i_crtime.tv_nsec;
}
+ /* Report ptime from dedicated field, not crtime */
+ if ((request_mask & STATX_PTIME) &&
+ EXT4_FITS_IN_INODE(raw_inode, ei, i_ptime) &&
+ (ei->i_ptime.tv_sec || ei->i_ptime.tv_nsec)) {
+ stat->result_mask |= STATX_PTIME;
+ stat->ptime.tv_sec = ei->i_ptime.tv_sec;
+ stat->ptime.tv_nsec = ei->i_ptime.tv_nsec;
+ }
+
/*
* Return the DIO alignment restrictions if requested. We only return
* this information when requested, since on encrypted files it might
diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c
index c4b5e252a..1bfe4df24 100644
--- a/fs/ext4/namei.c
+++ b/fs/ext4/namei.c
@@ -3942,6 +3942,19 @@ static int ext4_rename(struct mnt_idmap *idmap, struct inode *old_dir,
* rename.
*/
inode_set_ctime_current(old.inode);
+
+ /* ptime rename-over: preserve ptime across atomic saves */
+ if (new.inode && S_ISREG(old.inode->i_mode) &&
+ S_ISREG(new.inode->i_mode) && old.inode->i_nlink == 1 &&
+ !(flags & RENAME_WHITEOUT)) {
+ struct ext4_inode_info *old_ei = EXT4_I(old.inode);
+ struct ext4_inode_info *new_ei = EXT4_I(new.inode);
+
+ if (!old_ei->i_ptime.tv_sec && !old_ei->i_ptime.tv_nsec &&
+ (new_ei->i_ptime.tv_sec || new_ei->i_ptime.tv_nsec))
+ old_ei->i_ptime = new_ei->i_ptime;
+ }
+
retval = ext4_mark_inode_dirty(handle, old.inode);
if (unlikely(retval))
goto end_rename;
--
2.53.0
next prev parent reply other threads:[~2026-04-05 19:50 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-05 19:49 [RFC PATCH v1 0/6] provenance_time (ptime): a new settable timestamp for cross-filesystem provenance Sean Smith
2026-04-05 19:49 ` [PATCH 1/6] vfs: add provenance_time (ptime) infrastructure Sean Smith
2026-04-05 19:49 ` [PATCH 2/6] btrfs: add provenance time (ptime) support Sean Smith
2026-04-05 19:49 ` [PATCH 3/6] ntfs3: map ptime to NTFS creation time with rename-over Sean Smith
2026-04-05 19:50 ` Sean Smith [this message]
2026-04-05 19:50 ` [PATCH 5/6] fat: map ptime to FAT " Sean Smith
2026-04-05 19:50 ` [PATCH 6/6] exfat: map ptime to exFAT " Sean Smith
2026-04-05 22:54 ` [RFC PATCH v1 0/6] provenance_time (ptime): a new settable timestamp for cross-filesystem provenance Theodore Tso
2026-04-07 0:05 ` Sean Smith
2026-04-07 1:42 ` Darrick J. Wong
2026-04-07 6:06 ` Sean Smith
2026-04-07 15:17 ` Darrick J. Wong
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260405195007.1306-5-DefendTheDisabled@gmail.com \
--to=defendthedisabled@gmail.com \
--cc=almaz@kernel.org \
--cc=brauner@kernel.org \
--cc=david@fromorbit.com \
--cc=dsterba@suse.com \
--cc=hirofumi@mail.parknet.co.jp \
--cc=linkinjeon@kernel.org \
--cc=linux-btrfs@vger.kernel.org \
--cc=linux-ext4@vger.kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=osandov@osandov.com \
--cc=tytso@mit.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox