From: Darrick J. Wong <djwong@xxxxxxxxxx> 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" <djwong@xxxxxxxxxx> --- lib/ext2fs/ext2_fs.h | 4 ++++ misc/fuse2fs.c | 39 ++++++++++++++++++++++++++++++++------- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/lib/ext2fs/ext2_fs.h b/lib/ext2fs/ext2_fs.h index 3a5eb7387d0c9d..fcd42055665788 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 bf4e592e7d2782..9cf8c59b8b88ee 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)