diff mbox series

[08/16] fuse2fs: clamp timestamps that are being written to disk

Message ID 174553065069.1160461.14751120886781323020.stgit@frogsfrogsfrogs
State New
Headers show
Series [01/16] fuse2fs: refuse unsupported features | expand

Commit Message

Darrick J. Wong April 24, 2025, 9:42 p.m. UTC
From: Darrick J. Wong <djwong@kernel.org>

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>
---
 lib/ext2fs/ext2_fs.h |    4 ++++
 misc/fuse2fs.c       |   39 ++++++++++++++++++++++++++++++++-------
 2 files changed, 36 insertions(+), 7 deletions(-)
diff mbox series

Patch

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)