2023-09-27 06:00:08

by Andreas Dilger

[permalink] [raw]
Subject: [PATCH] misc: add 2038 timestamp support

The ext4 kernel code implemented support for s_mtime_hi,
s_wtime_hi, and related timestamp fields to avoid timestamp
overflow in 2038, but similar handling is not in e2fsprogs.

Add helper macros for the superblock _hi timestamp fields
ext2fs_super_tstamp_get() and ext2fs_super_tstamp_set().

Add helper macro for inode _extra timestamp fields
ext2fs_inode_xtime_get() and ext2fs_inode_xtime_set().

Add helper macro ext2fs_actual_inode_size() to avoid open
coding the i_extra_isize check in multiple places.

Remove inode_time_to_string() since this is unused once callers
change to time_to_string(ext2fs_inode_xtime_get()) directly.

Fix inode_includes() macro to properly wrap "inode" parameter,
and rename to ext2fs_inode_includes() to avoid potential name
clashes. Use this to check inode field inclusion in debugfs
instead of bare constants for inode field offsets.

Use these interfaces to access timestamps in debugfs, e2fsck,
libext2fs, fuse2fs, tune2fs, and e2undo.

Signed-off-by: Andreas Dilger <[email protected]>
---
debugfs/debugfs.c | 69 +++++++++++++++++++++++------------------
debugfs/debugfs.h | 1 -
debugfs/journal.c | 7 +++--
debugfs/set_fields.c | 32 ++++++++++++-------
debugfs/util.c | 8 -----
e2fsck/message.c | 2 +-
e2fsck/pass1.c | 16 +++++-----
e2fsck/pass3.c | 8 +++--
e2fsck/super.c | 16 +++++-----
e2fsck/unix.c | 2 +-
lib/e2p/ls.c | 28 +++++++----------
lib/ext2fs/bb_inode.c | 11 ++++---
lib/ext2fs/closefs.c | 2 +-
lib/ext2fs/ext2_fs.h | 6 ++--
lib/ext2fs/ext2fs.h | 52 +++++++++++++++++++++++++++++++
lib/ext2fs/initialize.c | 3 +-
lib/ext2fs/inode.c | 16 +++++-----
lib/ext2fs/mkjournal.c | 5 ++-
lib/ext2fs/orphan.c | 7 +++--
lib/ext2fs/res_gdt.c | 5 ++-
lib/ext2fs/swapfs.c | 16 +++++-----
lib/support/mkquota.c | 2 +-
lib/support/plausible.c | 9 ++----
lib/support/quotaio.c | 7 +++--
misc/create_inode.c | 20 +++++++-----
misc/e2undo.c | 6 ++--
misc/findsuper.c | 7 ++---
misc/fuse2fs.c | 8 ++---
misc/tune2fs.c | 6 ++--
29 files changed, 231 insertions(+), 146 deletions(-)

diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index 9b6321dc..96551173 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -831,11 +831,13 @@ void internal_dump_inode(FILE *out, const char *prefix,
char frag, fsize;
int os = current_fs->super->s_creator_os;
struct ext2_inode_large *large_inode;
- int is_large_inode = 0;
+ size_t inode_size;

- if (EXT2_INODE_SIZE(current_fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
- is_large_inode = 1;
large_inode = (struct ext2_inode_large *) inode;
+ if (EXT2_INODE_SIZE(current_fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
+ inode_size = ext2fs_inode_actual_size(large_inode);
+ else
+ inode_size = EXT2_GOOD_OLD_INODE_SIZE;

if (LINUX_S_ISDIR(inode->i_mode)) i_type = "directory";
else if (LINUX_S_ISREG(inode->i_mode)) i_type = "regular";
@@ -848,7 +850,7 @@ void internal_dump_inode(FILE *out, const char *prefix,
fprintf(out, "%sInode: %u Type: %s ", prefix, inode_num, i_type);
fprintf(out, "%sMode: 0%03o Flags: 0x%x\n",
prefix, inode->i_mode & 07777, inode->i_flags);
- if (is_large_inode && large_inode->i_extra_isize >= 24) {
+ if (ext2fs_inode_includes(inode_size, i_version_hi)) {
fprintf(out, "%sGeneration: %u Version: 0x%08x:%08x\n",
prefix, inode->i_generation, large_inode->i_version_hi,
inode->osd1.linux1.l_i_version);
@@ -858,7 +860,7 @@ void internal_dump_inode(FILE *out, const char *prefix,
}
fprintf(out, "%sUser: %5d Group: %5d",
prefix, inode_uid(*inode), inode_gid(*inode));
- if (is_large_inode && large_inode->i_extra_isize >= 32)
+ if (ext2fs_inode_includes(inode_size, i_projid))
fprintf(out, " Project: %5d", large_inode->i_projid);
fputs(" Size: ", out);
if (LINUX_S_ISREG(inode->i_mode) || LINUX_S_ISDIR(inode->i_mode))
@@ -895,39 +897,48 @@ void internal_dump_inode(FILE *out, const char *prefix,
}
fprintf(out, "%sFragment: Address: %u Number: %u Size: %u\n",
prefix, inode->i_faddr, frag, fsize);
- if (is_large_inode && large_inode->i_extra_isize >= 24) {
+ if (ext2fs_inode_includes(inode_size, i_ctime_extra))
fprintf(out, "%s ctime: 0x%08x:%08x -- %s", prefix,
inode->i_ctime, large_inode->i_ctime_extra,
- inode_time_to_string(inode->i_ctime,
- large_inode->i_ctime_extra));
+ time_to_string(ext2fs_inode_xtime_get(inode, i_ctime)));
+ else
+ fprintf(out, "%sctime: 0x%08x -- %s", prefix, inode->i_ctime,
+ time_to_string((__s32) inode->i_ctime));
+ if (ext2fs_inode_includes(inode_size, i_atime_extra))
fprintf(out, "%s atime: 0x%08x:%08x -- %s", prefix,
inode->i_atime, large_inode->i_atime_extra,
- inode_time_to_string(inode->i_atime,
- large_inode->i_atime_extra));
+ time_to_string(ext2fs_inode_xtime_get(inode, i_atime)));
+ else
+ fprintf(out, "%satime: 0x%08x -- %s", prefix, inode->i_atime,
+ time_to_string((__s32) inode->i_atime));
+ if (ext2fs_inode_includes(inode_size, i_mtime_extra))
fprintf(out, "%s mtime: 0x%08x:%08x -- %s", prefix,
inode->i_mtime, large_inode->i_mtime_extra,
- inode_time_to_string(inode->i_mtime,
- large_inode->i_mtime_extra));
+ time_to_string(ext2fs_inode_xtime_get(inode, i_mtime)));
+ else
+ fprintf(out, "%smtime: 0x%08x -- %s", prefix, inode->i_mtime,
+ time_to_string((__s32) inode->i_mtime));
+ if (ext2fs_inode_includes(inode_size, i_crtime_extra))
fprintf(out, "%scrtime: 0x%08x:%08x -- %s", prefix,
large_inode->i_crtime, large_inode->i_crtime_extra,
- inode_time_to_string(large_inode->i_crtime,
- large_inode->i_crtime_extra));
- if (inode->i_dtime)
+ time_to_string(ext2fs_inode_xtime_get(large_inode,
+ i_crtime)));
+ if (inode->i_dtime) {
+ if (ext2fs_inode_includes(inode_size, i_ctime_extra)) {
+ time_t tm;
+
+ /* dtime doesn't have its own i_dtime_extra field, so
+ * approximate this with i_ctime_extra instead. */
+ tm = __decode_extra_sec(inode->i_dtime,
+ large_inode->i_ctime_extra);
fprintf(out, "%s dtime: 0x%08x:(%08x) -- %s", prefix,
- large_inode->i_dtime, large_inode->i_ctime_extra,
- inode_time_to_string(inode->i_dtime,
- large_inode->i_ctime_extra));
- } else {
- fprintf(out, "%sctime: 0x%08x -- %s", prefix, inode->i_ctime,
- time_to_string((__s32) inode->i_ctime));
- fprintf(out, "%satime: 0x%08x -- %s", prefix, inode->i_atime,
- time_to_string((__s32) inode->i_atime));
- fprintf(out, "%smtime: 0x%08x -- %s", prefix, inode->i_mtime,
- time_to_string((__s32) inode->i_mtime));
- if (inode->i_dtime)
+ inode->i_dtime, large_inode->i_ctime_extra,
+ time_to_string(tm));
+ } else {
fprintf(out, "%sdtime: 0x%08x -- %s", prefix,
inode->i_dtime,
time_to_string((__s32) inode->i_dtime));
+ }
}
if (EXT2_INODE_SIZE(current_fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
internal_dump_inode_extra(out, prefix, inode_num,
@@ -935,11 +946,7 @@ void internal_dump_inode(FILE *out, const char *prefix,
dump_inode_attributes(out, inode_num);
if (ext2fs_has_feature_metadata_csum(current_fs->super)) {
__u32 crc = inode->i_checksum_lo;
- if (is_large_inode &&
- large_inode->i_extra_isize >=
- (offsetof(struct ext2_inode_large,
- i_checksum_hi) -
- EXT2_GOOD_OLD_INODE_SIZE))
+ if (ext2fs_inode_includes(inode_size, i_checksum_hi))
crc |= ((__u32)large_inode->i_checksum_hi) << 16;
fprintf(out, "Inode checksum: 0x%08x\n", crc);
}
diff --git a/debugfs/debugfs.h b/debugfs/debugfs.h
index 39bc0247..85c82b95 100644
--- a/debugfs/debugfs.h
+++ b/debugfs/debugfs.h
@@ -36,7 +36,6 @@ extern int check_fs_not_open(char *name);
extern int check_fs_read_write(char *name);
extern int check_fs_bitmaps(char *name);
extern ext2_ino_t string_to_inode(char *str);
-extern char *inode_time_to_string(__u32 xtime, __u32 xtime_extra);
extern char *time_to_string(__s64);
extern __s64 string_to_time(const char *);
extern unsigned long parse_ulong(const char *str, const char *cmd,
diff --git a/debugfs/journal.c b/debugfs/journal.c
index 5bac0d3b..454fbcfc 100644
--- a/debugfs/journal.c
+++ b/debugfs/journal.c
@@ -245,6 +245,8 @@ void wait_on_buffer(struct buffer_head *bh)

static void ext2fs_clear_recover(ext2_filsys fs, int error)
{
+ time_t s_mtime;
+
ext2fs_clear_feature_journal_needs_recovery(fs->super);

/* if we had an error doing journal recovery, we need a full fsck */
@@ -254,8 +256,9 @@ static void ext2fs_clear_recover(ext2_filsys fs, int error)
* If we replayed the journal by definition the file system
* was mounted since the last time it was checked
*/
- if (fs->super->s_lastcheck >= fs->super->s_mtime)
- fs->super->s_lastcheck = fs->super->s_mtime - 1;
+ s_mtime = ext2fs_get_tstamp(fs->super, s_mtime);
+ if (ext2fs_get_tstamp(fs->super, s_lastcheck) >= s_mtime)
+ ext2fs_set_tstamp(fs->super, s_lastcheck, s_mtime - 1);
ext2fs_mark_super_dirty(fs);
}

diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
index f916deab..ef137b0f 100644
--- a/debugfs/set_fields.c
+++ b/debugfs/set_fields.c
@@ -99,15 +99,16 @@ static struct field_set_info super_fields[] = {
{ "blocks_per_group", &set_sb.s_blocks_per_group, NULL, 4, parse_uint },
{ "clusters_per_group", &set_sb.s_clusters_per_group, NULL, 4, parse_uint },
{ "inodes_per_group", &set_sb.s_inodes_per_group, NULL, 4, parse_uint },
- { "mtime", &set_sb.s_mtime, NULL, 4, parse_time },
- { "wtime", &set_sb.s_wtime, NULL, 4, parse_time },
+ { "mtime", &set_sb.s_mtime, &set_sb.s_mtime_hi, 5, parse_time },
+ { "wtime", &set_sb.s_wtime, &set_sb.s_wtime_hi, 5, parse_time },
{ "mnt_count", &set_sb.s_mnt_count, NULL, 2, parse_uint },
{ "max_mnt_count", &set_sb.s_max_mnt_count, NULL, 2, parse_int },
/* s_magic */
{ "state", &set_sb.s_state, NULL, 2, parse_uint },
{ "errors", &set_sb.s_errors, NULL, 2, parse_uint },
{ "minor_rev_level", &set_sb.s_minor_rev_level, NULL, 2, parse_uint },
- { "lastcheck", &set_sb.s_lastcheck, NULL, 4, parse_time },
+ { "lastcheck", &set_sb.s_lastcheck, &set_sb.s_lastcheck_hi, 5,
+ parse_time },
{ "checkinterval", &set_sb.s_checkinterval, NULL, 4, parse_uint },
{ "creator_os", &set_sb.s_creator_os, NULL, 4, parse_uint },
{ "rev_level", &set_sb.s_rev_level, NULL, 4, parse_uint },
@@ -139,7 +140,8 @@ static struct field_set_info super_fields[] = {
{ "desc_size", &set_sb.s_desc_size, NULL, 2, parse_uint },
{ "default_mount_opts", &set_sb.s_default_mount_opts, NULL, 4, parse_uint },
{ "first_meta_bg", &set_sb.s_first_meta_bg, NULL, 4, parse_uint },
- { "mkfs_time", &set_sb.s_mkfs_time, NULL, 4, parse_time },
+ { "mkfs_time", &set_sb.s_mkfs_time, &set_sb.s_mkfs_time_hi, 5,
+ parse_time },
{ "jnl_blocks", &set_sb.s_jnl_blocks[0], NULL, 4, parse_uint, FLAG_ARRAY,
17 },
{ "min_extra_isize", &set_sb.s_min_extra_isize, NULL, 2, parse_uint },
@@ -167,12 +169,14 @@ static struct field_set_info super_fields[] = {
{ "checksum_type", &set_sb.s_checksum_type, NULL, 1, parse_uint },
{ "encryption_level", &set_sb.s_encryption_level, NULL, 1, parse_uint },
{ "error_count", &set_sb.s_error_count, NULL, 4, parse_uint },
- { "first_error_time", &set_sb.s_first_error_time, NULL, 4, parse_time },
+ { "first_error_time", &set_sb.s_first_error_time,
+ &set_sb.s_first_error_time_hi, 5, parse_time },
{ "first_error_ino", &set_sb.s_first_error_ino, NULL, 4, parse_uint },
{ "first_error_block", &set_sb.s_first_error_block, NULL, 8, parse_uint },
{ "first_error_func", &set_sb.s_first_error_func, NULL, 32, parse_string },
{ "first_error_line", &set_sb.s_first_error_line, NULL, 4, parse_uint },
- { "last_error_time", &set_sb.s_last_error_time, NULL, 4, parse_time },
+ { "last_error_time", &set_sb.s_last_error_time,
+ &set_sb.s_last_error_time_hi, 5, parse_time },
{ "last_error_ino", &set_sb.s_last_error_ino, NULL, 4, parse_uint },
{ "last_error_block", &set_sb.s_last_error_block, NULL, 8, parse_uint },
{ "last_error_func", &set_sb.s_last_error_func, NULL, 32, parse_string },
@@ -441,6 +445,9 @@ static struct field_set_info *find_field(struct field_set_info *fields,
* Note: info->size == 6 is special; this means a base size 4 bytes,
* and secondary (high) size of 2 bytes. This is needed for the
* special case of i_blocks_high and i_file_acl_high.
+ *
+ * Similarly, info->size == 5 is for superblock timestamps, which have
+ * a 4-byte primary field and a 1-byte _hi field.
*/
static errcode_t parse_uint(struct field_set_info *info, char *field,
char *arg)
@@ -449,7 +456,7 @@ static errcode_t parse_uint(struct field_set_info *info, char *field,
int suffix = check_suffix(field);
char *tmp;
void *field1 = info->ptr, *field2 = info->ptr2;
- int size = (info->size == 6) ? 4 : info->size;
+ int size = (info->size == 6 || info->size == 5) ? 4 : info->size;
union {
__u64 *ptr64;
__u32 *ptr32;
@@ -477,7 +484,7 @@ static errcode_t parse_uint(struct field_set_info *info, char *field,
}
mask = ~0ULL >> ((8 - size) * 8);
limit = ~0ULL >> ((8 - info->size) * 8);
- if (field2 && info->size != 6)
+ if (field2 && (info->size != 6 || info->size != 5))
limit = ~0ULL >> ((8 - info->size*2) * 8);

if (num > limit) {
@@ -504,13 +511,14 @@ static errcode_t parse_uint(struct field_set_info *info, char *field,
return 0;
n = (size == 8) ? 0 : (num >> (size*8));
u.ptr8 = (__u8 *) field2;
- if (info->size == 6)
- size = 2;
+ if (info->size > size)
+ size = info->size - size;
switch (size) {
case 8:
/* Should never get here */
- fprintf(stderr, "64-bit field %s has a second 64-bit field\n"
- "defined; BUG?!?\n", info->name);
+ fprintf(stderr,
+ "64-bit field %s has a second 64-bit field defined; BUG?!?\n",
+ info->name);
*u.ptr64 = 0;
break;
case 4:
diff --git a/debugfs/util.c b/debugfs/util.c
index 9e880548..d3ef63c6 100644
--- a/debugfs/util.c
+++ b/debugfs/util.c
@@ -191,14 +191,6 @@ int check_fs_bitmaps(char *name)
return 0;
}

-char *inode_time_to_string(__u32 xtime, __u32 xtime_extra)
-{
- __s64 t = (__s32) xtime;
-
- t += (__s64) (xtime_extra & EXT4_EPOCH_MASK) << 32;
- return time_to_string(t);
-}
-
/*
* This function takes a __s64 time value and converts it to a string,
* using ctime
diff --git a/e2fsck/message.c b/e2fsck/message.c
index ba38038c..9c42b13f 100644
--- a/e2fsck/message.c
+++ b/e2fsck/message.c
@@ -301,7 +301,7 @@ static _INLINE_ void expand_inode_expression(FILE *f, ext2_filsys fs, char ch,
fprintf(f, "0%o", inode->i_mode);
break;
case 'M':
- print_time(f, inode->i_mtime);
+ print_time(f, ext2fs_inode_xtime_get(inode, i_mtime));
break;
case 'F':
fprintf(f, "%u", inode->i_faddr);
diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
index a341c72a..078bcb9b 100644
--- a/e2fsck/pass1.c
+++ b/e2fsck/pass1.c
@@ -1181,6 +1181,7 @@ void e2fsck_pass1(e2fsck_t ctx)
ext2_ino_t ino_threshold = 0;
dgrp_t ra_group = 0;
struct ea_quota ea_ibody_quota;
+ time_t tm;

init_resource_track(&rtrack, ctx->fs->io);
clear_problem_context(&pctx);
@@ -1357,12 +1358,13 @@ void e2fsck_pass1(e2fsck_t ctx)
if (ctx->progress && ((ctx->progress)(ctx, 1, 0,
ctx->fs->group_desc_count)))
goto endit;
- if ((fs->super->s_wtime &&
- fs->super->s_wtime < fs->super->s_inodes_count) ||
- (fs->super->s_mtime &&
- fs->super->s_mtime < fs->super->s_inodes_count) ||
- (fs->super->s_mkfs_time &&
- fs->super->s_mkfs_time < fs->super->s_inodes_count))
+
+ if (((tm = ext2fs_get_tstamp(fs->super, s_wtime)) &&
+ tm < fs->super->s_inodes_count) ||
+ ((tm = ext2fs_get_tstamp(fs->super, s_mtime)) &&
+ tm < fs->super->s_inodes_count) ||
+ ((tm = ext2fs_get_tstamp(fs->super, s_mkfs_time)) &&
+ tm < fs->super->s_inodes_count))
low_dtime_check = 0;

if (ext2fs_has_feature_mmp(fs->super) &&
@@ -2076,7 +2078,7 @@ void e2fsck_pass1(e2fsck_t ctx)
if (!pctx.errcode) {
e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode,
"recreate inode");
- inode->i_mtime = ctx->now;
+ ext2fs_inode_xtime_set(inode, i_mtime, ctx->now);
e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode,
"recreate inode");
}
diff --git a/e2fsck/pass3.c b/e2fsck/pass3.c
index 16d243f6..ba794165 100644
--- a/e2fsck/pass3.c
+++ b/e2fsck/pass3.c
@@ -212,7 +212,9 @@ skip_new_block:
memset(&inode, 0, sizeof(inode));
inode.i_mode = 040755;
inode.i_size = fs->blocksize;
- inode.i_atime = inode.i_ctime = inode.i_mtime = ctx->now;
+ ext2fs_inode_xtime_set(&inode, i_atime, ctx->now);
+ ext2fs_inode_xtime_set(&inode, i_ctime, ctx->now);
+ ext2fs_inode_xtime_set(&inode, i_mtime, ctx->now);
inode.i_links_count = 2;
ext2fs_iblk_set(fs, iptr, 1);
inode.i_block[0] = blk;
@@ -528,7 +530,9 @@ skip_new_block:
memset(&inode, 0, sizeof(inode));
inode.i_mode = 040700;
inode.i_size = fs->blocksize;
- inode.i_atime = inode.i_ctime = inode.i_mtime = ctx->now;
+ ext2fs_inode_xtime_set(&inode, i_atime, ctx->now);
+ ext2fs_inode_xtime_set(&inode, i_ctime, ctx->now);
+ ext2fs_inode_xtime_set(&inode, i_mtime, ctx->now);
inode.i_links_count = 2;
ext2fs_iblk_set(fs, EXT2_INODE(&inode), 1);
inode.i_block[0] = blk;
diff --git a/e2fsck/super.c b/e2fsck/super.c
index be40dd8f..757a475d 100644
--- a/e2fsck/super.c
+++ b/e2fsck/super.c
@@ -1320,25 +1320,25 @@ void check_super_block(e2fsck_t ctx)
*/
if (((ctx->options & E2F_OPT_FORCE) || fs->super->s_checkinterval) &&
!broken_system_clock && !(ctx->flags & E2F_FLAG_TIME_INSANE) &&
- (fs->super->s_mtime > (__u32) ctx->now)) {
- pctx.num = fs->super->s_mtime;
+ (ext2fs_get_tstamp(fs->super, s_mtime) > ctx->now)) {
+ pctx.num = ext2fs_get_tstamp(fs->super, s_mtime);
problem = PR_0_FUTURE_SB_LAST_MOUNT;
- if (fs->super->s_mtime <= (__u32) ctx->now + ctx->time_fudge)
+ if (pctx.num <= ctx->now + ctx->time_fudge)
problem = PR_0_FUTURE_SB_LAST_MOUNT_FUDGED;
if (fix_problem(ctx, problem, &pctx)) {
- fs->super->s_mtime = ctx->now;
+ ext2fs_set_tstamp(fs->super, s_mtime, ctx->now);
fs->flags |= EXT2_FLAG_DIRTY;
}
}
if (((ctx->options & E2F_OPT_FORCE) || fs->super->s_checkinterval) &&
!broken_system_clock && !(ctx->flags & E2F_FLAG_TIME_INSANE) &&
- (fs->super->s_wtime > (__u32) ctx->now)) {
- pctx.num = fs->super->s_wtime;
+ (ext2fs_get_tstamp(fs->super, s_wtime) > ctx->now)) {
+ pctx.num = ext2fs_get_tstamp(fs->super, s_wtime);
problem = PR_0_FUTURE_SB_LAST_WRITE;
- if (fs->super->s_wtime <= (__u32) ctx->now + ctx->time_fudge)
+ if (pctx.num <= ctx->now + ctx->time_fudge)
problem = PR_0_FUTURE_SB_LAST_WRITE_FUDGED;
if (fix_problem(ctx, problem, &pctx)) {
- fs->super->s_wtime = ctx->now;
+ ext2fs_set_tstamp(fs->super, s_wtime, ctx->now);
fs->flags |= EXT2_FLAG_DIRTY;
}
}
diff --git a/e2fsck/unix.c b/e2fsck/unix.c
index e5b672a2..bc6b518d 100644
--- a/e2fsck/unix.c
+++ b/e2fsck/unix.c
@@ -2080,7 +2080,7 @@ cleanup:
} else
sb->s_state &= ~EXT2_VALID_FS;
if (!(ctx->flags & E2F_FLAG_TIME_INSANE))
- sb->s_lastcheck = ctx->now;
+ ext2fs_set_tstamp(sb, s_lastcheck, ctx->now);
sb->s_mnt_count = 0;
memset(((char *) sb) + EXT4_S_ERR_START, 0, EXT4_S_ERR_LEN);
pctx.errcode = ext2fs_set_gdt_csum(ctx->fs);
diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
index 0b74aea2..081ef975 100644
--- a/lib/e2p/ls.c
+++ b/lib/e2p/ls.c
@@ -313,27 +313,23 @@ void list_super2(struct ext2_super_block * sb, FILE *f)
if (sb->s_log_groups_per_flex)
fprintf(f, "Flex block group size: %u\n",
1U << sb->s_log_groups_per_flex);
- if (sb->s_mkfs_time) {
- tm = sb->s_mkfs_time;
+ tm = ext2fs_get_tstamp(sb, s_mkfs_time);
+ if (tm)
fprintf(f, "Filesystem created: %s", ctime(&tm));
- }
- tm = sb->s_mtime;
- fprintf(f, "Last mount time: %s",
- sb->s_mtime ? ctime(&tm) : "n/a\n");
- tm = sb->s_wtime;
+ tm = ext2fs_get_tstamp(sb, s_mtime);
+ fprintf(f, "Last mount time: %s", tm ? ctime(&tm) : "n/a\n");
+ tm = ext2fs_get_tstamp(sb, s_wtime);
fprintf(f, "Last write time: %s", ctime(&tm));
fprintf(f, "Mount count: %u\n", sb->s_mnt_count);
fprintf(f, "Maximum mount count: %d\n", sb->s_max_mnt_count);
- tm = sb->s_lastcheck;
+ tm = ext2fs_get_tstamp(sb, s_lastcheck);
fprintf(f, "Last checked: %s", ctime(&tm));
fprintf(f, "Check interval: %u (%s)\n", sb->s_checkinterval,
interval_string(sb->s_checkinterval));
if (sb->s_checkinterval)
{
- time_t next;
-
- next = sb->s_lastcheck + sb->s_checkinterval;
- fprintf(f, "Next check after: %s", ctime(&next));
+ tm += sb->s_checkinterval;
+ fprintf(f, "Next check after: %s", ctime(&tm));
}
#define POW2(x) ((__u64) 1 << (x))
if (sb->s_kbytes_written) {
@@ -419,8 +415,8 @@ void list_super2(struct ext2_super_block * sb, FILE *f)
if (sb->s_error_count)
fprintf(f, "FS Error count: %u\n",
sb->s_error_count);
- if (sb->s_first_error_time) {
- tm = sb->s_first_error_time;
+ tm = ext2fs_get_tstamp(sb, s_first_error_time);
+ if (tm) {
fprintf(f, "First error time: %s", ctime(&tm));
fprintf(f, "First error function: %.*s\n",
EXT2_LEN_STR(sb->s_first_error_func));
@@ -436,8 +432,8 @@ void list_super2(struct ext2_super_block * sb, FILE *f)
fprintf(f, "First error err: %s\n",
e2p_errcode2str(sb->s_first_error_errcode));
}
- if (sb->s_last_error_time) {
- tm = sb->s_last_error_time;
+ tm = ext2fs_get_tstamp(sb, s_last_error_time);
+ if (tm) {
fprintf(f, "Last error time: %s", ctime(&tm));
fprintf(f, "Last error function: %.*s\n",
EXT2_LEN_STR(sb->s_last_error_func));
diff --git a/lib/ext2fs/bb_inode.c b/lib/ext2fs/bb_inode.c
index 11f10ebc..927a4d41 100644
--- a/lib/ext2fs/bb_inode.c
+++ b/lib/ext2fs/bb_inode.c
@@ -58,8 +58,9 @@ static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list)
{
errcode_t retval;
- struct set_badblock_record rec;
+ struct set_badblock_record rec;
struct ext2_inode inode;
+ time_t now;

EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);

@@ -124,9 +125,11 @@ errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list)
if (retval)
goto cleanup;

- inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(0);
- if (!inode.i_ctime)
- inode.i_ctime = fs->now ? fs->now : time(0);
+ now = fs->now ? fs->now : time(0);
+ ext2fs_inode_xtime_set(&inode, i_atime, now);
+ if (!ext2fs_inode_xtime_get(&inode, i_ctime))
+ ext2fs_inode_xtime_set(&inode, i_ctime, now);
+ ext2fs_inode_xtime_set(&inode, i_mtime, now);
ext2fs_iblk_set(fs, &inode, rec.bad_block_count);
retval = ext2fs_inode_size_set(fs, &inode,
rec.bad_block_count * fs->blocksize);
diff --git a/lib/ext2fs/closefs.c b/lib/ext2fs/closefs.c
index 69cbdd8c..42bda1fa 100644
--- a/lib/ext2fs/closefs.c
+++ b/lib/ext2fs/closefs.c
@@ -301,7 +301,7 @@ errcode_t ext2fs_flush2(ext2_filsys fs, int flags)
fs_state = fs->super->s_state;
feature_incompat = fs->super->s_feature_incompat;

- fs->super->s_wtime = fs->now ? fs->now : time(NULL);
+ ext2fs_set_tstamp(fs->super, s_wtime, fs->now ? fs->now : time(NULL));
fs->super->s_block_group_nr = 0;

/*
diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
index 0fc9c09a..586141f8 100644
--- a/lib/ext2fs/ext2_fs.h
+++ b/lib/ext2fs/ext2_fs.h
@@ -512,9 +512,9 @@ struct ext2_inode_large {

#define i_checksum_lo osd2.linux2.l_i_checksum_lo

-#define inode_includes(size, field) \
- (size >= (sizeof(((struct ext2_inode_large *)0)->field) + \
- offsetof(struct ext2_inode_large, field)))
+#define ext2fs_inode_includes(size, field) \
+ ((size) >= (sizeof(((struct ext2_inode_large *)0)->field) + \
+ offsetof(struct ext2_inode_large, field)))

#if defined(__KERNEL__) || defined(__linux__)
#define i_reserved1 osd1.linux1.l_i_reserved1
diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
index 72c60d2b..8953817c 100644
--- a/lib/ext2fs/ext2fs.h
+++ b/lib/ext2fs/ext2fs.h
@@ -579,6 +579,58 @@ typedef struct ext2_struct_inode_scan *ext2_inode_scan;
*/
#define EXT2_I_SIZE(i) ((i)->i_size | ((__u64) (i)->i_size_high << 32))

+static inline __u32 __encode_extra_time(time_t seconds, __u32 nsec)
+{
+ __u32 extra = ((seconds - (__s32)seconds) >> 32) & EXT4_EPOCH_MASK;
+ return extra | (nsec << EXT4_EPOCH_BITS);
+}
+static inline time_t __decode_extra_sec(time_t seconds, __u32 extra)
+{
+ if (extra & EXT4_EPOCH_MASK)
+ seconds += ((time_t)(extra & EXT4_EPOCH_MASK) << 32);
+ return seconds;
+}
+static inline __u32 __decode_extra_nsec(__u32 extra)
+{
+ return (extra & EXT4_NSEC_MASK) >> EXT4_EPOCH_BITS;
+}
+#define ext2fs_inode_actual_size(inode) \
+ (EXT2_GOOD_OLD_INODE_SIZE + \
+ (sizeof(*inode) > EXT2_GOOD_OLD_INODE_SIZE ? \
+ ((struct ext2_inode_large *)(inode))->i_extra_isize : 0))
+#define clamp(val, min, max) ((val) < (min) ? (min) : ((val) > (max) ? \
+ (max) : (val)))
+#define ext2fs_inode_xtime_set(inode, field, sec) \
+do { \
+ if (ext2fs_inode_includes(ext2fs_inode_actual_size(inode), \
+ field ## _extra)) { \
+ (inode)->field = (__s32)sec; \
+ ((struct ext2_inode_large *)(inode))->field ## _extra = \
+ __encode_extra_time(sec, 0); \
+ } else { \
+ (inode)->field = clamp(sec, INT32_MIN, INT32_MAX); \
+ } \
+} while (0)
+#define ext2fs_inode_xtime_get(inode, field) \
+(ext2fs_inode_includes(ext2fs_inode_actual_size(inode), field ## _extra) ? \
+ __decode_extra_sec((inode)->field, \
+ ((struct ext2_inode_large *)(inode))->field ## _extra) : \
+ (time_t)(inode)->field)
+
+static inline void __sb_set_tstamp(__u32 *lo, __u8 *hi, time_t seconds)
+{
+ *lo = seconds & 0xffffffff;
+ *hi = seconds >> 32;
+}
+static inline time_t __sb_get_tstamp(__u32 *lo, __u8 *hi)
+{
+ return ((time_t)(*hi) << 32) | *lo;
+}
+#define ext2fs_set_tstamp(sb, field, seconds) \
+ __sb_set_tstamp(&(sb)->field, &(sb)->field ## _hi, seconds)
+#define ext2fs_get_tstamp(sb, field) \
+ __sb_get_tstamp(&(sb)->field, &(sb)->field ## _hi)
+
/*
* ext2_icount_t abstraction
*/
diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c
index edd692bb..32467a29 100644
--- a/lib/ext2fs/initialize.c
+++ b/lib/ext2fs/initialize.c
@@ -218,7 +218,8 @@ errcode_t ext2fs_initialize(const char *name, int flags,
}

set_field(s_checkinterval, 0);
- super->s_mkfs_time = super->s_lastcheck = fs->now ? fs->now : time(NULL);
+ ext2fs_set_tstamp(super, s_mkfs_time, fs->now ? fs->now : time(NULL));
+ ext2fs_set_tstamp(super, s_lastcheck, fs->now ? fs->now : time(NULL));

super->s_creator_os = CREATOR_OS;

diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
index 957d5aa9..8686f99c 100644
--- a/lib/ext2fs/inode.c
+++ b/lib/ext2fs/inode.c
@@ -1039,17 +1039,17 @@ errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino,
struct ext2_inode *inode)
{
struct ext2_inode *buf;
- int size = EXT2_INODE_SIZE(fs->super);
+ int size = EXT2_INODE_SIZE(fs->super);
struct ext2_inode_large *large_inode;
errcode_t retval;
- __u32 t = fs->now ? fs->now : time(NULL);
+ time_t t = fs->now ? fs->now : time(NULL);

- if (!inode->i_ctime)
- inode->i_ctime = t;
- if (!inode->i_mtime)
- inode->i_mtime = t;
- if (!inode->i_atime)
- inode->i_atime = t;
+ if (!ext2fs_inode_xtime_get(inode, i_atime))
+ ext2fs_inode_xtime_set(inode, i_atime, t);
+ if (!ext2fs_inode_xtime_get(inode, i_ctime))
+ ext2fs_inode_xtime_set(inode, i_ctime, t);
+ if (!ext2fs_inode_xtime_get(inode, i_mtime))
+ ext2fs_inode_xtime_set(inode, i_mtime, t);

if (size == sizeof(struct ext2_inode))
return ext2fs_write_inode_full(fs, ino, inode,
diff --git a/lib/ext2fs/mkjournal.c b/lib/ext2fs/mkjournal.c
index 54772dd5..4a947b61 100644
--- a/lib/ext2fs/mkjournal.c
+++ b/lib/ext2fs/mkjournal.c
@@ -285,6 +285,7 @@ static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino,
unsigned long long inode_size;
int falloc_flags = EXT2_FALLOCATE_FORCE_INIT;
blk64_t zblk;
+ time_t now;

if ((retval = ext2fs_create_journal_superblock2(fs, jparams, flags,
&buf)))
@@ -312,7 +313,9 @@ static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino,

inode_size = (unsigned long long)fs->blocksize *
(jparams->num_journal_blocks + jparams->num_fc_blocks);
- inode.i_mtime = inode.i_ctime = fs->now ? fs->now : time(0);
+ now = fs->now ? fs->now : time(0);
+ ext2fs_inode_xtime_set(&inode, i_mtime, now);
+ ext2fs_inode_xtime_set(&inode, i_ctime, now);
inode.i_links_count = 1;
inode.i_mode = LINUX_S_IFREG | 0600;
retval = ext2fs_inode_size_set(fs, &inode, inode_size);
diff --git a/lib/ext2fs/orphan.c b/lib/ext2fs/orphan.c
index e25f20ca..60f4ea18 100644
--- a/lib/ext2fs/orphan.c
+++ b/lib/ext2fs/orphan.c
@@ -126,6 +126,7 @@ errcode_t ext2fs_create_orphan_file(ext2_filsys fs, blk_t num_blocks)
char *buf = NULL, *zerobuf = NULL;
struct mkorphan_info oi;
struct ext4_orphan_block_tail *ob_tail;
+ time_t now;

if (!ino) {
err = ext2fs_new_inode(fs, EXT2_ROOT_INO, LINUX_S_IFREG | 0600,
@@ -185,8 +186,10 @@ errcode_t ext2fs_create_orphan_file(ext2_filsys fs, blk_t num_blocks)
if (err)
goto out;
ext2fs_iblk_set(fs, &inode, 0);
- inode.i_atime = inode.i_mtime =
- inode.i_ctime = fs->now ? fs->now : time(0);
+ now = fs->now ? fs->now : time(0);
+ ext2fs_inode_xtime_set(&inode, i_atime, now);
+ ext2fs_inode_xtime_set(&inode, i_ctime, now);
+ ext2fs_inode_xtime_set(&inode, i_mtime, now);
inode.i_links_count = 1;
inode.i_mode = LINUX_S_IFREG | 0600;
ext2fs_iblk_add_blocks(fs, &inode, oi.alloc_blocks);
diff --git a/lib/ext2fs/res_gdt.c b/lib/ext2fs/res_gdt.c
index fa8d8d6b..9024165d 100644
--- a/lib/ext2fs/res_gdt.c
+++ b/lib/ext2fs/res_gdt.c
@@ -227,7 +227,10 @@ out_inode:
EXT2_I_SIZE(&inode));
#endif
if (inode_dirty) {
- inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(0);
+ time_t now = fs->now ? fs->now : time(0);
+
+ ext2fs_inode_xtime_set(&inode, i_atime, now);
+ ext2fs_inode_xtime_set(&inode, i_mtime, now);
retval2 = ext2fs_write_new_inode(fs, EXT2_RESIZE_INO, &inode);
if (!retval)
retval = retval2;
diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
index fe764b9e..d8d21407 100644
--- a/lib/ext2fs/swapfs.c
+++ b/lib/ext2fs/swapfs.c
@@ -345,21 +345,21 @@ void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
return; /* Illegal inode extra_isize */

inode_size = EXT2_GOOD_OLD_INODE_SIZE + extra_isize;
- if (inode_includes(inode_size, i_checksum_hi))
+ if (ext2fs_inode_includes(inode_size, i_checksum_hi))
t->i_checksum_hi = ext2fs_swab16(f->i_checksum_hi);
- if (inode_includes(inode_size, i_ctime_extra))
+ if (ext2fs_inode_includes(inode_size, i_ctime_extra))
t->i_ctime_extra = ext2fs_swab32(f->i_ctime_extra);
- if (inode_includes(inode_size, i_mtime_extra))
+ if (ext2fs_inode_includes(inode_size, i_mtime_extra))
t->i_mtime_extra = ext2fs_swab32(f->i_mtime_extra);
- if (inode_includes(inode_size, i_atime_extra))
+ if (ext2fs_inode_includes(inode_size, i_atime_extra))
t->i_atime_extra = ext2fs_swab32(f->i_atime_extra);
- if (inode_includes(inode_size, i_crtime))
+ if (ext2fs_inode_includes(inode_size, i_crtime))
t->i_crtime = ext2fs_swab32(f->i_crtime);
- if (inode_includes(inode_size, i_crtime_extra))
+ if (ext2fs_inode_includes(inode_size, i_crtime_extra))
t->i_crtime_extra = ext2fs_swab32(f->i_crtime_extra);
- if (inode_includes(inode_size, i_version_hi))
+ if (ext2fs_inode_includes(inode_size, i_version_hi))
t->i_version_hi = ext2fs_swab32(f->i_version_hi);
- if (inode_includes(inode_size, i_projid))
+ if (ext2fs_inode_includes(inode_size, i_projid))
t->i_projid = ext2fs_swab32(f->i_projid);
/* catch new static fields added after i_projid */
EXT2FS_BUILD_BUG_ON(sizeof(struct ext2_inode_large) != 160);
diff --git a/lib/support/mkquota.c b/lib/support/mkquota.c
index 9339c994..81cfbf1f 100644
--- a/lib/support/mkquota.c
+++ b/lib/support/mkquota.c
@@ -269,7 +269,7 @@ static inline qid_t get_qid(struct ext2_inode_large *inode, enum quota_type qtyp
case PRJQUOTA:
inode_size = EXT2_GOOD_OLD_INODE_SIZE +
inode->i_extra_isize;
- if (inode_includes(inode_size, i_projid))
+ if (ext2fs_inode_includes(inode_size, i_projid))
return inode_projid(*inode);
return 0;
default:
diff --git a/lib/support/plausible.c b/lib/support/plausible.c
index 65a6b2e1..eccba22a 100644
--- a/lib/support/plausible.c
+++ b/lib/support/plausible.c
@@ -108,18 +108,15 @@ static void print_ext2_info(const char *device)
return;
sb = fs->super;

- if (sb->s_mtime) {
- tm = sb->s_mtime;
+ if ((tm = ext2fs_get_tstamp(sb, s_mtime))) {
if (sb->s_last_mounted[0])
printf(_("\tlast mounted on %.*s on %s"),
EXT2_LEN_STR(sb->s_last_mounted), ctime(&tm));
else
printf(_("\tlast mounted on %s"), ctime(&tm));
- } else if (sb->s_mkfs_time) {
- tm = sb->s_mkfs_time;
+ } else if ((tm = ext2fs_get_tstamp(sb, s_mkfs_time))) {
printf(_("\tcreated on %s"), ctime(&tm));
- } else if (sb->s_wtime) {
- tm = sb->s_wtime;
+ } else if ((tm = ext2fs_get_tstamp(sb, s_wtime))) {
printf(_("\tlast modified on %s"), ctime(&tm));
}
ext2fs_close_free(&fs);
diff --git a/lib/support/quotaio.c b/lib/support/quotaio.c
index b41bb749..916e28cf 100644
--- a/lib/support/quotaio.c
+++ b/lib/support/quotaio.c
@@ -272,6 +272,7 @@ static errcode_t quota_inode_init_new(ext2_filsys fs, ext2_ino_t ino)
{
struct ext2_inode inode;
errcode_t err = 0;
+ time_t now;

err = ext2fs_read_inode(fs, ino, &inode);
if (err) {
@@ -287,8 +288,10 @@ static errcode_t quota_inode_init_new(ext2_filsys fs, ext2_ino_t ino)

memset(&inode, 0, sizeof(struct ext2_inode));
ext2fs_iblk_set(fs, &inode, 0);
- inode.i_atime = inode.i_mtime =
- inode.i_ctime = fs->now ? fs->now : time(0);
+ now = fs->now ? fs->now : time(0);
+ ext2fs_inode_xtime_set(&inode, i_atime, now);
+ ext2fs_inode_xtime_set(&inode, i_ctime, now);
+ ext2fs_inode_xtime_set(&inode, i_mtime, now);
inode.i_links_count = 1;
inode.i_mode = LINUX_S_IFREG | 0600;
inode.i_flags |= EXT2_IMMUTABLE_FL;
diff --git a/misc/create_inode.c b/misc/create_inode.c
index a3a34cd9..28f478c0 100644
--- a/misc/create_inode.c
+++ b/misc/create_inode.c
@@ -125,9 +125,9 @@ static errcode_t set_inode_extra(ext2_filsys fs, ext2_ino_t ino,
inode.i_gid = st->st_gid;
ext2fs_set_i_gid_high(inode, st->st_gid >> 16);
inode.i_mode = (LINUX_S_IFMT & inode.i_mode) | (~S_IFMT & st->st_mode);
- inode.i_atime = st->st_atime;
- inode.i_mtime = st->st_mtime;
- inode.i_ctime = st->st_ctime;
+ ext2fs_inode_xtime_set(&inode, i_atime, st->st_atime);
+ ext2fs_inode_xtime_set(&inode, i_ctime, st->st_ctime);
+ ext2fs_inode_xtime_set(&inode, i_mtime, st->st_mtime);

retval = ext2fs_write_inode(fs, ino, &inode);
if (retval)
@@ -256,6 +256,7 @@ errcode_t do_mknod_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
struct ext2_inode inode;
unsigned long devmajor, devminor, mode;
int filetype;
+ time_t now;

switch(st_mode & S_IFMT) {
case S_IFCHR:
@@ -309,8 +310,10 @@ errcode_t do_mknod_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
memset(&inode, 0, sizeof(inode));
inode.i_mode = mode;
- inode.i_atime = inode.i_ctime = inode.i_mtime =
- fs->now ? fs->now : time(0);
+ now = fs->now ? fs->now : time(0);
+ ext2fs_inode_xtime_set(&inode, i_atime, now);
+ ext2fs_inode_xtime_set(&inode, i_ctime, now);
+ ext2fs_inode_xtime_set(&inode, i_mtime, now);

if (filetype != S_IFIFO) {
devmajor = major(st_rdev);
@@ -631,6 +634,7 @@ errcode_t do_write_internal(ext2_filsys fs, ext2_ino_t cwd, const char *src,
errcode_t retval;
struct ext2_inode inode;
char *cp;
+ time_t now;

fd = ext2fs_open_file(src, O_RDONLY, 0);
if (fd < 0) {
@@ -684,8 +688,10 @@ errcode_t do_write_internal(ext2_filsys fs, ext2_ino_t cwd, const char *src,
ext2fs_inode_alloc_stats2(fs, newfile, +1, 0);
memset(&inode, 0, sizeof(inode));
inode.i_mode = (statbuf.st_mode & ~S_IFMT) | LINUX_S_IFREG;
- inode.i_atime = inode.i_ctime = inode.i_mtime =
- fs->now ? fs->now : time(0);
+ now = fs->now ? fs->now : time(0);
+ ext2fs_inode_xtime_set(&inode, i_atime, now);
+ ext2fs_inode_xtime_set(&inode, i_ctime, now);
+ ext2fs_inode_xtime_set(&inode, i_mtime, now);
inode.i_links_count = 1;
retval = ext2fs_inode_size_set(fs, &inode, statbuf.st_size);
if (retval)
diff --git a/misc/e2undo.c b/misc/e2undo.c
index bc78fb2e..4cbf8884 100644
--- a/misc/e2undo.c
+++ b/misc/e2undo.c
@@ -154,9 +154,11 @@ static void print_undo_mismatch(struct ext2_super_block *fs_super,
if (memcmp(fs_super->s_uuid, undo_super->s_uuid,
sizeof(fs_super->s_uuid)))
printf("%s", _("UUID does not match.\n"));
- if (fs_super->s_mtime != undo_super->s_mtime)
+ if (ext2fs_get_tstamp(fs_super, s_mtime) !=
+ ext2fs_get_tstamp(undo_super, s_mtime))
printf("%s", _("Last mount time does not match.\n"));
- if (fs_super->s_wtime != undo_super->s_wtime)
+ if (ext2fs_get_tstamp(fs_super, s_wtime) !=
+ ext2fs_get_tstamp(undo_super, s_wtime))
printf("%s", _("Last write time does not match.\n"));
if (fs_super->s_kbytes_written != undo_super->s_kbytes_written)
printf("%s", _("Lifetime write counter does not match.\n"));
diff --git a/misc/findsuper.c b/misc/findsuper.c
index 7e78c1fc..1f5c3e72 100644
--- a/misc/findsuper.c
+++ b/misc/findsuper.c
@@ -230,10 +230,9 @@ int main(int argc, char *argv[])
WHY("free_inodes_count > inodes_count (%u > %u)\n",
ext2.s_free_inodes_count, ext2.s_inodes_count);

- if (ext2.s_mkfs_time != 0)
- tm = ext2.s_mkfs_time;
- else
- tm = ext2.s_mtime;
+ tm = ext2fs_get_tstamp(ext2, s_mkfs_time);
+ if (tm == 0)
+ tm = ext2fs_get_tstamp(ext2, s_mtime);
s = ctime(&tm);
s[24] = 0;
bsize = 1 << (ext2.s_log_block_size + 10);
diff --git a/misc/fuse2fs.c b/misc/fuse2fs.c
index 0dc77ead..4133e060 100644
--- a/misc/fuse2fs.c
+++ b/misc/fuse2fs.c
@@ -746,7 +746,7 @@ static void *op_init(struct fuse_conn_info *conn)
#endif
if (fs->flags & EXT2_FLAG_RW) {
fs->super->s_mnt_count++;
- fs->super->s_mtime = time(NULL);
+ ext2fs_set_tstamp(fs->super, s_mtime, time(NULL));
fs->super->s_state &= ~EXT2_VALID_FS;
ext2fs_mark_super_dirty(fs);
err = ext2fs_flush2(fs, 0);
@@ -3984,14 +3984,14 @@ no_translation:

/* Make a note in the error log */
get_now(&now);
- fs->super->s_last_error_time = now.tv_sec;
+ ext2fs_set_tstamp(fs->super, s_last_error_time, now.tv_sec);
fs->super->s_last_error_ino = ino;
fs->super->s_last_error_line = line;
fs->super->s_last_error_block = err; /* Yeah... */
strncpy((char *)fs->super->s_last_error_func, file,
sizeof(fs->super->s_last_error_func));
- if (fs->super->s_first_error_time == 0) {
- fs->super->s_first_error_time = now.tv_sec;
+ if (ext2fs_get_tstamp(fs->super, s_first_error_time) == 0) {
+ ext2fs_set_tstamp(fs->super, s_first_error_time, now.tv_sec);
fs->super->s_first_error_ino = ino;
fs->super->s_first_error_line = line;
fs->super->s_first_error_block = err;
diff --git a/misc/tune2fs.c b/misc/tune2fs.c
index 458f7cf6..52b0aa53 100644
--- a/misc/tune2fs.c
+++ b/misc/tune2fs.c
@@ -466,7 +466,8 @@ static int check_fsck_needed(ext2_filsys fs, const char *prompt)
/* Refuse to modify anything but a freshly checked valid filesystem. */
if (!(fs->super->s_state & EXT2_VALID_FS) ||
(fs->super->s_state & EXT2_ERROR_FS) ||
- (fs->super->s_lastcheck < fs->super->s_mtime)) {
+ (ext2fs_get_tstamp(fs->super, s_lastcheck) <
+ ext2fs_get_tstamp(fs->super, s_mtime))) {
puts(_(fsck_explain));
puts(_(please_fsck));
if (mount_flags & EXT2_MF_READONLY)
@@ -520,7 +521,8 @@ static void convert_64bit(ext2_filsys fs, int direction)
if (!fsck_requested &&
((fs->super->s_state & EXT2_ERROR_FS) ||
!(fs->super->s_state & EXT2_VALID_FS) ||
- fs->super->s_lastcheck < fs->super->s_mtime))
+ ext2fs_get_tstamp(fs->super, s_lastcheck) <
+ ext2fs_get_tstamp(fs->super, s_mtime)))
request_fsck_afterwards(fs);
if (fsck_requested)
fprintf(stderr, _("After running e2fsck, please run `resize2fs %s %s"),
--
2.25.1


2024-04-01 21:28:25

by Andreas Dilger

[permalink] [raw]
Subject: Re: [PATCH] misc: add 2038 timestamp support

On Sep 26, 2023, at 11:40 PM, Andreas Dilger <[email protected]> wrote:
>
> The ext4 kernel code implemented support for s_mtime_hi,
> s_wtime_hi, and related timestamp fields to avoid timestamp
> overflow in 2038, but similar handling is not in e2fsprogs.

Hi Ted,
I'm just going through some of my branches in e2fsprogs. and I
see this one is marked as "accepted" in Patchwork, but has not
been landed to a branch (maint, master, next). Not that it is
urgent, but you know how people keep old e2fsprogs around for
a long time, and 14 years is no longer that the far in the future.

Cheers, Andreas

>
> Add helper macros for the superblock _hi timestamp fields
> ext2fs_super_tstamp_get() and ext2fs_super_tstamp_set().
>
> Add helper macro for inode _extra timestamp fields
> ext2fs_inode_xtime_get() and ext2fs_inode_xtime_set().
>
> Add helper macro ext2fs_actual_inode_size() to avoid open
> coding the i_extra_isize check in multiple places.
>
> Remove inode_time_to_string() since this is unused once callers
> change to time_to_string(ext2fs_inode_xtime_get()) directly.
>
> Fix inode_includes() macro to properly wrap "inode" parameter,
> and rename to ext2fs_inode_includes() to avoid potential name
> clashes. Use this to check inode field inclusion in debugfs
> instead of bare constants for inode field offsets.
>
> Use these interfaces to access timestamps in debugfs, e2fsck,
> libext2fs, fuse2fs, tune2fs, and e2undo.
>
> Signed-off-by: Andreas Dilger <[email protected]>
> ---
> debugfs/debugfs.c | 69 +++++++++++++++++++++++------------------
> debugfs/debugfs.h | 1 -
> debugfs/journal.c | 7 +++--
> debugfs/set_fields.c | 32 ++++++++++++-------
> debugfs/util.c | 8 -----
> e2fsck/message.c | 2 +-
> e2fsck/pass1.c | 16 +++++-----
> e2fsck/pass3.c | 8 +++--
> e2fsck/super.c | 16 +++++-----
> e2fsck/unix.c | 2 +-
> lib/e2p/ls.c | 28 +++++++----------
> lib/ext2fs/bb_inode.c | 11 ++++---
> lib/ext2fs/closefs.c | 2 +-
> lib/ext2fs/ext2_fs.h | 6 ++--
> lib/ext2fs/ext2fs.h | 52 +++++++++++++++++++++++++++++++
> lib/ext2fs/initialize.c | 3 +-
> lib/ext2fs/inode.c | 16 +++++-----
> lib/ext2fs/mkjournal.c | 5 ++-
> lib/ext2fs/orphan.c | 7 +++--
> lib/ext2fs/res_gdt.c | 5 ++-
> lib/ext2fs/swapfs.c | 16 +++++-----
> lib/support/mkquota.c | 2 +-
> lib/support/plausible.c | 9 ++----
> lib/support/quotaio.c | 7 +++--
> misc/create_inode.c | 20 +++++++-----
> misc/e2undo.c | 6 ++--
> misc/findsuper.c | 7 ++---
> misc/fuse2fs.c | 8 ++---
> misc/tune2fs.c | 6 ++--
> 29 files changed, 231 insertions(+), 146 deletions(-)
>
> diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
> index 9b6321dc..96551173 100644
> --- a/debugfs/debugfs.c
> +++ b/debugfs/debugfs.c
> @@ -831,11 +831,13 @@ void internal_dump_inode(FILE *out, const char *prefix,
> char frag, fsize;
> int os = current_fs->super->s_creator_os;
> struct ext2_inode_large *large_inode;
> - int is_large_inode = 0;
> + size_t inode_size;
>
> - if (EXT2_INODE_SIZE(current_fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
> - is_large_inode = 1;
> large_inode = (struct ext2_inode_large *) inode;
> + if (EXT2_INODE_SIZE(current_fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
> + inode_size = ext2fs_inode_actual_size(large_inode);
> + else
> + inode_size = EXT2_GOOD_OLD_INODE_SIZE;
>
> if (LINUX_S_ISDIR(inode->i_mode)) i_type = "directory";
> else if (LINUX_S_ISREG(inode->i_mode)) i_type = "regular";
> @@ -848,7 +850,7 @@ void internal_dump_inode(FILE *out, const char *prefix,
> fprintf(out, "%sInode: %u Type: %s ", prefix, inode_num, i_type);
> fprintf(out, "%sMode: 0%03o Flags: 0x%x\n",
> prefix, inode->i_mode & 07777, inode->i_flags);
> - if (is_large_inode && large_inode->i_extra_isize >= 24) {
> + if (ext2fs_inode_includes(inode_size, i_version_hi)) {
> fprintf(out, "%sGeneration: %u Version: 0x%08x:%08x\n",
> prefix, inode->i_generation, large_inode->i_version_hi,
> inode->osd1.linux1.l_i_version);
> @@ -858,7 +860,7 @@ void internal_dump_inode(FILE *out, const char *prefix,
> }
> fprintf(out, "%sUser: %5d Group: %5d",
> prefix, inode_uid(*inode), inode_gid(*inode));
> - if (is_large_inode && large_inode->i_extra_isize >= 32)
> + if (ext2fs_inode_includes(inode_size, i_projid))
> fprintf(out, " Project: %5d", large_inode->i_projid);
> fputs(" Size: ", out);
> if (LINUX_S_ISREG(inode->i_mode) || LINUX_S_ISDIR(inode->i_mode))
> @@ -895,39 +897,48 @@ void internal_dump_inode(FILE *out, const char *prefix,
> }
> fprintf(out, "%sFragment: Address: %u Number: %u Size: %u\n",
> prefix, inode->i_faddr, frag, fsize);
> - if (is_large_inode && large_inode->i_extra_isize >= 24) {
> + if (ext2fs_inode_includes(inode_size, i_ctime_extra))
> fprintf(out, "%s ctime: 0x%08x:%08x -- %s", prefix,
> inode->i_ctime, large_inode->i_ctime_extra,
> - inode_time_to_string(inode->i_ctime,
> - large_inode->i_ctime_extra));
> + time_to_string(ext2fs_inode_xtime_get(inode, i_ctime)));
> + else
> + fprintf(out, "%sctime: 0x%08x -- %s", prefix, inode->i_ctime,
> + time_to_string((__s32) inode->i_ctime));
> + if (ext2fs_inode_includes(inode_size, i_atime_extra))
> fprintf(out, "%s atime: 0x%08x:%08x -- %s", prefix,
> inode->i_atime, large_inode->i_atime_extra,
> - inode_time_to_string(inode->i_atime,
> - large_inode->i_atime_extra));
> + time_to_string(ext2fs_inode_xtime_get(inode, i_atime)));
> + else
> + fprintf(out, "%satime: 0x%08x -- %s", prefix, inode->i_atime,
> + time_to_string((__s32) inode->i_atime));
> + if (ext2fs_inode_includes(inode_size, i_mtime_extra))
> fprintf(out, "%s mtime: 0x%08x:%08x -- %s", prefix,
> inode->i_mtime, large_inode->i_mtime_extra,
> - inode_time_to_string(inode->i_mtime,
> - large_inode->i_mtime_extra));
> + time_to_string(ext2fs_inode_xtime_get(inode, i_mtime)));
> + else
> + fprintf(out, "%smtime: 0x%08x -- %s", prefix, inode->i_mtime,
> + time_to_string((__s32) inode->i_mtime));
> + if (ext2fs_inode_includes(inode_size, i_crtime_extra))
> fprintf(out, "%scrtime: 0x%08x:%08x -- %s", prefix,
> large_inode->i_crtime, large_inode->i_crtime_extra,
> - inode_time_to_string(large_inode->i_crtime,
> - large_inode->i_crtime_extra));
> - if (inode->i_dtime)
> + time_to_string(ext2fs_inode_xtime_get(large_inode,
> + i_crtime)));
> + if (inode->i_dtime) {
> + if (ext2fs_inode_includes(inode_size, i_ctime_extra)) {
> + time_t tm;
> +
> + /* dtime doesn't have its own i_dtime_extra field, so
> + * approximate this with i_ctime_extra instead. */
> + tm = __decode_extra_sec(inode->i_dtime,
> + large_inode->i_ctime_extra);
> fprintf(out, "%s dtime: 0x%08x:(%08x) -- %s", prefix,
> - large_inode->i_dtime, large_inode->i_ctime_extra,
> - inode_time_to_string(inode->i_dtime,
> - large_inode->i_ctime_extra));
> - } else {
> - fprintf(out, "%sctime: 0x%08x -- %s", prefix, inode->i_ctime,
> - time_to_string((__s32) inode->i_ctime));
> - fprintf(out, "%satime: 0x%08x -- %s", prefix, inode->i_atime,
> - time_to_string((__s32) inode->i_atime));
> - fprintf(out, "%smtime: 0x%08x -- %s", prefix, inode->i_mtime,
> - time_to_string((__s32) inode->i_mtime));
> - if (inode->i_dtime)
> + inode->i_dtime, large_inode->i_ctime_extra,
> + time_to_string(tm));
> + } else {
> fprintf(out, "%sdtime: 0x%08x -- %s", prefix,
> inode->i_dtime,
> time_to_string((__s32) inode->i_dtime));
> + }
> }
> if (EXT2_INODE_SIZE(current_fs->super) > EXT2_GOOD_OLD_INODE_SIZE)
> internal_dump_inode_extra(out, prefix, inode_num,
> @@ -935,11 +946,7 @@ void internal_dump_inode(FILE *out, const char *prefix,
> dump_inode_attributes(out, inode_num);
> if (ext2fs_has_feature_metadata_csum(current_fs->super)) {
> __u32 crc = inode->i_checksum_lo;
> - if (is_large_inode &&
> - large_inode->i_extra_isize >=
> - (offsetof(struct ext2_inode_large,
> - i_checksum_hi) -
> - EXT2_GOOD_OLD_INODE_SIZE))
> + if (ext2fs_inode_includes(inode_size, i_checksum_hi))
> crc |= ((__u32)large_inode->i_checksum_hi) << 16;
> fprintf(out, "Inode checksum: 0x%08x\n", crc);
> }
> diff --git a/debugfs/debugfs.h b/debugfs/debugfs.h
> index 39bc0247..85c82b95 100644
> --- a/debugfs/debugfs.h
> +++ b/debugfs/debugfs.h
> @@ -36,7 +36,6 @@ extern int check_fs_not_open(char *name);
> extern int check_fs_read_write(char *name);
> extern int check_fs_bitmaps(char *name);
> extern ext2_ino_t string_to_inode(char *str);
> -extern char *inode_time_to_string(__u32 xtime, __u32 xtime_extra);
> extern char *time_to_string(__s64);
> extern __s64 string_to_time(const char *);
> extern unsigned long parse_ulong(const char *str, const char *cmd,
> diff --git a/debugfs/journal.c b/debugfs/journal.c
> index 5bac0d3b..454fbcfc 100644
> --- a/debugfs/journal.c
> +++ b/debugfs/journal.c
> @@ -245,6 +245,8 @@ void wait_on_buffer(struct buffer_head *bh)
>
> static void ext2fs_clear_recover(ext2_filsys fs, int error)
> {
> + time_t s_mtime;
> +
> ext2fs_clear_feature_journal_needs_recovery(fs->super);
>
> /* if we had an error doing journal recovery, we need a full fsck */
> @@ -254,8 +256,9 @@ static void ext2fs_clear_recover(ext2_filsys fs, int error)
> * If we replayed the journal by definition the file system
> * was mounted since the last time it was checked
> */
> - if (fs->super->s_lastcheck >= fs->super->s_mtime)
> - fs->super->s_lastcheck = fs->super->s_mtime - 1;
> + s_mtime = ext2fs_get_tstamp(fs->super, s_mtime);
> + if (ext2fs_get_tstamp(fs->super, s_lastcheck) >= s_mtime)
> + ext2fs_set_tstamp(fs->super, s_lastcheck, s_mtime - 1);
> ext2fs_mark_super_dirty(fs);
> }
>
> diff --git a/debugfs/set_fields.c b/debugfs/set_fields.c
> index f916deab..ef137b0f 100644
> --- a/debugfs/set_fields.c
> +++ b/debugfs/set_fields.c
> @@ -99,15 +99,16 @@ static struct field_set_info super_fields[] = {
> { "blocks_per_group", &set_sb.s_blocks_per_group, NULL, 4, parse_uint },
> { "clusters_per_group", &set_sb.s_clusters_per_group, NULL, 4, parse_uint },
> { "inodes_per_group", &set_sb.s_inodes_per_group, NULL, 4, parse_uint },
> - { "mtime", &set_sb.s_mtime, NULL, 4, parse_time },
> - { "wtime", &set_sb.s_wtime, NULL, 4, parse_time },
> + { "mtime", &set_sb.s_mtime, &set_sb.s_mtime_hi, 5, parse_time },
> + { "wtime", &set_sb.s_wtime, &set_sb.s_wtime_hi, 5, parse_time },
> { "mnt_count", &set_sb.s_mnt_count, NULL, 2, parse_uint },
> { "max_mnt_count", &set_sb.s_max_mnt_count, NULL, 2, parse_int },
> /* s_magic */
> { "state", &set_sb.s_state, NULL, 2, parse_uint },
> { "errors", &set_sb.s_errors, NULL, 2, parse_uint },
> { "minor_rev_level", &set_sb.s_minor_rev_level, NULL, 2, parse_uint },
> - { "lastcheck", &set_sb.s_lastcheck, NULL, 4, parse_time },
> + { "lastcheck", &set_sb.s_lastcheck, &set_sb.s_lastcheck_hi, 5,
> + parse_time },
> { "checkinterval", &set_sb.s_checkinterval, NULL, 4, parse_uint },
> { "creator_os", &set_sb.s_creator_os, NULL, 4, parse_uint },
> { "rev_level", &set_sb.s_rev_level, NULL, 4, parse_uint },
> @@ -139,7 +140,8 @@ static struct field_set_info super_fields[] = {
> { "desc_size", &set_sb.s_desc_size, NULL, 2, parse_uint },
> { "default_mount_opts", &set_sb.s_default_mount_opts, NULL, 4, parse_uint },
> { "first_meta_bg", &set_sb.s_first_meta_bg, NULL, 4, parse_uint },
> - { "mkfs_time", &set_sb.s_mkfs_time, NULL, 4, parse_time },
> + { "mkfs_time", &set_sb.s_mkfs_time, &set_sb.s_mkfs_time_hi, 5,
> + parse_time },
> { "jnl_blocks", &set_sb.s_jnl_blocks[0], NULL, 4, parse_uint, FLAG_ARRAY,
> 17 },
> { "min_extra_isize", &set_sb.s_min_extra_isize, NULL, 2, parse_uint },
> @@ -167,12 +169,14 @@ static struct field_set_info super_fields[] = {
> { "checksum_type", &set_sb.s_checksum_type, NULL, 1, parse_uint },
> { "encryption_level", &set_sb.s_encryption_level, NULL, 1, parse_uint },
> { "error_count", &set_sb.s_error_count, NULL, 4, parse_uint },
> - { "first_error_time", &set_sb.s_first_error_time, NULL, 4, parse_time },
> + { "first_error_time", &set_sb.s_first_error_time,
> + &set_sb.s_first_error_time_hi, 5, parse_time },
> { "first_error_ino", &set_sb.s_first_error_ino, NULL, 4, parse_uint },
> { "first_error_block", &set_sb.s_first_error_block, NULL, 8, parse_uint },
> { "first_error_func", &set_sb.s_first_error_func, NULL, 32, parse_string },
> { "first_error_line", &set_sb.s_first_error_line, NULL, 4, parse_uint },
> - { "last_error_time", &set_sb.s_last_error_time, NULL, 4, parse_time },
> + { "last_error_time", &set_sb.s_last_error_time,
> + &set_sb.s_last_error_time_hi, 5, parse_time },
> { "last_error_ino", &set_sb.s_last_error_ino, NULL, 4, parse_uint },
> { "last_error_block", &set_sb.s_last_error_block, NULL, 8, parse_uint },
> { "last_error_func", &set_sb.s_last_error_func, NULL, 32, parse_string },
> @@ -441,6 +445,9 @@ static struct field_set_info *find_field(struct field_set_info *fields,
> * Note: info->size == 6 is special; this means a base size 4 bytes,
> * and secondary (high) size of 2 bytes. This is needed for the
> * special case of i_blocks_high and i_file_acl_high.
> + *
> + * Similarly, info->size == 5 is for superblock timestamps, which have
> + * a 4-byte primary field and a 1-byte _hi field.
> */
> static errcode_t parse_uint(struct field_set_info *info, char *field,
> char *arg)
> @@ -449,7 +456,7 @@ static errcode_t parse_uint(struct field_set_info *info, char *field,
> int suffix = check_suffix(field);
> char *tmp;
> void *field1 = info->ptr, *field2 = info->ptr2;
> - int size = (info->size == 6) ? 4 : info->size;
> + int size = (info->size == 6 || info->size == 5) ? 4 : info->size;
> union {
> __u64 *ptr64;
> __u32 *ptr32;
> @@ -477,7 +484,7 @@ static errcode_t parse_uint(struct field_set_info *info, char *field,
> }
> mask = ~0ULL >> ((8 - size) * 8);
> limit = ~0ULL >> ((8 - info->size) * 8);
> - if (field2 && info->size != 6)
> + if (field2 && (info->size != 6 || info->size != 5))
> limit = ~0ULL >> ((8 - info->size*2) * 8);
>
> if (num > limit) {
> @@ -504,13 +511,14 @@ static errcode_t parse_uint(struct field_set_info *info, char *field,
> return 0;
> n = (size == 8) ? 0 : (num >> (size*8));
> u.ptr8 = (__u8 *) field2;
> - if (info->size == 6)
> - size = 2;
> + if (info->size > size)
> + size = info->size - size;
> switch (size) {
> case 8:
> /* Should never get here */
> - fprintf(stderr, "64-bit field %s has a second 64-bit field\n"
> - "defined; BUG?!?\n", info->name);
> + fprintf(stderr,
> + "64-bit field %s has a second 64-bit field defined; BUG?!?\n",
> + info->name);
> *u.ptr64 = 0;
> break;
> case 4:
> diff --git a/debugfs/util.c b/debugfs/util.c
> index 9e880548..d3ef63c6 100644
> --- a/debugfs/util.c
> +++ b/debugfs/util.c
> @@ -191,14 +191,6 @@ int check_fs_bitmaps(char *name)
> return 0;
> }
>
> -char *inode_time_to_string(__u32 xtime, __u32 xtime_extra)
> -{
> - __s64 t = (__s32) xtime;
> -
> - t += (__s64) (xtime_extra & EXT4_EPOCH_MASK) << 32;
> - return time_to_string(t);
> -}
> -
> /*
> * This function takes a __s64 time value and converts it to a string,
> * using ctime
> diff --git a/e2fsck/message.c b/e2fsck/message.c
> index ba38038c..9c42b13f 100644
> --- a/e2fsck/message.c
> +++ b/e2fsck/message.c
> @@ -301,7 +301,7 @@ static _INLINE_ void expand_inode_expression(FILE *f, ext2_filsys fs, char ch,
> fprintf(f, "0%o", inode->i_mode);
> break;
> case 'M':
> - print_time(f, inode->i_mtime);
> + print_time(f, ext2fs_inode_xtime_get(inode, i_mtime));
> break;
> case 'F':
> fprintf(f, "%u", inode->i_faddr);
> diff --git a/e2fsck/pass1.c b/e2fsck/pass1.c
> index a341c72a..078bcb9b 100644
> --- a/e2fsck/pass1.c
> +++ b/e2fsck/pass1.c
> @@ -1181,6 +1181,7 @@ void e2fsck_pass1(e2fsck_t ctx)
> ext2_ino_t ino_threshold = 0;
> dgrp_t ra_group = 0;
> struct ea_quota ea_ibody_quota;
> + time_t tm;
>
> init_resource_track(&rtrack, ctx->fs->io);
> clear_problem_context(&pctx);
> @@ -1357,12 +1358,13 @@ void e2fsck_pass1(e2fsck_t ctx)
> if (ctx->progress && ((ctx->progress)(ctx, 1, 0,
> ctx->fs->group_desc_count)))
> goto endit;
> - if ((fs->super->s_wtime &&
> - fs->super->s_wtime < fs->super->s_inodes_count) ||
> - (fs->super->s_mtime &&
> - fs->super->s_mtime < fs->super->s_inodes_count) ||
> - (fs->super->s_mkfs_time &&
> - fs->super->s_mkfs_time < fs->super->s_inodes_count))
> +
> + if (((tm = ext2fs_get_tstamp(fs->super, s_wtime)) &&
> + tm < fs->super->s_inodes_count) ||
> + ((tm = ext2fs_get_tstamp(fs->super, s_mtime)) &&
> + tm < fs->super->s_inodes_count) ||
> + ((tm = ext2fs_get_tstamp(fs->super, s_mkfs_time)) &&
> + tm < fs->super->s_inodes_count))
> low_dtime_check = 0;
>
> if (ext2fs_has_feature_mmp(fs->super) &&
> @@ -2076,7 +2078,7 @@ void e2fsck_pass1(e2fsck_t ctx)
> if (!pctx.errcode) {
> e2fsck_read_inode(ctx, EXT2_RESIZE_INO, inode,
> "recreate inode");
> - inode->i_mtime = ctx->now;
> + ext2fs_inode_xtime_set(inode, i_mtime, ctx->now);
> e2fsck_write_inode(ctx, EXT2_RESIZE_INO, inode,
> "recreate inode");
> }
> diff --git a/e2fsck/pass3.c b/e2fsck/pass3.c
> index 16d243f6..ba794165 100644
> --- a/e2fsck/pass3.c
> +++ b/e2fsck/pass3.c
> @@ -212,7 +212,9 @@ skip_new_block:
> memset(&inode, 0, sizeof(inode));
> inode.i_mode = 040755;
> inode.i_size = fs->blocksize;
> - inode.i_atime = inode.i_ctime = inode.i_mtime = ctx->now;
> + ext2fs_inode_xtime_set(&inode, i_atime, ctx->now);
> + ext2fs_inode_xtime_set(&inode, i_ctime, ctx->now);
> + ext2fs_inode_xtime_set(&inode, i_mtime, ctx->now);
> inode.i_links_count = 2;
> ext2fs_iblk_set(fs, iptr, 1);
> inode.i_block[0] = blk;
> @@ -528,7 +530,9 @@ skip_new_block:
> memset(&inode, 0, sizeof(inode));
> inode.i_mode = 040700;
> inode.i_size = fs->blocksize;
> - inode.i_atime = inode.i_ctime = inode.i_mtime = ctx->now;
> + ext2fs_inode_xtime_set(&inode, i_atime, ctx->now);
> + ext2fs_inode_xtime_set(&inode, i_ctime, ctx->now);
> + ext2fs_inode_xtime_set(&inode, i_mtime, ctx->now);
> inode.i_links_count = 2;
> ext2fs_iblk_set(fs, EXT2_INODE(&inode), 1);
> inode.i_block[0] = blk;
> diff --git a/e2fsck/super.c b/e2fsck/super.c
> index be40dd8f..757a475d 100644
> --- a/e2fsck/super.c
> +++ b/e2fsck/super.c
> @@ -1320,25 +1320,25 @@ void check_super_block(e2fsck_t ctx)
> */
> if (((ctx->options & E2F_OPT_FORCE) || fs->super->s_checkinterval) &&
> !broken_system_clock && !(ctx->flags & E2F_FLAG_TIME_INSANE) &&
> - (fs->super->s_mtime > (__u32) ctx->now)) {
> - pctx.num = fs->super->s_mtime;
> + (ext2fs_get_tstamp(fs->super, s_mtime) > ctx->now)) {
> + pctx.num = ext2fs_get_tstamp(fs->super, s_mtime);
> problem = PR_0_FUTURE_SB_LAST_MOUNT;
> - if (fs->super->s_mtime <= (__u32) ctx->now + ctx->time_fudge)
> + if (pctx.num <= ctx->now + ctx->time_fudge)
> problem = PR_0_FUTURE_SB_LAST_MOUNT_FUDGED;
> if (fix_problem(ctx, problem, &pctx)) {
> - fs->super->s_mtime = ctx->now;
> + ext2fs_set_tstamp(fs->super, s_mtime, ctx->now);
> fs->flags |= EXT2_FLAG_DIRTY;
> }
> }
> if (((ctx->options & E2F_OPT_FORCE) || fs->super->s_checkinterval) &&
> !broken_system_clock && !(ctx->flags & E2F_FLAG_TIME_INSANE) &&
> - (fs->super->s_wtime > (__u32) ctx->now)) {
> - pctx.num = fs->super->s_wtime;
> + (ext2fs_get_tstamp(fs->super, s_wtime) > ctx->now)) {
> + pctx.num = ext2fs_get_tstamp(fs->super, s_wtime);
> problem = PR_0_FUTURE_SB_LAST_WRITE;
> - if (fs->super->s_wtime <= (__u32) ctx->now + ctx->time_fudge)
> + if (pctx.num <= ctx->now + ctx->time_fudge)
> problem = PR_0_FUTURE_SB_LAST_WRITE_FUDGED;
> if (fix_problem(ctx, problem, &pctx)) {
> - fs->super->s_wtime = ctx->now;
> + ext2fs_set_tstamp(fs->super, s_wtime, ctx->now);
> fs->flags |= EXT2_FLAG_DIRTY;
> }
> }
> diff --git a/e2fsck/unix.c b/e2fsck/unix.c
> index e5b672a2..bc6b518d 100644
> --- a/e2fsck/unix.c
> +++ b/e2fsck/unix.c
> @@ -2080,7 +2080,7 @@ cleanup:
> } else
> sb->s_state &= ~EXT2_VALID_FS;
> if (!(ctx->flags & E2F_FLAG_TIME_INSANE))
> - sb->s_lastcheck = ctx->now;
> + ext2fs_set_tstamp(sb, s_lastcheck, ctx->now);
> sb->s_mnt_count = 0;
> memset(((char *) sb) + EXT4_S_ERR_START, 0, EXT4_S_ERR_LEN);
> pctx.errcode = ext2fs_set_gdt_csum(ctx->fs);
> diff --git a/lib/e2p/ls.c b/lib/e2p/ls.c
> index 0b74aea2..081ef975 100644
> --- a/lib/e2p/ls.c
> +++ b/lib/e2p/ls.c
> @@ -313,27 +313,23 @@ void list_super2(struct ext2_super_block * sb, FILE *f)
> if (sb->s_log_groups_per_flex)
> fprintf(f, "Flex block group size: %u\n",
> 1U << sb->s_log_groups_per_flex);
> - if (sb->s_mkfs_time) {
> - tm = sb->s_mkfs_time;
> + tm = ext2fs_get_tstamp(sb, s_mkfs_time);
> + if (tm)
> fprintf(f, "Filesystem created: %s", ctime(&tm));
> - }
> - tm = sb->s_mtime;
> - fprintf(f, "Last mount time: %s",
> - sb->s_mtime ? ctime(&tm) : "n/a\n");
> - tm = sb->s_wtime;
> + tm = ext2fs_get_tstamp(sb, s_mtime);
> + fprintf(f, "Last mount time: %s", tm ? ctime(&tm) : "n/a\n");
> + tm = ext2fs_get_tstamp(sb, s_wtime);
> fprintf(f, "Last write time: %s", ctime(&tm));
> fprintf(f, "Mount count: %u\n", sb->s_mnt_count);
> fprintf(f, "Maximum mount count: %d\n", sb->s_max_mnt_count);
> - tm = sb->s_lastcheck;
> + tm = ext2fs_get_tstamp(sb, s_lastcheck);
> fprintf(f, "Last checked: %s", ctime(&tm));
> fprintf(f, "Check interval: %u (%s)\n", sb->s_checkinterval,
> interval_string(sb->s_checkinterval));
> if (sb->s_checkinterval)
> {
> - time_t next;
> -
> - next = sb->s_lastcheck + sb->s_checkinterval;
> - fprintf(f, "Next check after: %s", ctime(&next));
> + tm += sb->s_checkinterval;
> + fprintf(f, "Next check after: %s", ctime(&tm));
> }
> #define POW2(x) ((__u64) 1 << (x))
> if (sb->s_kbytes_written) {
> @@ -419,8 +415,8 @@ void list_super2(struct ext2_super_block * sb, FILE *f)
> if (sb->s_error_count)
> fprintf(f, "FS Error count: %u\n",
> sb->s_error_count);
> - if (sb->s_first_error_time) {
> - tm = sb->s_first_error_time;
> + tm = ext2fs_get_tstamp(sb, s_first_error_time);
> + if (tm) {
> fprintf(f, "First error time: %s", ctime(&tm));
> fprintf(f, "First error function: %.*s\n",
> EXT2_LEN_STR(sb->s_first_error_func));
> @@ -436,8 +432,8 @@ void list_super2(struct ext2_super_block * sb, FILE *f)
> fprintf(f, "First error err: %s\n",
> e2p_errcode2str(sb->s_first_error_errcode));
> }
> - if (sb->s_last_error_time) {
> - tm = sb->s_last_error_time;
> + tm = ext2fs_get_tstamp(sb, s_last_error_time);
> + if (tm) {
> fprintf(f, "Last error time: %s", ctime(&tm));
> fprintf(f, "Last error function: %.*s\n",
> EXT2_LEN_STR(sb->s_last_error_func));
> diff --git a/lib/ext2fs/bb_inode.c b/lib/ext2fs/bb_inode.c
> index 11f10ebc..927a4d41 100644
> --- a/lib/ext2fs/bb_inode.c
> +++ b/lib/ext2fs/bb_inode.c
> @@ -58,8 +58,9 @@ static int clear_bad_block_proc(ext2_filsys fs, blk_t *block_nr,
> errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list)
> {
> errcode_t retval;
> - struct set_badblock_record rec;
> + struct set_badblock_record rec;
> struct ext2_inode inode;
> + time_t now;
>
> EXT2_CHECK_MAGIC(fs, EXT2_ET_MAGIC_EXT2FS_FILSYS);
>
> @@ -124,9 +125,11 @@ errcode_t ext2fs_update_bb_inode(ext2_filsys fs, ext2_badblocks_list bb_list)
> if (retval)
> goto cleanup;
>
> - inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(0);
> - if (!inode.i_ctime)
> - inode.i_ctime = fs->now ? fs->now : time(0);
> + now = fs->now ? fs->now : time(0);
> + ext2fs_inode_xtime_set(&inode, i_atime, now);
> + if (!ext2fs_inode_xtime_get(&inode, i_ctime))
> + ext2fs_inode_xtime_set(&inode, i_ctime, now);
> + ext2fs_inode_xtime_set(&inode, i_mtime, now);
> ext2fs_iblk_set(fs, &inode, rec.bad_block_count);
> retval = ext2fs_inode_size_set(fs, &inode,
> rec.bad_block_count * fs->blocksize);
> diff --git a/lib/ext2fs/closefs.c b/lib/ext2fs/closefs.c
> index 69cbdd8c..42bda1fa 100644
> --- a/lib/ext2fs/closefs.c
> +++ b/lib/ext2fs/closefs.c
> @@ -301,7 +301,7 @@ errcode_t ext2fs_flush2(ext2_filsys fs, int flags)
> fs_state = fs->super->s_state;
> feature_incompat = fs->super->s_feature_incompat;
>
> - fs->super->s_wtime = fs->now ? fs->now : time(NULL);
> + ext2fs_set_tstamp(fs->super, s_wtime, fs->now ? fs->now : time(NULL));
> fs->super->s_block_group_nr = 0;
>
> /*
> diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h
> index 0fc9c09a..586141f8 100644
> --- a/lib/ext2fs/ext2_fs.h
> +++ b/lib/ext2fs/ext2_fs.h
> @@ -512,9 +512,9 @@ struct ext2_inode_large {
>
> #define i_checksum_lo osd2.linux2.l_i_checksum_lo
>
> -#define inode_includes(size, field) \
> - (size >= (sizeof(((struct ext2_inode_large *)0)->field) + \
> - offsetof(struct ext2_inode_large, field)))
> +#define ext2fs_inode_includes(size, field) \
> + ((size) >= (sizeof(((struct ext2_inode_large *)0)->field) + \
> + offsetof(struct ext2_inode_large, field)))
>
> #if defined(__KERNEL__) || defined(__linux__)
> #define i_reserved1 osd1.linux1.l_i_reserved1
> diff --git a/lib/ext2fs/ext2fs.h b/lib/ext2fs/ext2fs.h
> index 72c60d2b..8953817c 100644
> --- a/lib/ext2fs/ext2fs.h
> +++ b/lib/ext2fs/ext2fs.h
> @@ -579,6 +579,58 @@ typedef struct ext2_struct_inode_scan *ext2_inode_scan;
> */
> #define EXT2_I_SIZE(i) ((i)->i_size | ((__u64) (i)->i_size_high << 32))
>
> +static inline __u32 __encode_extra_time(time_t seconds, __u32 nsec)
> +{
> + __u32 extra = ((seconds - (__s32)seconds) >> 32) & EXT4_EPOCH_MASK;
> + return extra | (nsec << EXT4_EPOCH_BITS);
> +}
> +static inline time_t __decode_extra_sec(time_t seconds, __u32 extra)
> +{
> + if (extra & EXT4_EPOCH_MASK)
> + seconds += ((time_t)(extra & EXT4_EPOCH_MASK) << 32);
> + return seconds;
> +}
> +static inline __u32 __decode_extra_nsec(__u32 extra)
> +{
> + return (extra & EXT4_NSEC_MASK) >> EXT4_EPOCH_BITS;
> +}
> +#define ext2fs_inode_actual_size(inode) \
> + (EXT2_GOOD_OLD_INODE_SIZE + \
> + (sizeof(*inode) > EXT2_GOOD_OLD_INODE_SIZE ? \
> + ((struct ext2_inode_large *)(inode))->i_extra_isize : 0))
> +#define clamp(val, min, max) ((val) < (min) ? (min) : ((val) > (max) ? \
> + (max) : (val)))
> +#define ext2fs_inode_xtime_set(inode, field, sec) \
> +do { \
> + if (ext2fs_inode_includes(ext2fs_inode_actual_size(inode), \
> + field ## _extra)) { \
> + (inode)->field = (__s32)sec; \
> + ((struct ext2_inode_large *)(inode))->field ## _extra = \
> + __encode_extra_time(sec, 0); \
> + } else { \
> + (inode)->field = clamp(sec, INT32_MIN, INT32_MAX); \
> + } \
> +} while (0)
> +#define ext2fs_inode_xtime_get(inode, field) \
> +(ext2fs_inode_includes(ext2fs_inode_actual_size(inode), field ## _extra) ? \
> + __decode_extra_sec((inode)->field, \
> + ((struct ext2_inode_large *)(inode))->field ## _extra) : \
> + (time_t)(inode)->field)
> +
> +static inline void __sb_set_tstamp(__u32 *lo, __u8 *hi, time_t seconds)
> +{
> + *lo = seconds & 0xffffffff;
> + *hi = seconds >> 32;
> +}
> +static inline time_t __sb_get_tstamp(__u32 *lo, __u8 *hi)
> +{
> + return ((time_t)(*hi) << 32) | *lo;
> +}
> +#define ext2fs_set_tstamp(sb, field, seconds) \
> + __sb_set_tstamp(&(sb)->field, &(sb)->field ## _hi, seconds)
> +#define ext2fs_get_tstamp(sb, field) \
> + __sb_get_tstamp(&(sb)->field, &(sb)->field ## _hi)
> +
> /*
> * ext2_icount_t abstraction
> */
> diff --git a/lib/ext2fs/initialize.c b/lib/ext2fs/initialize.c
> index edd692bb..32467a29 100644
> --- a/lib/ext2fs/initialize.c
> +++ b/lib/ext2fs/initialize.c
> @@ -218,7 +218,8 @@ errcode_t ext2fs_initialize(const char *name, int flags,
> }
>
> set_field(s_checkinterval, 0);
> - super->s_mkfs_time = super->s_lastcheck = fs->now ? fs->now : time(NULL);
> + ext2fs_set_tstamp(super, s_mkfs_time, fs->now ? fs->now : time(NULL));
> + ext2fs_set_tstamp(super, s_lastcheck, fs->now ? fs->now : time(NULL));
>
> super->s_creator_os = CREATOR_OS;
>
> diff --git a/lib/ext2fs/inode.c b/lib/ext2fs/inode.c
> index 957d5aa9..8686f99c 100644
> --- a/lib/ext2fs/inode.c
> +++ b/lib/ext2fs/inode.c
> @@ -1039,17 +1039,17 @@ errcode_t ext2fs_write_new_inode(ext2_filsys fs, ext2_ino_t ino,
> struct ext2_inode *inode)
> {
> struct ext2_inode *buf;
> - int size = EXT2_INODE_SIZE(fs->super);
> + int size = EXT2_INODE_SIZE(fs->super);
> struct ext2_inode_large *large_inode;
> errcode_t retval;
> - __u32 t = fs->now ? fs->now : time(NULL);
> + time_t t = fs->now ? fs->now : time(NULL);
>
> - if (!inode->i_ctime)
> - inode->i_ctime = t;
> - if (!inode->i_mtime)
> - inode->i_mtime = t;
> - if (!inode->i_atime)
> - inode->i_atime = t;
> + if (!ext2fs_inode_xtime_get(inode, i_atime))
> + ext2fs_inode_xtime_set(inode, i_atime, t);
> + if (!ext2fs_inode_xtime_get(inode, i_ctime))
> + ext2fs_inode_xtime_set(inode, i_ctime, t);
> + if (!ext2fs_inode_xtime_get(inode, i_mtime))
> + ext2fs_inode_xtime_set(inode, i_mtime, t);
>
> if (size == sizeof(struct ext2_inode))
> return ext2fs_write_inode_full(fs, ino, inode,
> diff --git a/lib/ext2fs/mkjournal.c b/lib/ext2fs/mkjournal.c
> index 54772dd5..4a947b61 100644
> --- a/lib/ext2fs/mkjournal.c
> +++ b/lib/ext2fs/mkjournal.c
> @@ -285,6 +285,7 @@ static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino,
> unsigned long long inode_size;
> int falloc_flags = EXT2_FALLOCATE_FORCE_INIT;
> blk64_t zblk;
> + time_t now;
>
> if ((retval = ext2fs_create_journal_superblock2(fs, jparams, flags,
> &buf)))
> @@ -312,7 +313,9 @@ static errcode_t write_journal_inode(ext2_filsys fs, ext2_ino_t journal_ino,
>
> inode_size = (unsigned long long)fs->blocksize *
> (jparams->num_journal_blocks + jparams->num_fc_blocks);
> - inode.i_mtime = inode.i_ctime = fs->now ? fs->now : time(0);
> + now = fs->now ? fs->now : time(0);
> + ext2fs_inode_xtime_set(&inode, i_mtime, now);
> + ext2fs_inode_xtime_set(&inode, i_ctime, now);
> inode.i_links_count = 1;
> inode.i_mode = LINUX_S_IFREG | 0600;
> retval = ext2fs_inode_size_set(fs, &inode, inode_size);
> diff --git a/lib/ext2fs/orphan.c b/lib/ext2fs/orphan.c
> index e25f20ca..60f4ea18 100644
> --- a/lib/ext2fs/orphan.c
> +++ b/lib/ext2fs/orphan.c
> @@ -126,6 +126,7 @@ errcode_t ext2fs_create_orphan_file(ext2_filsys fs, blk_t num_blocks)
> char *buf = NULL, *zerobuf = NULL;
> struct mkorphan_info oi;
> struct ext4_orphan_block_tail *ob_tail;
> + time_t now;
>
> if (!ino) {
> err = ext2fs_new_inode(fs, EXT2_ROOT_INO, LINUX_S_IFREG | 0600,
> @@ -185,8 +186,10 @@ errcode_t ext2fs_create_orphan_file(ext2_filsys fs, blk_t num_blocks)
> if (err)
> goto out;
> ext2fs_iblk_set(fs, &inode, 0);
> - inode.i_atime = inode.i_mtime =
> - inode.i_ctime = fs->now ? fs->now : time(0);
> + now = fs->now ? fs->now : time(0);
> + ext2fs_inode_xtime_set(&inode, i_atime, now);
> + ext2fs_inode_xtime_set(&inode, i_ctime, now);
> + ext2fs_inode_xtime_set(&inode, i_mtime, now);
> inode.i_links_count = 1;
> inode.i_mode = LINUX_S_IFREG | 0600;
> ext2fs_iblk_add_blocks(fs, &inode, oi.alloc_blocks);
> diff --git a/lib/ext2fs/res_gdt.c b/lib/ext2fs/res_gdt.c
> index fa8d8d6b..9024165d 100644
> --- a/lib/ext2fs/res_gdt.c
> +++ b/lib/ext2fs/res_gdt.c
> @@ -227,7 +227,10 @@ out_inode:
> EXT2_I_SIZE(&inode));
> #endif
> if (inode_dirty) {
> - inode.i_atime = inode.i_mtime = fs->now ? fs->now : time(0);
> + time_t now = fs->now ? fs->now : time(0);
> +
> + ext2fs_inode_xtime_set(&inode, i_atime, now);
> + ext2fs_inode_xtime_set(&inode, i_mtime, now);
> retval2 = ext2fs_write_new_inode(fs, EXT2_RESIZE_INO, &inode);
> if (!retval)
> retval = retval2;
> diff --git a/lib/ext2fs/swapfs.c b/lib/ext2fs/swapfs.c
> index fe764b9e..d8d21407 100644
> --- a/lib/ext2fs/swapfs.c
> +++ b/lib/ext2fs/swapfs.c
> @@ -345,21 +345,21 @@ void ext2fs_swap_inode_full(ext2_filsys fs, struct ext2_inode_large *t,
> return; /* Illegal inode extra_isize */
>
> inode_size = EXT2_GOOD_OLD_INODE_SIZE + extra_isize;
> - if (inode_includes(inode_size, i_checksum_hi))
> + if (ext2fs_inode_includes(inode_size, i_checksum_hi))
> t->i_checksum_hi = ext2fs_swab16(f->i_checksum_hi);
> - if (inode_includes(inode_size, i_ctime_extra))
> + if (ext2fs_inode_includes(inode_size, i_ctime_extra))
> t->i_ctime_extra = ext2fs_swab32(f->i_ctime_extra);
> - if (inode_includes(inode_size, i_mtime_extra))
> + if (ext2fs_inode_includes(inode_size, i_mtime_extra))
> t->i_mtime_extra = ext2fs_swab32(f->i_mtime_extra);
> - if (inode_includes(inode_size, i_atime_extra))
> + if (ext2fs_inode_includes(inode_size, i_atime_extra))
> t->i_atime_extra = ext2fs_swab32(f->i_atime_extra);
> - if (inode_includes(inode_size, i_crtime))
> + if (ext2fs_inode_includes(inode_size, i_crtime))
> t->i_crtime = ext2fs_swab32(f->i_crtime);
> - if (inode_includes(inode_size, i_crtime_extra))
> + if (ext2fs_inode_includes(inode_size, i_crtime_extra))
> t->i_crtime_extra = ext2fs_swab32(f->i_crtime_extra);
> - if (inode_includes(inode_size, i_version_hi))
> + if (ext2fs_inode_includes(inode_size, i_version_hi))
> t->i_version_hi = ext2fs_swab32(f->i_version_hi);
> - if (inode_includes(inode_size, i_projid))
> + if (ext2fs_inode_includes(inode_size, i_projid))
> t->i_projid = ext2fs_swab32(f->i_projid);
> /* catch new static fields added after i_projid */
> EXT2FS_BUILD_BUG_ON(sizeof(struct ext2_inode_large) != 160);
> diff --git a/lib/support/mkquota.c b/lib/support/mkquota.c
> index 9339c994..81cfbf1f 100644
> --- a/lib/support/mkquota.c
> +++ b/lib/support/mkquota.c
> @@ -269,7 +269,7 @@ static inline qid_t get_qid(struct ext2_inode_large *inode, enum quota_type qtyp
> case PRJQUOTA:
> inode_size = EXT2_GOOD_OLD_INODE_SIZE +
> inode->i_extra_isize;
> - if (inode_includes(inode_size, i_projid))
> + if (ext2fs_inode_includes(inode_size, i_projid))
> return inode_projid(*inode);
> return 0;
> default:
> diff --git a/lib/support/plausible.c b/lib/support/plausible.c
> index 65a6b2e1..eccba22a 100644
> --- a/lib/support/plausible.c
> +++ b/lib/support/plausible.c
> @@ -108,18 +108,15 @@ static void print_ext2_info(const char *device)
> return;
> sb = fs->super;
>
> - if (sb->s_mtime) {
> - tm = sb->s_mtime;
> + if ((tm = ext2fs_get_tstamp(sb, s_mtime))) {
> if (sb->s_last_mounted[0])
> printf(_("\tlast mounted on %.*s on %s"),
> EXT2_LEN_STR(sb->s_last_mounted), ctime(&tm));
> else
> printf(_("\tlast mounted on %s"), ctime(&tm));
> - } else if (sb->s_mkfs_time) {
> - tm = sb->s_mkfs_time;
> + } else if ((tm = ext2fs_get_tstamp(sb, s_mkfs_time))) {
> printf(_("\tcreated on %s"), ctime(&tm));
> - } else if (sb->s_wtime) {
> - tm = sb->s_wtime;
> + } else if ((tm = ext2fs_get_tstamp(sb, s_wtime))) {
> printf(_("\tlast modified on %s"), ctime(&tm));
> }
> ext2fs_close_free(&fs);
> diff --git a/lib/support/quotaio.c b/lib/support/quotaio.c
> index b41bb749..916e28cf 100644
> --- a/lib/support/quotaio.c
> +++ b/lib/support/quotaio.c
> @@ -272,6 +272,7 @@ static errcode_t quota_inode_init_new(ext2_filsys fs, ext2_ino_t ino)
> {
> struct ext2_inode inode;
> errcode_t err = 0;
> + time_t now;
>
> err = ext2fs_read_inode(fs, ino, &inode);
> if (err) {
> @@ -287,8 +288,10 @@ static errcode_t quota_inode_init_new(ext2_filsys fs, ext2_ino_t ino)
>
> memset(&inode, 0, sizeof(struct ext2_inode));
> ext2fs_iblk_set(fs, &inode, 0);
> - inode.i_atime = inode.i_mtime =
> - inode.i_ctime = fs->now ? fs->now : time(0);
> + now = fs->now ? fs->now : time(0);
> + ext2fs_inode_xtime_set(&inode, i_atime, now);
> + ext2fs_inode_xtime_set(&inode, i_ctime, now);
> + ext2fs_inode_xtime_set(&inode, i_mtime, now);
> inode.i_links_count = 1;
> inode.i_mode = LINUX_S_IFREG | 0600;
> inode.i_flags |= EXT2_IMMUTABLE_FL;
> diff --git a/misc/create_inode.c b/misc/create_inode.c
> index a3a34cd9..28f478c0 100644
> --- a/misc/create_inode.c
> +++ b/misc/create_inode.c
> @@ -125,9 +125,9 @@ static errcode_t set_inode_extra(ext2_filsys fs, ext2_ino_t ino,
> inode.i_gid = st->st_gid;
> ext2fs_set_i_gid_high(inode, st->st_gid >> 16);
> inode.i_mode = (LINUX_S_IFMT & inode.i_mode) | (~S_IFMT & st->st_mode);
> - inode.i_atime = st->st_atime;
> - inode.i_mtime = st->st_mtime;
> - inode.i_ctime = st->st_ctime;
> + ext2fs_inode_xtime_set(&inode, i_atime, st->st_atime);
> + ext2fs_inode_xtime_set(&inode, i_ctime, st->st_ctime);
> + ext2fs_inode_xtime_set(&inode, i_mtime, st->st_mtime);
>
> retval = ext2fs_write_inode(fs, ino, &inode);
> if (retval)
> @@ -256,6 +256,7 @@ errcode_t do_mknod_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
> struct ext2_inode inode;
> unsigned long devmajor, devminor, mode;
> int filetype;
> + time_t now;
>
> switch(st_mode & S_IFMT) {
> case S_IFCHR:
> @@ -309,8 +310,10 @@ errcode_t do_mknod_internal(ext2_filsys fs, ext2_ino_t cwd, const char *name,
> ext2fs_inode_alloc_stats2(fs, ino, +1, 0);
> memset(&inode, 0, sizeof(inode));
> inode.i_mode = mode;
> - inode.i_atime = inode.i_ctime = inode.i_mtime =
> - fs->now ? fs->now : time(0);
> + now = fs->now ? fs->now : time(0);
> + ext2fs_inode_xtime_set(&inode, i_atime, now);
> + ext2fs_inode_xtime_set(&inode, i_ctime, now);
> + ext2fs_inode_xtime_set(&inode, i_mtime, now);
>
> if (filetype != S_IFIFO) {
> devmajor = major(st_rdev);
> @@ -631,6 +634,7 @@ errcode_t do_write_internal(ext2_filsys fs, ext2_ino_t cwd, const char *src,
> errcode_t retval;
> struct ext2_inode inode;
> char *cp;
> + time_t now;
>
> fd = ext2fs_open_file(src, O_RDONLY, 0);
> if (fd < 0) {
> @@ -684,8 +688,10 @@ errcode_t do_write_internal(ext2_filsys fs, ext2_ino_t cwd, const char *src,
> ext2fs_inode_alloc_stats2(fs, newfile, +1, 0);
> memset(&inode, 0, sizeof(inode));
> inode.i_mode = (statbuf.st_mode & ~S_IFMT) | LINUX_S_IFREG;
> - inode.i_atime = inode.i_ctime = inode.i_mtime =
> - fs->now ? fs->now : time(0);
> + now = fs->now ? fs->now : time(0);
> + ext2fs_inode_xtime_set(&inode, i_atime, now);
> + ext2fs_inode_xtime_set(&inode, i_ctime, now);
> + ext2fs_inode_xtime_set(&inode, i_mtime, now);
> inode.i_links_count = 1;
> retval = ext2fs_inode_size_set(fs, &inode, statbuf.st_size);
> if (retval)
> diff --git a/misc/e2undo.c b/misc/e2undo.c
> index bc78fb2e..4cbf8884 100644
> --- a/misc/e2undo.c
> +++ b/misc/e2undo.c
> @@ -154,9 +154,11 @@ static void print_undo_mismatch(struct ext2_super_block *fs_super,
> if (memcmp(fs_super->s_uuid, undo_super->s_uuid,
> sizeof(fs_super->s_uuid)))
> printf("%s", _("UUID does not match.\n"));
> - if (fs_super->s_mtime != undo_super->s_mtime)
> + if (ext2fs_get_tstamp(fs_super, s_mtime) !=
> + ext2fs_get_tstamp(undo_super, s_mtime))
> printf("%s", _("Last mount time does not match.\n"));
> - if (fs_super->s_wtime != undo_super->s_wtime)
> + if (ext2fs_get_tstamp(fs_super, s_wtime) !=
> + ext2fs_get_tstamp(undo_super, s_wtime))
> printf("%s", _("Last write time does not match.\n"));
> if (fs_super->s_kbytes_written != undo_super->s_kbytes_written)
> printf("%s", _("Lifetime write counter does not match.\n"));
> diff --git a/misc/findsuper.c b/misc/findsuper.c
> index 7e78c1fc..1f5c3e72 100644
> --- a/misc/findsuper.c
> +++ b/misc/findsuper.c
> @@ -230,10 +230,9 @@ int main(int argc, char *argv[])
> WHY("free_inodes_count > inodes_count (%u > %u)\n",
> ext2.s_free_inodes_count, ext2.s_inodes_count);
>
> - if (ext2.s_mkfs_time != 0)
> - tm = ext2.s_mkfs_time;
> - else
> - tm = ext2.s_mtime;
> + tm = ext2fs_get_tstamp(ext2, s_mkfs_time);
> + if (tm == 0)
> + tm = ext2fs_get_tstamp(ext2, s_mtime);
> s = ctime(&tm);
> s[24] = 0;
> bsize = 1 << (ext2.s_log_block_size + 10);
> diff --git a/misc/fuse2fs.c b/misc/fuse2fs.c
> index 0dc77ead..4133e060 100644
> --- a/misc/fuse2fs.c
> +++ b/misc/fuse2fs.c
> @@ -746,7 +746,7 @@ static void *op_init(struct fuse_conn_info *conn)
> #endif
> if (fs->flags & EXT2_FLAG_RW) {
> fs->super->s_mnt_count++;
> - fs->super->s_mtime = time(NULL);
> + ext2fs_set_tstamp(fs->super, s_mtime, time(NULL));
> fs->super->s_state &= ~EXT2_VALID_FS;
> ext2fs_mark_super_dirty(fs);
> err = ext2fs_flush2(fs, 0);
> @@ -3984,14 +3984,14 @@ no_translation:
>
> /* Make a note in the error log */
> get_now(&now);
> - fs->super->s_last_error_time = now.tv_sec;
> + ext2fs_set_tstamp(fs->super, s_last_error_time, now.tv_sec);
> fs->super->s_last_error_ino = ino;
> fs->super->s_last_error_line = line;
> fs->super->s_last_error_block = err; /* Yeah... */
> strncpy((char *)fs->super->s_last_error_func, file,
> sizeof(fs->super->s_last_error_func));
> - if (fs->super->s_first_error_time == 0) {
> - fs->super->s_first_error_time = now.tv_sec;
> + if (ext2fs_get_tstamp(fs->super, s_first_error_time) == 0) {
> + ext2fs_set_tstamp(fs->super, s_first_error_time, now.tv_sec);
> fs->super->s_first_error_ino = ino;
> fs->super->s_first_error_line = line;
> fs->super->s_first_error_block = err;
> diff --git a/misc/tune2fs.c b/misc/tune2fs.c
> index 458f7cf6..52b0aa53 100644
> --- a/misc/tune2fs.c
> +++ b/misc/tune2fs.c
> @@ -466,7 +466,8 @@ static int check_fsck_needed(ext2_filsys fs, const char *prompt)
> /* Refuse to modify anything but a freshly checked valid filesystem. */
> if (!(fs->super->s_state & EXT2_VALID_FS) ||
> (fs->super->s_state & EXT2_ERROR_FS) ||
> - (fs->super->s_lastcheck < fs->super->s_mtime)) {
> + (ext2fs_get_tstamp(fs->super, s_lastcheck) <
> + ext2fs_get_tstamp(fs->super, s_mtime))) {
> puts(_(fsck_explain));
> puts(_(please_fsck));
> if (mount_flags & EXT2_MF_READONLY)
> @@ -520,7 +521,8 @@ static void convert_64bit(ext2_filsys fs, int direction)
> if (!fsck_requested &&
> ((fs->super->s_state & EXT2_ERROR_FS) ||
> !(fs->super->s_state & EXT2_VALID_FS) ||
> - fs->super->s_lastcheck < fs->super->s_mtime))
> + ext2fs_get_tstamp(fs->super, s_lastcheck) <
> + ext2fs_get_tstamp(fs->super, s_mtime)))
> request_fsck_afterwards(fs);
> if (fsck_requested)
> fprintf(stderr, _("After running e2fsck, please run `resize2fs %s %s"),
> --
> 2.25.1
>


Cheers, Andreas






Attachments:
signature.asc (890.00 B)
Message signed with OpenPGP

2024-04-17 02:04:05

by Theodore Ts'o

[permalink] [raw]
Subject: Re: [PATCH] misc: add 2038 timestamp support


On Tue, 26 Sep 2023 23:40:16 -0600, Andreas Dilger wrote:
> The ext4 kernel code implemented support for s_mtime_hi,
> s_wtime_hi, and related timestamp fields to avoid timestamp
> overflow in 2038, but similar handling is not in e2fsprogs.
>
> Add helper macros for the superblock _hi timestamp fields
> ext2fs_super_tstamp_get() and ext2fs_super_tstamp_set().
>
> [...]

Applied, thanks!

[1/1] misc: add post-2038 timestamp support to e2fsprogs
commit: ca8bc9240a00665dd4c96de350e610add8543a08

Best regards,
--
Theodore Ts'o <[email protected]>