Hello, I have changed just a couple minor things and pushed, thanks!
Samuel Milos Nikic, le mar. 27 janv. 2026 09:38:39 -0800, a ecrit: > Hey Samuel, > > Here it is with these two things changed. > > I simplified this and added a comment: > /* > * We need enough space for ctime, mtime, atime and extra fields. > * i_crtime follows immediately after i_atime_extra, so its offset > * gives us the total size required for the timestamps we support. > */ > #define EXT2_INODE_EXTRA_TIME_SIZE offsetof (struct ext2_inode_extra, > i_crtime) > > Let me know if something else is needed. > > Thanks, > Milos > > > On Tue, Jan 27, 2026 at 12:13 AM Samuel Thibault <[1][email protected]> > wrote: > > Just a quick thought: > > Milos Nikic, le lun. 26 janv. 2026 23:06:01 -0800, a ecrit: > > index daa49543..6460db35 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) */ > > Thinking about it: since we increase the inode size to 32B, we have to > fill these fields: i_extra_isize and i_checksum_hi > > > + __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,10 +437,13 @@ 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 > > +#define EXT2_INODE_EXTENT_SIZE \ > > Put some "TIME" in the name so it's clear what size we are talking > about. We may have more fields to look at. > > > + (offsetof (struct ext2_inode_extra, i_mtime_extra) + \ > > + sizeof (((struct ext2_inode_extra *)0)->i_mtime_extra)) > > > > /* > > * Feature set definitions > > > References: > > [1] mailto:[email protected] > From a33cf00e3eac8867c1d6d77a33cd576bd12b5503 Mon Sep 17 00:00:00 2001 > From: Milos Nikic <[email protected]> > Date: Mon, 26 Jan 2026 22:32:02 -0800 > Subject: [PATCH] ext2: support 64 bit time > > 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 | 20 ++++++++++- > ext2fs/ext2fs.h | 9 ++--- > ext2fs/hyper.c | 11 ++++-- > ext2fs/ialloc.c | 2 +- > ext2fs/inode.c | 93 ++++++++++++++++++++++++++++++++++-------------- > 5 files changed, 101 insertions(+), 34 deletions(-) > > diff --git a/ext2fs/ext2_fs.h b/ext2fs/ext2_fs.h > index daa49543..195e9b6b 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,10 +437,16 @@ 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 > +/* > + * We need enough space for ctime, mtime, atime and extra fields. > + * i_crtime follows immediately after i_atime_extra, so its offset > + * gives us the total size required for the timestamps we support. > + */ > +#define EXT2_INODE_EXTRA_TIME_SIZE offsetof (struct ext2_inode_extra, > i_crtime) > > /* > * Feature set definitions > diff --git a/ext2fs/ext2fs.h b/ext2fs/ext2fs.h > index 62ee9f77..78a88a6a 100644 > --- a/ext2fs/ext2fs.h > +++ b/ext2fs/ext2fs.h > @@ -281,6 +281,8 @@ int disk_cache_block_is_ref (block_t block); > extern struct ext2_super_block *sblock; > /* True if sblock has been modified. */ > extern int sblock_dirty; > +/* Size of one inode. */ > +extern __u16 global_inode_size; > > /* Where the super-block is located on disk (at min-block 1). */ > #define SBLOCK_BLOCK 1 /* Default location, second 1k block. */ > @@ -428,10 +430,9 @@ dino_ref (ino_t inum) > 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; > - ext2_debug ("(%llu) = %p", inum, inode); > - return inode; > + void *block_ptr = disk_cache_block_ref (block); > + size_t offset = (group_inum % inodes_per_block) * global_inode_size; > + return (struct ext2_inode *)((char *)block_ptr + offset); > } > > EXT2FS_EI void > diff --git a/ext2fs/hyper.c b/ext2fs/hyper.c > index 2af7e870..9e135015 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"); > } > @@ -171,6 +172,7 @@ get_hypermetadata (void) > } > > static struct ext2_super_block *mapped_sblock; > +__u16 global_inode_size; > > void > map_hypermetadata (void) > @@ -181,6 +183,11 @@ map_hypermetadata (void) > These are stored in the filesystem blocks following the superblock. */ > group_desc_image = > (struct ext2_group_desc *) bptr (group_desc_block); > + > + global_inode_size = le16toh (sblock->s_inode_size); > + > + if (global_inode_size == 0) > + global_inode_size = EXT2_GOOD_OLD_INODE_SIZE; > } > > error_t > diff --git a/ext2fs/ialloc.c b/ext2fs/ialloc.c > index c2588fc4..46c86eaa 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); > } > > diff --git a/ext2fs/inode.c b/ext2fs/inode.c > index dc309ac8..8d1656f6 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,29 @@ 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) >= EXT2_INODE_EXTRA_TIME_SIZE) > + { > + 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); > + } > + } > > st->st_blocks = le32toh (di->i_blocks); > > @@ -416,19 +452,24 @@ 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) < EXT2_INODE_EXTRA_TIME_SIZE) > + di_extra->i_extra_isize = htole16 (EXT2_INODE_EXTRA_TIME_SIZE); > + > + di_extra->i_checksum_hi = 0; > + 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)); > + } > /* 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 > -- Samuel if (argc > 1 && strcmp(argv[1], "-advice") == 0) { printf("Don't Panic!\n"); exit(42); } -- Arnold Robbins in the LJ of February '95, describing RCS
