filesystemsunix-timestampext2year2038

How do ext2/3/4 filesystems deal with 64 bit time_t?


I am working on small ext2 filesystem image manipulation tool (like listing directories, adding and extracting files without the need to mount it). I've just came into a problem with Unix timestamp fields. They are all 32 bit in ext filesystems. As we know, 32 bit Unix timestamps won't work after year 2038 anymore. Most software can easily deal with this problem by just changing definition of time_t to 64 bit. But it is not that easy for filesystems. They need to be compatible with existing implementations yet they need to be updated from time to time. How exactly ext filesystems do that? Particularly fields like s_mtime, s_wtime, s_lastcheck, i_atime, i_ctime, i_mtime and i_dtime.


Solution

  • If you look at ext4's inode structure, you'll see:

    struct ext4_inode {
        ...
        __le32  i_ctime;    /* Inode Change time */
        ...
        __le32  i_ctime_extra;  /* extra Change time      (nsec << 2 | epoch) */
        ...
    };
    

    You won't find i_ctime_extra being used (*sigh*). Instead, you'll find:

    #define EXT4_INODE_GET_XTIME(xtime, inode, raw_inode)               \
    do {                                        \
        (inode)->xtime.tv_sec = (signed)le32_to_cpu((raw_inode)->xtime);    \
        if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra)) {    \
            ext4_decode_extra_time(&(inode)->xtime,             \
                           raw_inode->xtime ## _extra);     \
            }                               \
        else                                    \
            (inode)->xtime.tv_nsec = 0;                 \
    } while (0)
    
    
    #define EXT4_EINODE_GET_XTIME(xtime, einode, raw_inode)                \
    do {                                           \
        if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime))              \
            (einode)->xtime.tv_sec =                       \
                (signed)le32_to_cpu((raw_inode)->xtime);           \
        else                                       \
            (einode)->xtime.tv_sec = 0;                    \
        if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime ## _extra))        \
            ext4_decode_extra_time(&(einode)->xtime,               \
                           raw_inode->xtime ## _extra);        \
        else                                       \
            (einode)->xtime.tv_nsec = 0;                       \
    } while (0)
    

    And if you look at usages, you'll see that in-memory, a 64 bit integer is used, the separation only exists on disk.