diff mbox

[09/19] y2038: introduce struct __kernel_stat

Message ID 1430929826-318934-10-git-send-email-arnd@arndb.de
State Not Applicable
Headers show

Commit Message

Arnd Bergmann May 6, 2015, 4:30 p.m. UTC
The stat() family of system calls uses up to three different data
structures: 'struct __kernel_oldstat', 'struct stat', and 'struct
stat64', which were extended in various ways over time. Unfortunately,
on most 32-bit architectures and even some 64-bit machines (parisc),
all of them use 32-bit timestamps, which are already broken because
they cannot represent the range of times that can be stored on
typical file systems. When time_t overflows, things of course become
much worse.

This introduces a fourth data structure, 'struct __kernel_stat', which
is supposed to match the layout of 'struct stat' on 64-bit architectures,
so the compat handlers can be shared between native 64-bit and compat
32-bit syscalls. On architectures that are always 32-bit, the asm-generic
variant can be used.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 arch/alpha/include/uapi/asm/stat.h     |  4 +++
 arch/arm/include/uapi/asm/stat.h       |  2 ++
 arch/avr32/include/uapi/asm/stat.h     |  2 ++
 arch/blackfin/include/uapi/asm/stat.h  |  2 ++
 arch/cris/include/uapi/asm/stat.h      |  2 ++
 arch/frv/include/uapi/asm/stat.h       |  2 ++
 arch/ia64/include/uapi/asm/stat.h      |  4 +++
 arch/m32r/include/uapi/asm/stat.h      |  1 +
 arch/m68k/include/uapi/asm/stat.h      |  2 ++
 arch/mips/include/uapi/asm/stat.h      |  1 +
 arch/mn10300/include/uapi/asm/stat.h   |  2 ++
 arch/parisc/include/uapi/asm/stat.h    |  1 +
 arch/powerpc/include/uapi/asm/stat.h   | 25 +++++++++++++++++
 arch/s390/include/uapi/asm/stat.h      | 24 +++++++++++++++++
 arch/sh/include/uapi/asm/stat.h        |  2 ++
 arch/sparc/include/uapi/asm/stat.h     | 28 +++++++++++++++++++
 arch/x86/include/uapi/asm/stat.h       | 49 ++++++++++++++++++++++++----------
 arch/xtensa/include/uapi/asm/stat.h    |  2 ++
 include/linux/syscalls.h               | 11 ++++----
 include/uapi/asm-generic/kernel_stat.h | 36 +++++++++++++++++++++++++
 include/uapi/asm-generic/stat.h        | 12 +++++----
 21 files changed, 189 insertions(+), 25 deletions(-)
 create mode 100644 include/uapi/asm-generic/kernel_stat.h
diff mbox

Patch

diff --git a/arch/alpha/include/uapi/asm/stat.h b/arch/alpha/include/uapi/asm/stat.h
index 07ad3e6b3f3e..ca566bc620f6 100644
--- a/arch/alpha/include/uapi/asm/stat.h
+++ b/arch/alpha/include/uapi/asm/stat.h
@@ -1,6 +1,10 @@ 
 #ifndef _ALPHA_STAT_H
 #define _ALPHA_STAT_H
 
