This changes the utimensat() system call to use __kernel_timespec instead of timespec, to allow 32-bit architectures the use of 64-bit time_t in user space, with the compat syscalls providing backwards compatibility support for binaries using utime, utimes, or futimesat.
New libc versions would implement all four system call through the sys_utimensat() kernel interface, and provide backwards compatility for existing source code in user space.
This change is not sufficient to set times beyond 2038 in file systems by itself, a larger follow-up is required to change the timestamp format in inodes to use timespec64, and change all file systems to use that correctly.
Signed-off-by: Arnd Bergmann arnd@arndb.de --- arch/alpha/kernel/osf_sys.c | 2 +- fs/compat.c | 10 +++++----- fs/utimes.c | 19 ++++++++++--------- include/linux/syscalls.h | 2 +- include/linux/time.h | 2 +- init/initramfs.c | 2 +- 6 files changed, 19 insertions(+), 18 deletions(-)
diff --git a/arch/alpha/kernel/osf_sys.c b/arch/alpha/kernel/osf_sys.c index 6824e68d32ad..04368e8129b7 100644 --- a/arch/alpha/kernel/osf_sys.c +++ b/arch/alpha/kernel/osf_sys.c @@ -1068,7 +1068,7 @@ SYSCALL_DEFINE3(osf_setitimer, int, which, struct itimerval32 __user *, in, SYSCALL_DEFINE2(osf_utimes, const char __user *, filename, struct timeval32 __user *, tvs) { - struct timespec tv[2]; + struct timespec64 tv[2];
if (tvs) { struct timeval ktvs[2]; diff --git a/fs/compat.c b/fs/compat.c index 3e7ccc6e3503..3f110ebe9c3b 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -62,7 +62,7 @@ COMPAT_SYSCALL_DEFINE2(utime, const char __user *, filename, struct compat_utimbuf __user *, t) { - struct timespec tv[2]; + struct timespec64 tv[2];
if (t) { if (get_user(tv[0].tv_sec, &t->actime) || @@ -76,11 +76,11 @@ COMPAT_SYSCALL_DEFINE2(utime, const char __user *, filename,
COMPAT_SYSCALL_DEFINE4(utimensat, unsigned int, dfd, const char __user *, filename, struct compat_timespec __user *, t, int, flags) { - struct timespec tv[2]; + struct timespec64 tv[2];
if (t) { - if (compat_get_timespec(&tv[0], &t[0]) || - compat_get_timespec(&tv[1], &t[1])) + if (compat_get_timespec64(&tv[0], &t[0]) || + compat_get_timespec64(&tv[1], &t[1])) return -EFAULT;
if (tv[0].tv_nsec == UTIME_OMIT && tv[1].tv_nsec == UTIME_OMIT) @@ -91,7 +91,7 @@ COMPAT_SYSCALL_DEFINE4(utimensat, unsigned int, dfd, const char __user *, filena
COMPAT_SYSCALL_DEFINE3(futimesat, unsigned int, dfd, const char __user *, filename, struct compat_timeval __user *, t) { - struct timespec tv[2]; + struct timespec64 tv[2];
if (t) { if (get_user(tv[0].tv_sec, &t[0].tv_sec) || diff --git a/fs/utimes.c b/fs/utimes.c index aa138d64560a..a269b5be2418 100644 --- a/fs/utimes.c +++ b/fs/utimes.c @@ -26,7 +26,7 @@ */ SYSCALL_DEFINE2(utime, char __user *, filename, struct utimbuf __user *, times) { - struct timespec tv[2]; + struct timespec64 tv[2];
if (times) { if (get_user(tv[0].tv_sec, ×->actime) || @@ -48,7 +48,7 @@ static bool nsec_valid(long nsec) return nsec >= 0 && nsec <= 999999999; }
-static int utimes_common(struct path *path, struct timespec *times) +static int utimes_common(struct path *path, struct timespec64 *times) { int error; struct iattr newattrs; @@ -68,7 +68,7 @@ static int utimes_common(struct path *path, struct timespec *times) if (times[0].tv_nsec == UTIME_OMIT) newattrs.ia_valid &= ~ATTR_ATIME; else if (times[0].tv_nsec != UTIME_NOW) { - newattrs.ia_atime.tv_sec = times[0].tv_sec; + newattrs.ia_atime.tv_sec = (time_t)times[0].tv_sec; newattrs.ia_atime.tv_nsec = times[0].tv_nsec; newattrs.ia_valid |= ATTR_ATIME_SET; } @@ -76,7 +76,7 @@ static int utimes_common(struct path *path, struct timespec *times) if (times[1].tv_nsec == UTIME_OMIT) newattrs.ia_valid &= ~ATTR_MTIME; else if (times[1].tv_nsec != UTIME_NOW) { - newattrs.ia_mtime.tv_sec = times[1].tv_sec; + newattrs.ia_mtime.tv_sec = (time_t)times[1].tv_sec; newattrs.ia_mtime.tv_nsec = times[1].tv_nsec; newattrs.ia_valid |= ATTR_MTIME_SET; } @@ -133,7 +133,7 @@ out: * must be owner or have write permission. * Else, update from *times, must be owner or super user. */ -long do_utimes(int dfd, const char __user *filename, struct timespec *times, +long do_utimes(int dfd, const char __user *filename, struct timespec64 *times, int flags) { int error = -EINVAL; @@ -183,12 +183,13 @@ out: }
SYSCALL_DEFINE4(utimensat, int, dfd, const char __user *, filename, - struct timespec __user *, utimes, int, flags) + struct __kernel_timespec __user *, utimes, int, flags) { - struct timespec tstimes[2]; + struct timespec64 tstimes[2];
if (utimes) { - if (copy_from_user(&tstimes, utimes, sizeof(tstimes))) + if (get_timespec64(&tstimes[0], &utimes[0]) || + get_timespec64(&tstimes[1], &utimes[1])) return -EFAULT;
/* Nothing to do, we must not even check the path. */ @@ -204,7 +205,7 @@ SYSCALL_DEFINE3(futimesat, int, dfd, const char __user *, filename, struct timeval __user *, utimes) { struct timeval times[2]; - struct timespec tstimes[2]; + struct timespec64 tstimes[2];
if (utimes) { if (copy_from_user(×, utimes, sizeof(times))) diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index 19119800b93c..756ead542240 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -776,7 +776,7 @@ asmlinkage long sys_newfstatat(int dfd, const char __user *filename, 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, - struct timespec __user *utimes, int flags); + struct __kernel_timespec __user *utimes, int flags); asmlinkage long sys_unshare(unsigned long unshare_flags);
asmlinkage long sys_splice(int fd_in, loff_t __user *off_in, diff --git a/include/linux/time.h b/include/linux/time.h index 5b5a64952df7..b29c49196026 100644 --- a/include/linux/time.h +++ b/include/linux/time.h @@ -139,7 +139,7 @@ extern int do_getitimer(int which, struct itimerval *value);
extern unsigned int alarm_setitimer(unsigned int seconds);
-extern long do_utimes(int dfd, const char __user *filename, struct timespec *times, int flags); +extern long do_utimes(int dfd, const char __user *filename, struct timespec64 *times, int flags);
struct tms; extern void do_sys_times(struct tms *); diff --git a/init/initramfs.c b/init/initramfs.c index 10c808e97023..62f8807b8ce3 100644 --- a/init/initramfs.c +++ b/init/initramfs.c @@ -109,7 +109,7 @@ static void __init free_hash(void)
static long __init do_utime(char *filename, time_t mtime) { - struct timespec t[2]; + struct timespec64 t[2];
t[0].tv_sec = mtime; t[0].tv_nsec = 0;