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

Reply via email to