git: 43ee9ad56701 - stable/13 - linux(4): Implement utimensat_time64 system call.

From: Dmitry Chagin <dchagin_at_FreeBSD.org>
Date: Fri, 17 Jun 2022 19:28:53 UTC
The branch stable/13 has been updated by dchagin:

URL: https://cgit.FreeBSD.org/src/commit/?id=43ee9ad56701d02f03681e3eacb177cafd54d261

commit 43ee9ad56701d02f03681e3eacb177cafd54d261
Author:     Dmitry Chagin <dchagin@FreeBSD.org>
AuthorDate: 2021-06-07 01:54:30 +0000
Commit:     Dmitry Chagin <dchagin@FreeBSD.org>
CommitDate: 2022-06-17 19:27:55 +0000

    linux(4): Implement utimensat_time64 system call.
    
    MFC after:      2 weeks
    
    (cherry picked from commit e4bffb80bbc6a2e4b3be89aefcbd5bb2c2fc0ba0)
---
 sys/amd64/linux32/linux32_dummy_machdep.c |   1 -
 sys/amd64/linux32/syscalls.master         |   7 +-
 sys/compat/linux/linux_misc.c             | 181 ++++++++++++++++++++----------
 sys/i386/linux/linux_dummy_machdep.c      |   1 -
 sys/i386/linux/syscalls.master            |   7 +-
 5 files changed, 134 insertions(+), 63 deletions(-)

diff --git a/sys/amd64/linux32/linux32_dummy_machdep.c b/sys/amd64/linux32/linux32_dummy_machdep.c
index 5eb54fefd276..be07eb033e77 100644
--- a/sys/amd64/linux32/linux32_dummy_machdep.c
+++ b/sys/amd64/linux32/linux32_dummy_machdep.c
@@ -76,7 +76,6 @@ DUMMY(timer_gettime64);
 DUMMY(timer_settime64);
 DUMMY(timerfd_gettime64);
 DUMMY(timerfd_settime64);
-DUMMY(utimensat_time64);
 DUMMY(pselect6_time64);
 DUMMY(ppoll_time64);
 DUMMY(io_pgetevents_time64);