+#ifndef __kernel_stat
+#define __kernel_stat stat
+#endif
+
 struct stat {
 	unsigned int	st_dev;
 	unsigned int	st_ino;
diff --git a/arch/arm/include/uapi/asm/stat.h b/arch/arm/include/uapi/asm/stat.h
index 42c0c13999d5..537a12553dd8 100644
--- a/arch/arm/include/uapi/asm/stat.h
+++ b/arch/arm/include/uapi/asm/stat.h
@@ -1,6 +1,8 @@ 
 #ifndef _ASMARM_STAT_H
 #define _ASMARM_STAT_H
 
+#include <asm-generic/kernel_stat.h>
+
 struct __old_kernel_stat {
 	unsigned short st_dev;
 	unsigned short st_ino;
diff --git a/arch/avr32/include/uapi/asm/stat.h b/arch/avr32/include/uapi/asm/stat.h
index c06acef7fce7..2b528ca17985 100644
--- a/arch/avr32/include/uapi/asm/stat.h
+++ b/arch/avr32/include/uapi/asm/stat.h
@@ -8,6 +8,8 @@ 
 #ifndef _UAPI__ASM_AVR32_STAT_H
 #define _UAPI__ASM_AVR32_STAT_H
 
+#include <asm-generic/kernel_stat.h>
+
 struct __old_kernel_stat {
         unsigned short st_dev;
         unsigned short st_ino;
diff --git a/arch/blackfin/include/uapi/asm/stat.h b/arch/blackfin/include/uapi/asm/stat.h
index d3068a750b94..99ee343aec23 100644
--- a/arch/blackfin/include/uapi/asm/stat.h
+++ b/arch/blackfin/include/uapi/asm/stat.h
@@ -7,6 +7,8 @@ 
 #ifndef _UAPI_BFIN_STAT_H
 #define _UAPI_BFIN_STAT_H
 
+#include <asm-generic/kernel_stat.h>
+
 struct stat {
 	unsigned short st_dev;
 	unsigned short __pad1;
diff --git a/arch/cris/include/uapi/asm/stat.h b/arch/cris/include/uapi/asm/stat.h
index 9e558cc3c43b..4837884cd2d3 100644
--- a/arch/cris/include/uapi/asm/stat.h
+++ b/arch/cris/include/uapi/asm/stat.h
@@ -1,6 +1,8 @@ 
 #ifndef _CRIS_STAT_H
 #define _CRIS_STAT_H
 
+#include <asm-generic/kernel_stat.h>
+
 /* Keep this a verbatim copy of i386 version; tweak CRIS-specific bits in
    the kernel if necessary.  */
 
diff --git a/arch/frv/include/uapi/asm/stat.h b/arch/frv/include/uapi/asm/stat.h
index ce56de9b37ba..5448b198fbb6 100644
--- a/arch/frv/include/uapi/asm/stat.h
+++ b/arch/frv/include/uapi/asm/stat.h
@@ -1,6 +1,8 @@ 
 #ifndef _ASM_STAT_H
 #define _ASM_STAT_H
 
+#include <asm-generic/kernel_stat.h>
+
 struct __old_kernel_stat {
 	unsigned short st_dev;
 	unsigned short st_ino;
diff --git a/arch/ia64/include/uapi/asm/stat.h b/arch/ia64/include/uapi/asm/stat.h
index 367bb90cdffa..cde68a31e183 100644
--- a/arch/ia64/include/uapi/asm/stat.h
+++ b/arch/ia64/include/uapi/asm/stat.h
@@ -1,6 +1,10 @@ 
 #ifndef _ASM_IA64_STAT_H
 #define _ASM_IA64_STAT_H
 
+#ifndef __kernel_stat
+#define __kernel_stat stat
+#endif
+
 /*
  * Modified 1998, 1999
  *	David Mosberger-Tang <davidm@hpl.hp.com>, Hewlett-Packard Co
diff --git a/arch/m32r/include/uapi/asm/stat.h b/arch/m32r/include/uapi/asm/stat.h
index 98470fe483b6..d0ffa70f73c0 100644
--- a/arch/m32r/include/uapi/asm/stat.h
+++ b/arch/m32r/include/uapi/asm/stat.h
@@ -2,6 +2,7 @@ 
 #define _ASM_M32R_STAT_H
 
 #include <asm/byteorder.h>
+#include <asm-generic/kernel_stat.h>
 
 struct __old_kernel_stat {
 	unsigned short st_dev;
diff --git a/arch/m68k/include/uapi/asm/stat.h b/arch/m68k/include/uapi/asm/stat.h
index dd38bc2e9f98..6f455db47b4e 100644
--- a/arch/m68k/include/uapi/asm/stat.h
+++ b/arch/m68k/include/uapi/asm/stat.h
@@ -1,6 +1,8 @@ 
 #ifndef _M68K_STAT_H
 #define _M68K_STAT_H
 
+#include <asm-generic/kernel_stat.h>
+
 struct __old_kernel_stat {
 	unsigned short st_dev;
 	unsigned short st_ino;
diff --git a/arch/mips/include/uapi/asm/stat.h b/arch/mips/include/uapi/asm/stat.h
index b47bc541bbc0..53e58fbd83fa 100644
--- a/arch/mips/include/uapi/asm/stat.h
+++ b/arch/mips/include/uapi/asm/stat.h
@@ -12,6 +12,7 @@ 
 #include <linux/types.h>
 
 #include <asm/sgidefs.h>
+#include <asm-generic/kernel_stat.h>
 
 #if (_MIPS_SIM == _MIPS_SIM_ABI32) || (_MIPS_SIM == _MIPS_SIM_NABI32)
 
diff --git a/arch/mn10300/include/uapi/asm/stat.h b/arch/mn10300/include/uapi/asm/stat.h
index 63ff8371cf2c..af3b4d6b7b7a 100644
--- a/arch/mn10300/include/uapi/asm/stat.h
+++ b/arch/mn10300/include/uapi/asm/stat.h
@@ -1,6 +1,8 @@ 
 #ifndef _ASM_STAT_H
 #define _ASM_STAT_H
 
+#include <asm-generic/kernel_stat.h>
+
 struct __old_kernel_stat {
 	unsigned short st_dev;
 	unsigned short st_ino;
diff --git a/arch/parisc/include/uapi/asm/stat.h b/arch/parisc/include/uapi/asm/stat.h
index b606b366d0a7..f06ce7ba0115 100644
--- a/arch/parisc/include/uapi/asm/stat.h
+++ b/arch/parisc/include/uapi/asm/stat.h
@@ -2,6 +2,7 @@ 
 #define _PARISC_STAT_H
 
 #include <linux/types.h>
+#include <asm-generic/kernel_stat.h>
 
 struct stat {
 	unsigned int	st_dev;		/* dev_t is 32 bits on parisc */
diff --git a/arch/powerpc/include/uapi/asm/stat.h b/arch/powerpc/include/uapi/asm/stat.h
index 84880b80cc1c..248d8072267f 100644
--- a/arch/powerpc/include/uapi/asm/stat.h
+++ b/arch/powerpc/include/uapi/asm/stat.h
@@ -78,4 +78,29 @@  struct stat64 {
 	unsigned int	__unused5;
 };
 
+#ifndef __kernel_stat
+/* this matches the powerpc64 'struct stat' for compat tasks */
+struct __kernel_stat {
+	unsigned long long	st_dev;
+	unsigned long long	st_ino;
+	unsigned long long	st_nlink;
+	unsigned int		st_mode;
+	unsigned int		st_uid;
+	unsigned int		st_gid;
+	unsigned long long	st_rdev;
+	unsigned long long	st_size;
+	unsigned long long	st_blksize;
+	unsigned long long	st_blocks;
+	unsigned long long	st_atime;
+	unsigned long long	st_atime_nsec;
+	unsigned long long	st_mtime;
+	unsigned long long	st_mtime_nsec;
+	unsigned long long	st_ctime;
+	unsigned long long	st_ctime_nsec;
+	unsigned long long	__unused4;
+	unsigned long long	__unused5;
+	unsigned long long	__unused6;
+};
+#endif
+
 #endif /* _ASM_POWERPC_STAT_H */
diff --git a/arch/s390/include/uapi/asm/stat.h b/arch/s390/include/uapi/asm/stat.h
index b4ca97d91466..d4c2711249dd 100644
--- a/arch/s390/include/uapi/asm/stat.h
+++ b/arch/s390/include/uapi/asm/stat.h
@@ -100,4 +100,28 @@  struct stat {
 
 #define STAT_HAVE_NSEC 1
 
+/* same layout as 'struct stat on s390x' for both 32-bit and 64-bit tasks */
+#ifndef __kernel_stat
+struct __kernel_stat {
+	unsigned long long st_dev;
+	unsigned long long st_ino;
+	unsigned long long st_nlink;
+	unsigned int	   st_mode;
+	unsigned int	   st_uid;
+	unsigned int	   st_gid;
+	unsigned int	   __pad1;
+	unsigned long long st_rdev;
+	unsigned long long st_size;
+	unsigned long long st_atime;
+	unsigned long long st_atime_nsec;
+	unsigned long long st_mtime;
+	unsigned long long st_mtime_nsec;
+	unsigned long long st_ctime;
+	unsigned long long st_ctime_nsec;
+	unsigned long long st_blksize;
+	long long	   st_blocks;
+	unsigned long long __unused[3];
+};
+#endif
+
 #endif
diff --git a/arch/sh/include/uapi/asm/stat.h b/arch/sh/include/uapi/asm/stat.h
index e1810cc6e3da..a13ffbcccd50 100644
--- a/arch/sh/include/uapi/asm/stat.h
+++ b/arch/sh/include/uapi/asm/stat.h
@@ -1,6 +1,8 @@ 
 #ifndef __ASM_SH_STAT_H
 #define __ASM_SH_STAT_H
 
+#include <asm-generic/kernel_stat.h>
+
 struct __old_kernel_stat {
 	unsigned short st_dev;
 	unsigned short st_ino;
diff --git a/arch/sparc/include/uapi/asm/stat.h b/arch/sparc/include/uapi/asm/stat.h
index a232e9e1f4e5..6d19c7bdc641 100644
--- a/arch/sparc/include/uapi/asm/stat.h
+++ b/arch/sparc/include/uapi/asm/stat.h
@@ -104,4 +104,32 @@  struct stat64 {
 	unsigned int	__unused5;
 };
 #endif /* defined(__sparc__) && defined(__arch64__) */
+
+#ifndef __kernel_stat
+/* This matches the sparc64 'struct stat64' in compat tasks */
+struct __kernel_stat {
+	unsigned long long	st_dev;
+	unsigned long long	st_ino;
+	unsigned long long	st_nlink;
+
+	unsigned int		st_mode;
+	unsigned int		st_uid;
+	unsigned int		st_gid;
+	unsigned int		__pad0;
+
+	unsigned long long	st_rdev;
+	long long		st_size;
+	long long		st_blksize;
+	long long		st_blocks;
+
+	unsigned long long	st_atime;
+	unsigned long long	st_atime_nsec;
+	unsigned long long	st_mtime;
+	unsigned long long	st_mtime_nsec;
+	unsigned long long	st_ctime;
+	unsigned long long	st_ctime_nsec;
+	long long		__unused[3];
+};
+#endif
+
 #endif /* __SPARC_STAT_H */
diff --git a/arch/x86/include/uapi/asm/stat.h b/arch/x86/include/uapi/asm/stat.h
index bc03eb5d6360..5d5754fc3d36 100644
--- a/arch/x86/include/uapi/asm/stat.h
+++ b/arch/x86/include/uapi/asm/stat.h
@@ -27,12 +27,6 @@  struct stat {
 	unsigned long  __unused5;
 };
 
-/* We don't need to memset the whole thing just to initialize the padding */
-#define INIT_STRUCT_STAT_PADDING(st) do {	\
-	st.__unused4 = 0;			\
-	st.__unused5 = 0;			\
-} while (0)
-
 #define STAT64_HAS_BROKEN_ST_INO	1
 
 /* This matches struct stat64 in glibc2.1, hence the absolutely
@@ -102,14 +96,6 @@  struct stat {
 	__kernel_long_t		__unused[3];
 };
 
-/* We don't need to memset the whole thing just to initialize the padding */
-#define INIT_STRUCT_STAT_PADDING(st) do {	\
-	st.__pad0 = 0;				\
-	st.__unused[0] = 0;			\
-	st.__unused[1] = 0;			\
-	st.__unused[2] = 0;			\
-} while (0)
-
 #endif
 
 /* for 32bit emulation and 32 bit kernels */
@@ -134,4 +120,39 @@  struct __old_kernel_stat {
 #endif
 };
 
+#ifndef __kernel_stat
+/* This matches the 64-bit version of 'struct stat' on i386 */
+struct __kernel_stat {
+	unsigned long long	st_dev;
+	unsigned long long	st_ino;
+	unsigned long long	st_nlink;
+
+	unsigned int		st_mode;
+	unsigned int		st_uid;
+	unsigned int		st_gid;
+	unsigned int		__pad0;
+	unsigned long long	st_rdev;
+	long long		st_size;
+	long long		st_blksize;
+	long long		st_blocks;	/* Number 512-byte blocks allocated. */
+
+	unsigned long long	st_atime;
+	unsigned long long	st_atime_nsec;
+	unsigned long long	st_mtime;
+	unsigned long long	st_mtime_nsec;
+	unsigned long long	st_ctime;
+	unsigned long long	st_ctime_nsec;
+	long long		__unused[3];
+};
+
+/* We don't need to memset the whole thing just to initialize the padding */
+#define INIT_STRUCT_STAT_PADDING(st) do {	\
+	st.__pad0 = 0;				\
+	st.__unused[0] = 0;			\
+	st.__unused[1] = 0;			\
+	st.__unused[2] = 0;			\
+} while (0)
+
+#endif
+
 #endif /* _ASM_X86_STAT_H */
diff --git a/arch/xtensa/include/uapi/asm/stat.h b/arch/xtensa/include/uapi/asm/stat.h
index c4992038cee0..8d9c1d9d82d0 100644
--- a/arch/xtensa/include/uapi/asm/stat.h
+++ b/arch/xtensa/include/uapi/asm/stat.h
@@ -11,6 +11,8 @@ 
 #ifndef _XTENSA_STAT_H
 #define _XTENSA_STAT_H
 
+#include <asm-generic/kernel_stat.h>
+
 #define STAT_HAVE_NSEC 1
 
 struct stat {
diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h
index 76d1e38aabe1..71b574b0365e 100644
--- a/include/linux/syscalls.h
+++ b/include/linux/syscalls.h
@@ -44,8 +44,6 @@  struct semaphore;
 struct sembuf;
 struct shmid_ds;
 struct sockaddr;
-struct stat;
-struct stat64;
 struct statfs;
 struct statfs64;
 struct __sysctl_args;
@@ -79,6 +77,7 @@  union bpf_attr;
 #include <linux/quota.h>
 #include <linux/key.h>
 #include <trace/syscall.h>
+#include <linux/stat.h>
 
 /*
  * __MAP - apply a macro to syscall arguments
@@ -405,10 +404,10 @@  asmlinkage long sys_lstat(const char __user *filename,
 asmlinkage long sys_fstat(unsigned int fd,
 			struct __old_kernel_stat __user *statbuf);
 asmlinkage long sys_newstat(const char __user *filename,
-				struct stat __user *statbuf);
+				struct __kernel_stat __user *statbuf);
 asmlinkage long sys_newlstat(const char __user *filename,
-				struct stat __user *statbuf);
-asmlinkage long sys_newfstat(unsigned int fd, struct stat __user *statbuf);
+				struct __kernel_stat __user *statbuf);
+asmlinkage long sys_newfstat(unsigned int fd, struct __kernel_stat __user *statbuf);
 asmlinkage long sys_ustat(unsigned dev, struct ustat __user *ubuf);
 #if defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64)
 asmlinkage long sys_stat64(const char __user *filename,
@@ -774,7 +773,7 @@  asmlinkage long sys_fchownat(int dfd, const char __user *filename, uid_t user,
 asmlinkage long sys_openat(int dfd, const char __user *filename, int flags,
 			   umode_t mode);
 asmlinkage long sys_newfstatat(int dfd, const char __user *filename,
-			       struct stat __user *statbuf, int flag);
+			       struct __kernel_stat __user *statbuf, int flag);
 asmlinkage long sys_readlinkat(int dfd, const char __user *path, char __user *buf,
 			       int bufsiz);
 asmlinkage long sys_utimensat(int dfd, const char __user *filename,
diff --git a/include/uapi/asm-generic/kernel_stat.h b/include/uapi/asm-generic/kernel_stat.h
new file mode 100644
index 000000000000..d1db22583046
--- /dev/null
+++ b/include/uapi/asm-generic/kernel_stat.h
@@ -0,0 +1,36 @@ 
+#ifndef __ASM_GENERIC_KERNEL_STAT_H
+#define __ASM_GENERIC_KERNEL_STAT_H
+
+/*
+ * The new structure that works on both 32-bit and 64-bit and survives y2038
+ * The layout matches 'struct stat' from asm-generic/stat.h on 64-bit
+ * architecture, but is identical on 32-bit architectures and uses 64-bit
+ * st_?time members so we don't wrap around in 2038.
+ */
+
+#ifndef __kernel_stat
+struct __kernel_stat {
+	unsigned long long st_dev;	/* Device.  */
+	unsigned long long st_ino;	/* File serial number.  */
+	unsigned int	   st_mode;	/* File mode.  */
+	unsigned int	   st_nlink;	/* Link count.  */
+	unsigned int	   st_uid;	/* User ID of the file's owner.  */
+	unsigned int	   st_gid;	/* Group ID of the file's group. */
+	unsigned long long st_rdev;	/* Device number, if device.  */
+	unsigned long long __pad1;
+	long long	   st_size;	/* Size of file, in bytes.  */
+	int		   st_blksize;	/* Optimal block size for I/O.  */
+	int		   __pad2;
+	long long	   st_blocks;	/* Number 512-byte blocks allocated. */
+	long long	   st_atime;	/* Time of last access.  */
+	unsigned long long st_atime_nsec;
+	long long	   st_mtime;	/* Time of last modification.  */
+	unsigned long long st_mtime_nsec;
+	long long	   st_ctime;	/* Time of last status change.  */
+	unsigned long long st_ctime_nsec;
+	unsigned int	   __unused4;
+	unsigned int	   __unused5;
+};
+#endif
+
+#endif /* __ASM_GENERIC_KERNEL_STAT_H */
diff --git a/include/uapi/asm-generic/stat.h b/include/uapi/asm-generic/stat.h
index bd8cad21998e..64c32ba7c1a9 100644
--- a/include/uapi/asm-generic/stat.h
+++ b/include/uapi/asm-generic/stat.h
@@ -8,15 +8,17 @@ 
  *
  * stat64 is copied from powerpc64, with explicit padding added.
  * stat is the same structure layout on 64-bit, without the 'long long'
- * types.
+ * types. Unfortunately, we started out using '32-bit st_*time here,
+ * which was a huge mistake.
  *
- * By convention, 64 bit architectures use the stat interface, while
- * 32 bit architectures use the stat64 interface. Note that we don't
- * provide an __old_kernel_stat here, which new architecture should
- * not have to start with.
+ * New architectures use only the __kernel_stat interface with
+ * 'sys_newfstatat', everything else is provided for backwards
+ * compatibility. Note that we don't provide an __old_kernel_stat here,
+ * which new architecture should not have to start with.
  */
 
 #include <asm/bitsperlong.h>
+#include <asm-generic/kernel_stat.h>
 
 #define STAT_HAVE_NSEC 1