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
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
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]>
On Tue, Sep 26, 2023 at 11:40:16PM -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.
> ...
Hey Andreas,
I had recently taken this patch, but I've since found that it was
causing a number of problems. These have been fixed on the next
branch, but if you have your own build of e2fsprogs, you might want to
make sure you have these two fixups. The second is especially
important if you plan to use debugfs's set_super_value command on
customer file systems....
In the future, I strongly suggest that large patches to e2fsprogs are
run with make check run with trees built with "configure
--enable-ubsan" and "configure -enable-asan". If you have a github
account, pushing the changes so that the github actions will do a CI
using github actions to make sure that there aren't build problems on
i386, Windows, MacOS, and Android is also a good thing to do.
Cheers,
- Ted
commit 5b599a325c1af94111940c14d888ade937f29d19
Author: Theodore Ts'o <[email protected]>
Date: Wed Apr 17 23:47:02 2024 -0400
Fix 32-bit build and test failures
Commit ca8bc9240a00 ("Add post-2038 timestamp support to e2fsprogs")
was never built or tested on a 32-bit. It introduced some build
problems when time_t is a 32-bit integer, and it exposed some test
bugs. Fix them.
Fixes: ca8bc9240a00 ("Add post-2038 timestamp support to e2fsprogs")
Signed-off-by: Theodore Ts'o <[email protected]>
commit 9103e1e792170a836884db4ee9f2762bf1684f09
Author: Theodore Ts'o <[email protected]>
Date: Thu Apr 18 10:04:27 2024 -0400
debugfs: fix set_field's handling of timestamps
How timestamps are encoded in inodes and superblocks are different.
Unfortunately, commit ca8bc9240a00 which added post-2038 timestamps
was (a) overwriting adjacent superblock fields and/or attempting
unaligned writes to a 8-bit field from a 32-bit pointer, and (b) using
the incorrect encoding for timestamps stored in inodes. Fix both of
these issues, which were found thanks to UBSAN.
Fixes: ca8bc9240a00 ("Add post-2038 timestamp support to e2fsprogs")
Signed-off-by: Theodore Ts'o <[email protected]>
On Thu, Apr 18, 2024 at 10:36:11AM -0400, Theodore Ts'o wrote:
>
> I had recently taken this patch, but I've since found that it was
> causing a number of problems. These have been fixed on the next
> branch, but if you have your own build of e2fsprogs, you might want to
> make sure you have these two fixups. The second is especially
> important if you plan to use debugfs's set_super_value command on
> customer file systems....
One more commit that's needed to fix post-2038 timestamps:
commit 8b37e89f850610d51b7550ac34b8912b3000ced5
Author: Theodore Ts'o <[email protected]>
Date: Thu Apr 18 14:16:55 2024 -0400
debugfs: fix printing post-2038 inode timestamps
Debugfs's stat command called ext2fs_inode_xtime_get() with a struct
inode * instead of a struct large_inode *. As a result, printing
inode timestamps will be incorrect if the time value is larger than
2**32.
Fixes: ca8bc9240a00 ("Add post-2038 timestamp support to e2fsprogs")
Signed-off-by: Theodore Ts'o <[email protected]>
- Ted
On Apr 18, 2024, at 8:36 AM, Theodore Ts'o <[email protected]> wrote:
>
> On Tue, Sep 26, 2023 at 11:40:16PM -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.
>> ...
>
> Hey Andreas,
>
> I had recently taken this patch, but I've since found that it was
> causing a number of problems. These have been fixed on the next
> branch, but if you have your own build of e2fsprogs, you might want to
> make sure you have these two fixups. The second is especially
> important if you plan to use debugfs's set_super_value command on
> customer file systems....
Ted, thanks for catching this. Indeed, I had not tested this on 32-bit
systems (I think even my watch is 64-bit?), but as other recent posts
attest there are still 32-bit systems in use somewhere in the world.
This y2038 patch hasn't been in use anywhere in production. I had just
noticed while looking at the code that it was inconsistent with the
ext4 code and thought I'd "do the right thing" and submit a patch to
fix it. It only had manual bench testing and "make check". Sorry to
have introduced a broken patch.
> In the future, I strongly suggest that large patches to e2fsprogs are
> run with make check run with trees built with "configure
> --enable-ubsan" and "configure -enable-asan". If you have a github
> account, pushing the changes so that the github actions will do a CI
> using github actions to make sure that there aren't build problems on
> i386, Windows, MacOS, and Android is also a good thing to do.
I've never used Github actions for this. If I fork tytso/e2fsprogs and
push to adilger/e2fsprogs, are those actions automated already with a
config file inside the repo, or do I need to set that up myself?
PS: the kernel.org repo looks like it has not been updated in 4 months,
despite emails that you have landed patches. I was pulling from there
and didn't notice until now that you have been pushing only to github.
Cheers, Andreas
> commit 5b599a325c1af94111940c14d888ade937f29d19
> Author: Theodore Ts'o <[email protected]>
> Date: Wed Apr 17 23:47:02 2024 -0400
>
> Fix 32-bit build and test failures
>
> Commit ca8bc9240a00 ("Add post-2038 timestamp support to e2fsprogs")
> was never built or tested on a 32-bit. It introduced some build
> problems when time_t is a 32-bit integer, and it exposed some test
> bugs. Fix them.
>
> Fixes: ca8bc9240a00 ("Add post-2038 timestamp support to e2fsprogs")
> Signed-off-by: Theodore Ts'o <[email protected]>
>
> commit 9103e1e792170a836884db4ee9f2762bf1684f09
> Author: Theodore Ts'o <[email protected]>
> Date: Thu Apr 18 10:04:27 2024 -0400
>
> debugfs: fix set_field's handling of timestamps
>
> How timestamps are encoded in inodes and superblocks are different.
> Unfortunately, commit ca8bc9240a00 which added post-2038 timestamps
> was (a) overwriting adjacent superblock fields and/or attempting
> unaligned writes to a 8-bit field from a 32-bit pointer, and (b) using
> the incorrect encoding for timestamps stored in inodes. Fix both of
> these issues, which were found thanks to UBSAN.
>
> Fixes: ca8bc9240a00 ("Add post-2038 timestamp support to e2fsprogs")
> Signed-off-by: Theodore Ts'o <[email protected]>
Cheers, Andreas
On Thu, Apr 18, 2024 at 04:04:10PM -0600, Andreas Dilger wrote:
>
> I've never used Github actions for this. If I fork tytso/e2fsprogs and
> push to adilger/e2fsprogs, are those actions automated already with a
> config file inside the repo, or do I need to set that up myself?
Github actions should be enabled by default (although enterprise
accounts can have the default changed or prohibited by the enterprise
administrator's policy).
The config file is checked into the repo at .github/workflows/ci.yml
so if you fork the repo, you should get it.
> PS: the kernel.org repo looks like it has not been updated in 4 months,
> despite emails that you have landed patches. I was pulling from there
> and didn't notice until now that you have been pushing only to github.
I don't update kernel.org as frequently as github, but I suspect the
issue is that you were probably looking at the master branch, and the
updates were going on the next branch. When I'm processing a large
number of patches, sometimes I don't update master until I'm really
confident everything is in great shape to update the master branch to
point at next.
It's just that April has been super crazy busy me for me, so master
lagged next by a few weeks.
Cheers,
- Ted