[PATCH] hfs/hfsplus: use documented official timestamp range

From: Arnd Bergmann
Date: Fri Jun 22 2018 - 10:18:19 EST


According to the official documentation for HFS+ [1], inode timestamps
are supposed to cover the time range from 1904 to 2040 as originally
used in classic MacOS.

The traditional Linux usage is to convert the timestamps into an unsigned
32-bit number based on the Unix epoch and from there to a time_t. On
32-bit systems, that wraps the time from 2038 to 1902, so the last
two years of the valid time range become garbled. On 64-bit systems,
all times before 1970 get turned into timestamps between 2038 and 2106,
which is more convenient but also different from the documented behavior.

The same behavior is used in Darwin and presumaby all versions of MacOS X,
as seen in the to_hfs_time() function in [2]. It is unclear whether this
is a bug in the file system code, or intentional but undocumented behavior.

This changes Linux over to the traditional MacOS (pre MacOS X)
behavior. This means all files that are created on MacOS X or Linux
with future timestamps between 2040 and 2106 will now show up as past
dates. Timestamps between 2038 and 2040 will still be represented
incorrectly on 32-bit architectures as times between 1902 and 1904,
but that will be fixed once we have user space with 64-bit time_t.

Cc: stable@xxxxxxxxxxxxxxx
Link: [1] https://developer.apple.com/library/archive/technotes/tn/tn1150.html
Link: [2] https://opensource.apple.com/source/xnu/xnu-344/bsd/hfs/MacOSStubs.c
Suggested-by: Viacheslav Dubeyko <slava@xxxxxxxxxxx>
Signed-off-by: Arnd Bergmann <arnd@xxxxxxxx>
---
Note: This is the patch that Viacheslav asked for, but given how
MacOS X behaves, I'm increasingly thinking this is a bad idea.
---
fs/hfs/hfs_fs.h | 2 +-
fs/hfsplus/hfsplus_fs.h | 5 +++--
2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h
index 6d0783e2e276..39c1f3a43ed8 100644
--- a/fs/hfs/hfs_fs.h
+++ b/fs/hfs/hfs_fs.h
@@ -247,7 +247,7 @@ extern void hfs_mark_mdb_dirty(struct super_block *sb);
*
*/
#define __hfs_u_to_mtime(sec) cpu_to_be32(sec + 2082844800U - sys_tz.tz_minuteswest * 60)
-#define __hfs_m_to_utime(sec) (be32_to_cpu(sec) - 2082844800U + sys_tz.tz_minuteswest * 60)
+#define __hfs_m_to_utime(sec) ((time64_t)be32_to_cpu(sec) - 2082844800U + sys_tz.tz_minuteswest * 60)

#define HFS_I(inode) (container_of(inode, struct hfs_inode_info, vfs_inode))
#define HFS_SB(sb) ((struct hfs_sb_info *)(sb)->s_fs_info)
diff --git a/fs/hfsplus/hfsplus_fs.h b/fs/hfsplus/hfsplus_fs.h
index d9255abafb81..57838ef4dcdc 100644
--- a/fs/hfsplus/hfsplus_fs.h
+++ b/fs/hfsplus/hfsplus_fs.h
@@ -530,8 +530,9 @@ int hfsplus_submit_bio(struct super_block *sb, sector_t sector, void *buf,
void **data, int op, int op_flags);
int hfsplus_read_wrapper(struct super_block *sb);

-/* time macros */
-#define __hfsp_mt2ut(t) (be32_to_cpu(t) - 2082844800U)
+/* time macros: convert between 1904-2040 and 1970-2106 range,
+ * pre-1970 timestamps are interpreted as post-2038 times after wrap-around */
+#define __hfsp_mt2ut(t) ((time64_t)be32_to_cpu(t) - 2082844800U)
#define __hfsp_ut2mt(t) (cpu_to_be32(t + 2082844800U))

/* compatibility */
--
2.9.0