]> git.ipfire.org Git - thirdparty/e2fsprogs.git/commitdiff
fuse2fs: clamp timestamps that are being written to disk
authorDarrick J. Wong <djwong@kernel.org>
Thu, 24 Apr 2025 21:42:52 +0000 (14:42 -0700)
committerTheodore Ts'o <tytso@mit.edu>
Mon, 9 Jun 2025 20:53:01 +0000 (16:53 -0400)
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@kernel.org>
Link: https://lore.kernel.org/r/174553065069.1160461.14751120886781323020.stgit@frogsfrogsfrogs
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
(cherry picked from commit e13395876d63cebf008101b934ee9e5cdaae0150)

lib/ext2fs/ext2_fs.h
misc/fuse2fs.c

index 3a5eb7387d0c9dac18a4ef9e72aaadd961608334..fcd4205566578867e00aa82c64967aec66ce3f66 100644 (file)
@@ -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
  */
index eeb496d1b7d39482ca63b2618e73ccd5779b3723..238804dd4565ede4cc9b26efe3f8aa47b8a69a66 100644 (file)
@@ -209,21 +209,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)                      \
@@ -2843,7 +2865,10 @@ static int op_utimens(const char *path, const struct timespec ctv[2]
                ret = translate_error(fs, 0, err);
                goto out;
        }
-       dbg_printf("%s: ino=%d\n", __func__, ino);
+       dbg_printf("%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)
@@ -2867,9 +2892,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)