From: Darrick J. Wong Date: Thu, 24 Apr 2025 21:42:52 +0000 (-0700) Subject: fuse2fs: clamp timestamps that are being written to disk X-Git-Tag: v1.47.3-rc1~92 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e13395876d63cebf008101b934ee9e5cdaae0150;p=thirdparty%2Fe2fsprogs.git fuse2fs: clamp timestamps that are being written to disk Clamp the timestamps that we write to disk to the minimum and maximum values permitted given the ondisk format. This fixes y2038 support, as tested by generic/402. Signed-off-by: Darrick J. Wong Link: https://lore.kernel.org/r/174553065069.1160461.14751120886781323020.stgit@frogsfrogsfrogs Signed-off-by: Theodore Ts'o --- diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h index 3a5eb738..fcd42055 100644 --- a/lib/ext2fs/ext2_fs.h +++ b/lib/ext2fs/ext2_fs.h @@ -801,6 +801,10 @@ struct ext2_super_block { #define EXT2_GOOD_OLD_INODE_SIZE 128 +#define EXT4_EXTRA_TIMESTAMP_MAX (((int64_t)1 << 34) - 1 + INT32_MIN) +#define EXT4_NON_EXTRA_TIMESTAMP_MAX INT32_MAX +#define EXT4_TIMESTAMP_MIN INT32_MIN + /* * Journal inode backup types */ diff --git a/misc/fuse2fs.c b/misc/fuse2fs.c index a35986fb..6c9155b4 100644 --- a/misc/fuse2fs.c +++ b/misc/fuse2fs.c @@ -227,21 +227,43 @@ static inline void ext4_decode_extra_time(struct timespec *time, __u32 extra) time->tv_nsec = ((extra) & EXT4_NSEC_MASK) >> EXT4_EPOCH_BITS; } +#define EXT4_CLAMP_TIMESTAMP(xtime, timespec, raw_inode) \ +do { \ + if ((timespec)->tv_sec < EXT4_TIMESTAMP_MIN) \ + (timespec)->tv_sec = EXT4_TIMESTAMP_MIN; \ + if ((timespec)->tv_sec < EXT4_TIMESTAMP_MIN) \ + (timespec)->tv_sec = EXT4_TIMESTAMP_MIN; \ + \ + if (EXT4_FITS_IN_INODE(raw_inode, xtime ## _extra)) { \ + if ((timespec)->tv_sec > EXT4_EXTRA_TIMESTAMP_MAX) \ + (timespec)->tv_sec = EXT4_EXTRA_TIMESTAMP_MAX; \ + } else { \ + if ((timespec)->tv_sec > EXT4_NON_EXTRA_TIMESTAMP_MAX) \ + (timespec)->tv_sec = EXT4_NON_EXTRA_TIMESTAMP_MAX; \ + } \ +} while (0) + #define EXT4_INODE_SET_XTIME(xtime, timespec, raw_inode) \ do { \ - (raw_inode)->xtime = (timespec)->tv_sec; \ + typeof(*(timespec)) _ts = *(timespec); \ + \ + EXT4_CLAMP_TIMESTAMP(xtime, &_ts, raw_inode); \ + (raw_inode)->xtime = _ts.tv_sec; \ if (EXT4_FITS_IN_INODE(raw_inode, xtime ## _extra)) \ (raw_inode)->xtime ## _extra = \ - ext4_encode_extra_time(timespec); \ + ext4_encode_extra_time(&_ts); \ } while (0) #define EXT4_EINODE_SET_XTIME(xtime, timespec, raw_inode) \ do { \ + typeof(*(timespec)) _ts = *(timespec); \ + \ + EXT4_CLAMP_TIMESTAMP(xtime, &_ts, raw_inode); \ if (EXT4_FITS_IN_INODE(raw_inode, xtime)) \ - (raw_inode)->xtime = (timespec)->tv_sec; \ + (raw_inode)->xtime = _ts.tv_sec; \ if (EXT4_FITS_IN_INODE(raw_inode, xtime ## _extra)) \ (raw_inode)->xtime ## _extra = \ - ext4_encode_extra_time(timespec); \ + ext4_encode_extra_time(&_ts); \ } while (0) #define EXT4_INODE_GET_XTIME(xtime, timespec, raw_inode) \ @@ -2884,7 +2906,10 @@ static int op_utimens(const char *path, const struct timespec ctv[2] ret = translate_error(fs, 0, err); goto out; } - dbg_printf(ff, "%s: ino=%d\n", __func__, ino); + dbg_printf(ff, "%s: ino=%d atime=%lld.%ld mtime=%lld.%ld\n", __func__, + ino, + (long long int)ctv[0].tv_sec, ctv[0].tv_nsec, + (long long int)ctv[1].tv_sec, ctv[1].tv_nsec); ret = check_inum_access(fs, ino, W_OK); if (ret) @@ -2908,9 +2933,9 @@ static int op_utimens(const char *path, const struct timespec ctv[2] #endif /* UTIME_NOW */ #ifdef UTIME_OMIT if (tv[0].tv_nsec != UTIME_OMIT) - EXT4_INODE_SET_XTIME(i_atime, tv, &inode); + EXT4_INODE_SET_XTIME(i_atime, &tv[0], &inode); if (tv[1].tv_nsec != UTIME_OMIT) - EXT4_INODE_SET_XTIME(i_mtime, tv + 1, &inode); + EXT4_INODE_SET_XTIME(i_mtime, &tv[1], &inode); #endif /* UTIME_OMIT */ ret = update_ctime(fs, ino, &inode); if (ret)