Implemented at ext4 spec we now support timestamps after 2038 and
to a nano precision. Backward compatible (works as before on systems that are
formatted to 128)
if node size is larger it supports extra time precision
To test (inside Hurd):
dd if=/dev/zero of=test.img bs=1M count=100
sudo mke2fs -I 256 -v test.img
sudo settrans -ca /mnt /hurd/ext2fs.static test.img
sudo touch -d '2045-01-01 12:34:56.789123456' /mnt/precision_test
stat /mnt/precision_test
File: /mnt/precision_test
Size: 0 Blocks: 0 IO Block: 8192 regular empty file
Device: 3,1 Inode: 13 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2045-01-01 12:34:56.789123456 +0000
Modify: 2045-01-01 12:34:56.789123456 +0000
Change: 2026-01-24 06:23:58.318082000 +0000
---
ext2fs/ext2_fs.h | 18 ++++++++-
ext2fs/ext2fs.h | 11 ++++--
ext2fs/hyper.c | 5 ++-
ext2fs/ialloc.c | 6 ++-
ext2fs/inode.c | 96 +++++++++++++++++++++++++++++++++++-------------
5 files changed, 103 insertions(+), 33 deletions(-)
diff --git a/ext2fs/ext2_fs.h b/ext2fs/ext2_fs.h
index daa49543..ea84c81c 100644
--- a/ext2fs/ext2_fs.h
+++ b/ext2fs/ext2_fs.h
@@ -278,6 +278,18 @@ struct ext2_inode {
} osd2; /* OS dependent 2 */
};
+struct ext2_inode_extra {
+ __u16 i_extra_isize; /* Size of this extra record */
+ __u16 i_checksum_hi; /* Upper 16-bits of inode checksum */
+ __u32 i_ctime_extra; /* Extra ctime bits (nanos + epoch) */
+ __u32 i_mtime_extra; /* Extra mtime bits (nanos + epoch) */
+ __u32 i_atime_extra; /* Extra atime bits (nanos + epoch) */
+ __u32 i_crtime; /* File creation time (Birth time) */
+ __u32 i_crtime_extra; /* Extra crtime bits */
+ __u32 i_version_hi; /* High 32 bits of 64-bit version */
+ __u32 i_projid; /* Project ID */
+};
+
#define i_size_high i_dir_acl
#define i_translator osd1.hurd1.h_i_translator
@@ -425,7 +437,7 @@ struct ext2_super_block {
#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */
#define EXT2_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */
-#define EXT2_CURRENT_REV EXT2_GOOD_OLD_REV
+#define EXT2_CURRENT_REV EXT2_DYNAMIC_REV
#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV
#define EXT2_GOOD_OLD_INODE_SIZE 128
@@ -573,6 +585,10 @@ struct ext2_inode_info {
__u32 i_prealloc_block;
__u32 i_prealloc_count;
int i_new_inode:1; /* Is a freshly allocated inode */
+ __u16 i_extra_isize;
+ __u32 i_atime_extra;
+ __u32 i_ctime_extra;
+ __u32 i_mtime_extra;
};
#endif /* _LINUX_EXT2_FS_H */
diff --git a/ext2fs/ext2fs.h b/ext2fs/ext2fs.h
index 62ee9f77..eea85d1d 100644
--- a/ext2fs/ext2fs.h
+++ b/ext2fs/ext2fs.h
@@ -427,9 +427,14 @@ dino_ref (ino_t inum)
unsigned long bg_num = (inum - 1) / inodes_per_group;
unsigned long group_inum = (inum - 1) % inodes_per_group;
struct ext2_group_desc *bg = group_desc (bg_num);
- block_t block = le32toh (bg->bg_inode_table) + (group_inum /
inodes_per_block);
- struct ext2_inode *inode = disk_cache_block_ref (block);
- inode += group_inum % inodes_per_block;
+ uint16_t inode_size = le16toh (sblock->s_inode_size);
+ if (inode_size == 0)
+ inode_size = EXT2_GOOD_OLD_INODE_SIZE; /* Fallback for old volumes */
+ unsigned long inodes_per_blk = block_size / inode_size;
+ block_t block = le32toh (bg->bg_inode_table) + (group_inum / inodes_per_blk);
+ void *block_ptr = disk_cache_block_ref (block);
+ size_t offset = (group_inum % inodes_per_blk) * inode_size;
+ struct ext2_inode *inode = (struct ext2_inode *)((char *)block_ptr + offset);
ext2_debug ("(%llu) = %p", inum, inode);
return inode;
}
diff --git a/ext2fs/hyper.c b/ext2fs/hyper.c
index 2af7e870..ea368c95 100644
--- a/ext2fs/hyper.c
+++ b/ext2fs/hyper.c
@@ -133,8 +133,9 @@ get_hypermetadata (void)
features);
diskfs_readonly = 1;
}
- if (le16toh (sblock->s_inode_size) != EXT2_GOOD_OLD_INODE_SIZE)
- ext2_panic ("inode size %d isn't supported, only %d is supported",
le16toh (sblock->s_inode_size), EXT2_GOOD_OLD_INODE_SIZE);
+ uint16_t inode_size = le16toh (sblock->s_inode_size);
+ if (inode_size < EXT2_GOOD_OLD_INODE_SIZE || (inode_size & (inode_size -
1)) != 0)
+ ext2_panic ("inode size %d isn't supported", inode_size);
if (EXT2_HAS_COMPAT_FEATURE (sblock, EXT3_FEATURE_COMPAT_HAS_JOURNAL))
ext2_warning ("mounting ext3 filesystem as ext2");
}
diff --git a/ext2fs/ialloc.c b/ext2fs/ialloc.c
index c2588fc4..fd0f9db6 100644
--- a/ext2fs/ialloc.c
+++ b/ext2fs/ialloc.c
@@ -278,7 +278,7 @@ repeat:
fields. */
{
struct ext2_inode *di = dino_ref (inum);
- memset (di, 0, sizeof *di);
+ memset (di, 0, EXT2_INODE_SIZE (sblock));
dino_deref (di);
}
@@ -345,6 +345,10 @@ diskfs_alloc_node (struct node *dir, mode_t mode, struct
node **node)
diskfs_node_disknode (np)->info.i_next_alloc_goal = 0;
diskfs_node_disknode (np)->info.i_prealloc_block = 0;
diskfs_node_disknode (np)->info.i_prealloc_count = 0;
+ diskfs_node_disknode (np)->info.i_atime_extra = 0;
+ diskfs_node_disknode (np)->info.i_mtime_extra = 0;
+ diskfs_node_disknode (np)->info.i_ctime_extra = 0;
+ diskfs_node_disknode (np)->info.i_extra_isize = 0;
/* diskfs_node_disknode (np)->info.i_new_inode */
/*
diff --git a/ext2fs/inode.c b/ext2fs/inode.c
index dc309ac8..0fe07748 100644
--- a/ext2fs/inode.c
+++ b/ext2fs/inode.c
@@ -106,6 +106,36 @@ diskfs_new_hardrefs (struct node *np)
{
allow_pager_softrefs (np);
}
+
+static inline void
+ext2_decode_extra_time (uint32_t legacy_sec, uint32_t extra,
+ time_t *sec, long *nsec)
+{
+ /* Epoch extension (bits 32 and 33) */
+ *sec = (time_t)legacy_sec + (((time_t)extra & 0x3) << 32);
+ /* Nanoseconds (bits 2 through 31) */
+ *nsec = (long)(extra >> 2);
+}
+
+static inline uint32_t
+ext2_encode_extra_time (time_t sec, long nsec)
+{
+ uint32_t extra;
+ /* Pack nanoseconds into the upper 30 bits */
+ extra = (uint32_t)(nsec << 2);
+ /* Pack bits 32 and 33 of seconds into the lower 2 bits */
+ extra |= (uint32_t)((sec >> 32) & 0x3);
+ return extra;
+}
+
+/* Helper to check if the current filesystem supports extended inodes */
+static inline int
+ext2_has_extra_inodes (struct ext2_super_block *sb)
+{
+ return (le32toh (sb->s_rev_level) > EXT2_GOOD_OLD_REV
+ && le16toh (sb->s_inode_size) > EXT2_GOOD_OLD_INODE_SIZE);
+}
+
/* The user must define this function if she wants to use the node
cache. Read stat information out of the on-disk node. */
@@ -136,23 +166,31 @@ diskfs_user_read_node (struct node *np, struct
lookup_context *ctx)
st->st_gen = le32toh (di->i_generation);
st->st_atim.tv_sec = le32toh (di->i_atime);
-#ifdef not_yet
- /* ``struct ext2_inode'' doesn't do better than sec. precision yet. */
-#else
- st->st_atim.tv_nsec = 0;
-#endif
st->st_mtim.tv_sec = le32toh (di->i_mtime);
-#ifdef not_yet
- /* ``struct ext2_inode'' doesn't do better than sec. precision yet. */
-#else
- st->st_mtim.tv_nsec = 0;
-#endif
st->st_ctim.tv_sec = le32toh (di->i_ctime);
-#ifdef not_yet
- /* ``struct ext2_inode'' doesn't do better than sec. precision yet. */
-#else
- st->st_ctim.tv_nsec = 0;
-#endif
+ st->st_atim.tv_nsec = st->st_mtim.tv_nsec = st->st_ctim.tv_nsec = 0;
+ if (ext2_has_extra_inodes (sblock))
+ {
+ struct ext2_inode_extra *di_extra =
+ (struct ext2_inode_extra *) ((char *) di + EXT2_GOOD_OLD_INODE_SIZE);
+
+ /* Only decode if the inode actually uses the extra space (i_extra_isize)
+ The i_extra_isize tells us how many extra bytes are used in THIS
inode. */
+ if (le16toh (di_extra->i_extra_isize) >= 32) /* Enough room for all 3
extra timestamps */
+ {
+ ext2_decode_extra_time (le32toh (di->i_atime), le32toh
(di_extra->i_atime_extra),
+ &st->st_atim.tv_sec, &st->st_atim.tv_nsec);
+ ext2_decode_extra_time (le32toh (di->i_ctime), le32toh
(di_extra->i_ctime_extra),
+ &st->st_ctim.tv_sec, &st->st_ctim.tv_nsec);
+ ext2_decode_extra_time (le32toh (di->i_mtime), le32toh
(di_extra->i_mtime_extra),
+ &st->st_mtim.tv_sec, &st->st_mtim.tv_nsec);
+ info->i_atime_extra = le32toh (di_extra->i_atime_extra);
+ info->i_ctime_extra = le32toh (di_extra->i_ctime_extra);
+ info->i_mtime_extra = le32toh (di_extra->i_mtime_extra);
+ }
+ else
+ info->i_atime_extra = info->i_ctime_extra = info->i_mtime_extra = 0;
+ }
st->st_blocks = le32toh (di->i_blocks);
@@ -416,19 +454,25 @@ write_node (struct node *np)
di->i_links_count = htole16 (st->st_nlink);
di->i_atime = htole32(st->st_atim.tv_sec);
-#ifdef not_yet
- /* ``struct ext2_inode'' doesn't do better than sec. precision yet. */
- di->i_atime.tv_nsec = htole32 (st->st_atim.tv_nsec);
-#endif
di->i_mtime = htole32 (st->st_mtim.tv_sec);
-#ifdef not_yet
- di->i_mtime.tv_nsec = htole32 (st->st_mtim.tv_nsec);
-#endif
di->i_ctime = htole32 (st->st_ctim.tv_sec);
-#ifdef not_yet
- di->i_ctime.tv_nsec = htole32 (st->st_ctim.tv_nsec);
-#endif
-
+ if (ext2_has_extra_inodes (sblock))
+ {
+ struct ext2_inode_extra *di_extra =
+ (struct ext2_inode_extra *) ((char *) di +
EXT2_GOOD_OLD_INODE_SIZE);
+ if (le16toh (di_extra->i_extra_isize) < 32)
+ di_extra->i_extra_isize = htole16 (32);
+
+ di_extra->i_atime_extra = htole32 (ext2_encode_extra_time
(st->st_atim.tv_sec,
+
st->st_atim.tv_nsec));
+ di_extra->i_mtime_extra = htole32 (ext2_encode_extra_time
(st->st_mtim.tv_sec,
+
st->st_mtim.tv_nsec));
+ di_extra->i_ctime_extra = htole32 (ext2_encode_extra_time
(st->st_ctim.tv_sec,
+
st->st_ctim.tv_nsec));
+ info->i_atime_extra = le32toh (di_extra->i_atime_extra);
+ info->i_mtime_extra = le32toh (di_extra->i_mtime_extra);
+ info->i_ctime_extra = le32toh (di_extra->i_ctime_extra);
+ }
/* Convert generic flags in ST->st_flags to ext2-specific flags in DI
(but don't mess with ext2 flags we don't know about). The original
set was copied from DI into INFO by read_node, but might have been
--
2.52.0