diff --git a/sys/amd64/linux32/syscalls.master b/sys/amd64/linux32/syscalls.master
index d7002ed5ac72..67591b487652 100644
--- a/sys/amd64/linux32/syscalls.master
+++ b/sys/amd64/linux32/syscalls.master
@@ -2368,7 +2368,12 @@
 		int linux_timerfd_settime64(void);
 	}
 412	AUE_NULL	STD {
-		int linux_utimensat_time64(void);
+		int linux_utimensat_time64(
+		    l_int dfd,
+		    const char *pathname,
+		    const struct l_timespec64 *times64,
+		    l_int flags
+		);
 	}
 413	AUE_NULL	STD {
 		int linux_pselect6_time64(void);
diff --git a/sys/compat/linux/linux_misc.c b/sys/compat/linux/linux_misc.c
index 17a45b5e5b7a..4549abbeb0eb 100644
--- a/sys/compat/linux/linux_misc.c
+++ b/sys/compat/linux/linux_misc.c
@@ -130,7 +130,14 @@ struct l_pselect6arg {
 	l_size_t	ss_len;
 };
 
-static int	linux_utimensat_nsec_valid(l_long);
+static int	linux_utimensat_lts_to_ts(struct l_timespec *,
+			struct timespec *);
+#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
+static int	linux_utimensat_lts64_to_ts(struct l_timespec64 *,
+			struct timespec *);
+#endif
+static int	linux_common_utimensat(struct thread *, int,
+			const char *, struct timespec *, int);
 
 int
 linux_sysinfo(struct thread *td, struct linux_sysinfo_args *args)
@@ -780,89 +787,66 @@ linux_utimes(struct thread *td, struct linux_utimes_args *args)
 #endif
 
 static int
-linux_utimensat_nsec_valid(l_long nsec)
+linux_utimensat_lts_to_ts(struct l_timespec *l_times, struct timespec *times)
 {
 
-	if (nsec == LINUX_UTIME_OMIT || nsec == LINUX_UTIME_NOW)
-		return (0);
-	if (nsec >= 0 && nsec <= 999999999)
-		return (0);
-	return (1);
+	if (l_times->tv_nsec != LINUX_UTIME_OMIT &&
+	    l_times->tv_nsec != LINUX_UTIME_NOW &&
+	    (l_times->tv_nsec < 0 || l_times->tv_nsec > 999999999))
+		return (EINVAL);
+
+	times->tv_sec = l_times->tv_sec;
+	switch (l_times->tv_nsec)
+	{
+	case LINUX_UTIME_OMIT:
+		times->tv_nsec = UTIME_OMIT;
+		break;
+	case LINUX_UTIME_NOW:
+		times->tv_nsec = UTIME_NOW;
+		break;
+	default:
+		times->tv_nsec = l_times->tv_nsec;
+	}
+
+	return (0);
 }
 
-int
-linux_utimensat(struct thread *td, struct linux_utimensat_args *args)
+static int
+linux_common_utimensat(struct thread *td, int ldfd, const char *pathname,
+    struct timespec *timesp, int lflags)
 {
-	struct l_timespec l_times[2];
-	struct timespec times[2], *timesp = NULL;
 	char *path = NULL;
 	int error, dfd, flags = 0;
 
-	dfd = (args->dfd == LINUX_AT_FDCWD) ? AT_FDCWD : args->dfd;
+	dfd = (ldfd == LINUX_AT_FDCWD) ? AT_FDCWD : ldfd;
 
-	if (args->flags & ~(LINUX_AT_SYMLINK_NOFOLLOW | LINUX_AT_EMPTY_PATH))
+	if (lflags & ~(LINUX_AT_SYMLINK_NOFOLLOW | LINUX_AT_EMPTY_PATH))
 		return (EINVAL);
 
-	if (args->times != NULL) {
-		error = copyin(args->times, l_times, sizeof(l_times));
-		if (error != 0)
-			return (error);
-
-		if (linux_utimensat_nsec_valid(l_times[0].tv_nsec) != 0 ||
-		    linux_utimensat_nsec_valid(l_times[1].tv_nsec) != 0)
-			return (EINVAL);
-
-		times[0].tv_sec = l_times[0].tv_sec;
-		switch (l_times[0].tv_nsec)
-		{
-		case LINUX_UTIME_OMIT:
-			times[0].tv_nsec = UTIME_OMIT;
-			break;
-		case LINUX_UTIME_NOW:
-			times[0].tv_nsec = UTIME_NOW;
-			break;
-		default:
-			times[0].tv_nsec = l_times[0].tv_nsec;
-		}
-
-		times[1].tv_sec = l_times[1].tv_sec;
-		switch (l_times[1].tv_nsec)
-		{
-		case LINUX_UTIME_OMIT:
-			times[1].tv_nsec = UTIME_OMIT;
-			break;
-		case LINUX_UTIME_NOW:
-			times[1].tv_nsec = UTIME_NOW;
-			break;
-		default:
-			times[1].tv_nsec = l_times[1].tv_nsec;
-			break;
-		}
-		timesp = times;
-
+	if (timesp != NULL) {
 		/* This breaks POSIX, but is what the Linux kernel does
 		 * _on purpose_ (documented in the man page for utimensat(2)),
 		 * so we must follow that behaviour. */
-		if (times[0].tv_nsec == UTIME_OMIT &&
-		    times[1].tv_nsec == UTIME_OMIT)
+		if (timesp[0].tv_nsec == UTIME_OMIT &&
+		    timesp[1].tv_nsec == UTIME_OMIT)
 			return (0);
 	}
 
-	if (args->flags & LINUX_AT_SYMLINK_NOFOLLOW)
+	if (lflags & LINUX_AT_SYMLINK_NOFOLLOW)
 		flags |= AT_SYMLINK_NOFOLLOW;
-	if (args->flags & LINUX_AT_EMPTY_PATH)
+	if (lflags & LINUX_AT_EMPTY_PATH)
 		flags |= AT_EMPTY_PATH;
 
 	if (!LUSECONVPATH(td)) {
-		if (args->pathname != NULL) {
-			return (kern_utimensat(td, dfd, args->pathname,
+		if (pathname != NULL) {
+			return (kern_utimensat(td, dfd, pathname,
 			    UIO_USERSPACE, timesp, UIO_SYSSPACE, flags));
 		}
 	}
 
-	if (args->pathname != NULL)
-		LCONVPATHEXIST_AT(td, args->pathname, &path, dfd);
-	else if (args->flags != 0)
+	if (pathname != NULL)
+		LCONVPATHEXIST_AT(td, pathname, &path, dfd);
+	else if (lflags != 0)
 		return (EINVAL);
 
 	if (path == NULL)
@@ -876,6 +860,85 @@ linux_utimensat(struct thread *td, struct linux_utimensat_args *args)
 	return (error);
 }
 
+int
+linux_utimensat(struct thread *td, struct linux_utimensat_args *args)
+{
+	struct l_timespec l_times[2];
+	struct timespec times[2], *timesp;
+	int error;
+
+	if (args->times != NULL) {
+		error = copyin(args->times, l_times, sizeof(l_times));
+		if (error != 0)
+			return (error);
+
+		error = linux_utimensat_lts_to_ts(&l_times[0], &times[0]);
+		if (error != 0)
+			return (error);
+		error = linux_utimensat_lts_to_ts(&l_times[1], &times[1]);
+		if (error != 0)
+			return (error);
+		timesp = times;
+	} else
+		timesp = NULL;
+
+	return (linux_common_utimensat(td, args->dfd, args->pathname,
+	    timesp, args->flags));
+}
+
+#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
+static int
+linux_utimensat_lts64_to_ts(struct l_timespec64 *l_times, struct timespec *times)
+{
+
+	if (l_times->tv_nsec != LINUX_UTIME_OMIT &&
+	    l_times->tv_nsec != LINUX_UTIME_NOW &&
+	    (l_times->tv_nsec < 0 || l_times->tv_nsec > 999999999))
+		return (EINVAL);
+
+	times->tv_sec = l_times->tv_sec;
+	switch (l_times->tv_nsec)
+	{
+	case LINUX_UTIME_OMIT:
+		times->tv_nsec = UTIME_OMIT;
+		break;
+	case LINUX_UTIME_NOW:
+		times->tv_nsec = UTIME_NOW;
+		break;
+	default:
+		times->tv_nsec = l_times->tv_nsec;
+	}
+
+	return (0);
+}
+
+int
+linux_utimensat_time64(struct thread *td, struct linux_utimensat_time64_args *args)
+{
+	struct l_timespec64 l_times[2];
+	struct timespec times[2], *timesp;
+	int error;
+
+	if (args->times64 != NULL) {
+		error = copyin(args->times64, l_times, sizeof(l_times));
+		if (error != 0)
+			return (error);
+
+		error = linux_utimensat_lts64_to_ts(&l_times[0], &times[0]);
+		if (error != 0)
+			return (error);
+		error = linux_utimensat_lts64_to_ts(&l_times[1], &times[1]);
+		if (error != 0)
+			return (error);
+		timesp = times;
+	} else
+		timesp = NULL;
+
+	return (linux_common_utimensat(td, args->dfd, args->pathname,
+	    timesp, args->flags));
+}
+#endif /* __i386__ || (__amd64__ && COMPAT_LINUX32) */
+
 #ifdef LINUX_LEGACY_SYSCALLS
 int
 linux_futimesat(struct thread *td, struct linux_futimesat_args *args)
diff --git a/sys/i386/linux/linux_dummy_machdep.c b/sys/i386/linux/linux_dummy_machdep.c
index dfcc691a3582..d479ecde7a01 100644
--- a/sys/i386/linux/linux_dummy_machdep.c
+++ b/sys/i386/linux/linux_dummy_machdep.c
@@ -78,7 +78,6 @@ DUMMY(timer_gettime64);
 DUMMY(timer_settime64);
 DUMMY(timerfd_gettime64);
 DUMMY(timerfd_settime64);
-DUMMY(utimensat_time64);
 DUMMY(pselect6_time64);
 DUMMY(ppoll_time64);
 DUMMY(io_pgetevents_time64);
diff --git a/sys/i386/linux/syscalls.master b/sys/i386/linux/syscalls.master
index e2732d211235..5ba21877f42c 100644
--- a/sys/i386/linux/syscalls.master
+++ b/sys/i386/linux/syscalls.master
@@ -2386,7 +2386,12 @@
 		int linux_timerfd_settime64(void);
 	}
 412	AUE_NULL	STD {
-		int linux_utimensat_time64(void);
+		int linux_utimensat_time64(
+		    l_int dfd,
+		    const char *pathname,
+		    const struct l_timespec64 *times64,
+		    l_int flags
+		);
 	}
 413	AUE_NULL	STD {
 		int linux_pselect6_time64(void);