diff mbox

[1/3] hfs: stop using timespec based interfaces

Message ID 20180619160223.4108556-1-arnd@arndb.de (mailing list archive)
State New, archived
Headers show

Commit Message

Arnd Bergmann June 19, 2018, 4:02 p.m. UTC
The native HFS timestamps overflow in year 2040, two years after the Unix
y2038 overflow. However, the way that the conversion between on-disk
timestamps and in-kernel timestamps was implemented, 64-bit machines
actually ended up converting negative UTC timestamps (1902 through 1969)
into times between 2038 and 2106.

Rather than making all machines faithfully represent timestamps in the
ancient past but break after 2040, this changes the file system to
always use the unsigned UTC interpretation, reading back times between
1970 and 2106.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 fs/hfs/hfs_fs.h | 6 ++++--
 fs/hfs/inode.c  | 4 ++--
 2 files changed, 6 insertions(+), 4 deletions(-)

Comments

Viacheslav Dubeyko June 19, 2018, 5:03 p.m. UTC | #1
On Tue, 2018-06-19 at 18:02 +0200, Arnd Bergmann wrote:
> The native HFS timestamps overflow in year 2040, two years after the
> Unix
> y2038 overflow. However, the way that the conversion between on-disk
> timestamps and in-kernel timestamps was implemented, 64-bit machines
> actually ended up converting negative UTC timestamps (1902 through
> 1969)
> into times between 2038 and 2106.
> 
> Rather than making all machines faithfully represent timestamps in
> the
> ancient past but break after 2040, this changes the file system to
> always use the unsigned UTC interpretation, reading back times
> between
> 1970 and 2106.
> 

The trouble with HFS and HFS+ that the specification [1] declares this:

"HFS Plus stores dates in several data structures, including the volume
header and catalog records. These dates are stored in unsigned 32-bit
integers (UInt32) containing the number of seconds since midnight,
January 1, 1904, GMT. This is slightly different from HFS, where the
value represents local time. The maximum representable date is February
6, 2040 at 06:28:15 GMT."

So, I am not sure that we are able to support later dates because such
timestamps cannot be stored on HFS/HFS+ volumes and will be
incompatible with Mac OS X. Also, I am not sure that anybody will use
HFS/HFS+ after 2040.

Thanks,
Vyacheslav Dubeyko.

[1] https://developer.apple.com/library/archive/technotes/tn/tn1150.html


> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
>  fs/hfs/hfs_fs.h | 6 ++++--
>  fs/hfs/inode.c  | 4 ++--
>  2 files changed, 6 insertions(+), 4 deletions(-)
> 
> diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h
> index 6d0783e2e276..ff432931a5b1 100644
> --- a/fs/hfs/hfs_fs.h
> +++ b/fs/hfs/hfs_fs.h
> @@ -245,6 +245,8 @@ extern void hfs_mark_mdb_dirty(struct super_block
> *sb);
>   *	Unix:	unsigned lil-endian since 00:00 GMT, Jan. 1,
> 1970
>   *	mac:	unsigned big-endian since 00:00 GMT, Jan. 1,
> 1904
>   *
> + * We treat all timestamps before 1970 as times after 2038, so this
> + * actually works until year 2106
>   */
>  #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)
> @@ -252,9 +254,9 @@ extern void hfs_mark_mdb_dirty(struct super_block
> *sb);
>  #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)
>  
> -#define hfs_m_to_utime(time)	(struct timespec){ .tv_sec =
> __hfs_m_to_utime(time) }
> +#define hfs_m_to_utime(time)	(struct timespec64){ .tv_sec =
> __hfs_m_to_utime(time) }
>  #define hfs_u_to_mtime(time)	__hfs_u_to_mtime((time).tv_sec)
> -#define hfs_mtime()		__hfs_u_to_mtime(get_seconds())
> +#define hfs_mtime()		__hfs_u_to_mtime(ktime_get_real_s
> econds())
>  
>  static inline const char *hfs_mdb_name(struct super_block *sb)
>  {
> diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
> index 2a16111d312f..b3309b83371a 100644
> --- a/fs/hfs/inode.c
> +++ b/fs/hfs/inode.c
> @@ -351,7 +351,7 @@ static int hfs_read_inode(struct inode *inode,
> void *data)
>  		inode->i_mode &= ~hsb->s_file_umask;
>  		inode->i_mode |= S_IFREG;
>  		inode->i_ctime = inode->i_atime = inode->i_mtime =
> -				timespec_to_timespec64(hfs_m_to_utim
> e(rec->file.MdDat));
> +				hfs_m_to_utime(rec->file.MdDat);
>  		inode->i_op = &hfs_file_inode_operations;
>  		inode->i_fop = &hfs_file_operations;
>  		inode->i_mapping->a_ops = &hfs_aops;
> @@ -362,7 +362,7 @@ static int hfs_read_inode(struct inode *inode,
> void *data)
>  		HFS_I(inode)->fs_blocks = 0;
>  		inode->i_mode = S_IFDIR | (S_IRWXUGO & ~hsb-
> >s_dir_umask);
>  		inode->i_ctime = inode->i_atime = inode->i_mtime =
> -				timespec_to_timespec64(hfs_m_to_utim
> e(rec->dir.MdDat));
> +				hfs_m_to_utime(rec->dir.MdDat);
>  		inode->i_op = &hfs_dir_inode_operations;
>  		inode->i_fop = &hfs_dir_operations;
>  		break;
Ernesto A. Fernández July 31, 2018, 11:37 p.m. UTC | #2
If you decide to go with this series after all:

On Tue, Jun 19, 2018 at 06:02:07PM +0200, Arnd Bergmann wrote:
> The native HFS timestamps overflow in year 2040, two years after the Unix
> y2038 overflow. However, the way that the conversion between on-disk
> timestamps and in-kernel timestamps was implemented, 64-bit machines
> actually ended up converting negative UTC timestamps (1902 through 1969)
> into times between 2038 and 2106.
> 
> Rather than making all machines faithfully represent timestamps in the
> ancient past but break after 2040, this changes the file system to
> always use the unsigned UTC interpretation, reading back times between
> 1970 and 2106.
> 
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
>  fs/hfs/hfs_fs.h | 6 ++++--
>  fs/hfs/inode.c  | 4 ++--
>  2 files changed, 6 insertions(+), 4 deletions(-)
> 
> diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h
> index 6d0783e2e276..ff432931a5b1 100644
> --- a/fs/hfs/hfs_fs.h
> +++ b/fs/hfs/hfs_fs.h
> @@ -245,6 +245,8 @@ extern void hfs_mark_mdb_dirty(struct super_block *sb);
>   *	Unix:	unsigned lil-endian since 00:00 GMT, Jan. 1, 1970
>   *	mac:	unsigned big-endian since 00:00 GMT, Jan. 1, 1904
>   *
> + * We treat all timestamps before 1970 as times after 2038, so this
							 ^^^^
Same as the hfsplus patch, I find this confusing. I think it should say 2040.


Everything else looks good.

Reviewed-by: Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>

> + * actually works until year 2106
>   */
>  #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)
> @@ -252,9 +254,9 @@ extern void hfs_mark_mdb_dirty(struct super_block *sb);
>  #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)
>  
> -#define hfs_m_to_utime(time)	(struct timespec){ .tv_sec = __hfs_m_to_utime(time) }
> +#define hfs_m_to_utime(time)	(struct timespec64){ .tv_sec = __hfs_m_to_utime(time) }
>  #define hfs_u_to_mtime(time)	__hfs_u_to_mtime((time).tv_sec)
> -#define hfs_mtime()		__hfs_u_to_mtime(get_seconds())
> +#define hfs_mtime()		__hfs_u_to_mtime(ktime_get_real_seconds())
>  
>  static inline const char *hfs_mdb_name(struct super_block *sb)
>  {
> diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
> index 2a16111d312f..b3309b83371a 100644
> --- a/fs/hfs/inode.c
> +++ b/fs/hfs/inode.c
> @@ -351,7 +351,7 @@ static int hfs_read_inode(struct inode *inode, void *data)
>  		inode->i_mode &= ~hsb->s_file_umask;
>  		inode->i_mode |= S_IFREG;
>  		inode->i_ctime = inode->i_atime = inode->i_mtime =
> -				timespec_to_timespec64(hfs_m_to_utime(rec->file.MdDat));
> +				hfs_m_to_utime(rec->file.MdDat);
>  		inode->i_op = &hfs_file_inode_operations;
>  		inode->i_fop = &hfs_file_operations;
>  		inode->i_mapping->a_ops = &hfs_aops;
> @@ -362,7 +362,7 @@ static int hfs_read_inode(struct inode *inode, void *data)
>  		HFS_I(inode)->fs_blocks = 0;
>  		inode->i_mode = S_IFDIR | (S_IRWXUGO & ~hsb->s_dir_umask);
>  		inode->i_ctime = inode->i_atime = inode->i_mtime =
> -				timespec_to_timespec64(hfs_m_to_utime(rec->dir.MdDat));
> +				hfs_m_to_utime(rec->dir.MdDat);
>  		inode->i_op = &hfs_dir_inode_operations;
>  		inode->i_fop = &hfs_dir_operations;
>  		break;
> -- 
> 2.9.0
>
diff mbox

Patch

diff --git a/fs/hfs/hfs_fs.h b/fs/hfs/hfs_fs.h
index 6d0783e2e276..ff432931a5b1 100644
--- a/fs/hfs/hfs_fs.h
+++ b/fs/hfs/hfs_fs.h
@@ -245,6 +245,8 @@  extern void hfs_mark_mdb_dirty(struct super_block *sb);
  *	Unix:	unsigned lil-endian since 00:00 GMT, Jan. 1, 1970
  *	mac:	unsigned big-endian since 00:00 GMT, Jan. 1, 1904
  *
+ * We treat all timestamps before 1970 as times after 2038, so this
+ * actually works until year 2106
  */
 #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)
@@ -252,9 +254,9 @@  extern void hfs_mark_mdb_dirty(struct super_block *sb);
 #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)
 
-#define hfs_m_to_utime(time)	(struct timespec){ .tv_sec = __hfs_m_to_utime(time) }
+#define hfs_m_to_utime(time)	(struct timespec64){ .tv_sec = __hfs_m_to_utime(time) }
 #define hfs_u_to_mtime(time)	__hfs_u_to_mtime((time).tv_sec)
-#define hfs_mtime()		__hfs_u_to_mtime(get_seconds())
+#define hfs_mtime()		__hfs_u_to_mtime(ktime_get_real_seconds())
 
 static inline const char *hfs_mdb_name(struct super_block *sb)
 {
diff --git a/fs/hfs/inode.c b/fs/hfs/inode.c
index 2a16111d312f..b3309b83371a 100644
--- a/fs/hfs/inode.c
+++ b/fs/hfs/inode.c
@@ -351,7 +351,7 @@  static int hfs_read_inode(struct inode *inode, void *data)
 		inode->i_mode &= ~hsb->s_file_umask;
 		inode->i_mode |= S_IFREG;
 		inode->i_ctime = inode->i_atime = inode->i_mtime =
-				timespec_to_timespec64(hfs_m_to_utime(rec->file.MdDat));
+				hfs_m_to_utime(rec->file.MdDat);
 		inode->i_op = &hfs_file_inode_operations;
 		inode->i_fop = &hfs_file_operations;
 		inode->i_mapping->a_ops = &hfs_aops;
@@ -362,7 +362,7 @@  static int hfs_read_inode(struct inode *inode, void *data)
 		HFS_I(inode)->fs_blocks = 0;
 		inode->i_mode = S_IFDIR | (S_IRWXUGO & ~hsb->s_dir_umask);
 		inode->i_ctime = inode->i_atime = inode->i_mtime =
-				timespec_to_timespec64(hfs_m_to_utime(rec->dir.MdDat));
+				hfs_m_to_utime(rec->dir.MdDat);
 		inode->i_op = &hfs_dir_inode_operations;
 		inode->i_fop = &hfs_dir_operations;
 		break;