Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 31 Jan 2015 02:59:34 -0600
From:      "Andrew Wilcox" <AWilcox@Wilcox-Tech.com>
To:        "'Jilles Tjoelker'" <jilles@stack.nl>, <freebsd-arch@FreeBSD.org>
Subject:   RFC: Added utimensat(2) to Linuxulator (was RE: Current status of utimensat(2))
Message-ID:  <004f01d03d34$3caed970$b60c8c50$@Wilcox-Tech.com>

index | next in thread | raw e-mail

[-- Attachment #1 --]
Jilles Tjoelker sent 29 January 2015 17:06:
> I committed utimensat/futimens to head. I also made a start at Linuxulator
> utimensat, see attached patch. Please complete it and test it.
> 
> --
> Jilles Tjoelker


I have merged some of the things Jilles' patch does into the patch I was already working on.  I have tested a number of corner cases with it, and compared the results to a running Linux system.  I have also run software which all use utimensat(2) on Linux, such as recent tar(1), touch(1), and Python 3.3's shutil library, inside the Linuxulator with this patch applied.  I have found this patch's behaviour to be correct in these tests.  Please let me know of any comments or issues.

I will be additionally trying to merge this into dchagin's lemul branch, but that discussion will take place on Phabricator (and will involve testing on amd64-native Linuxulator, which I have not yet done). 

Regards,
Andrew

--
Andrew Wilcox, C/C++/Python developer, kernel hacker
Blog:   http://blog.foxkit.us/  WWW: http://foxkit.us/
GitHub: https://github.com/awilfox





[-- Attachment #2 --]
Index: sys/amd64/linux32/linux32_dummy.c
===================================================================
--- sys/amd64/linux32/linux32_dummy.c	(revision 277871)
+++ sys/amd64/linux32/linux32_dummy.c	(working copy)
@@ -111,7 +111,6 @@
 DUMMY(getcpu);
 DUMMY(epoll_pwait);
 /* linux 2.6.22: */
-DUMMY(utimensat);
 DUMMY(signalfd);
 DUMMY(timerfd_create);
 DUMMY(eventfd);
Index: sys/amd64/linux32/linux32_proto.h
===================================================================
--- sys/amd64/linux32/linux32_proto.h	(revision 277871)
+++ sys/amd64/linux32/linux32_proto.h	(working copy)
@@ -1017,7 +1017,10 @@
 	register_t dummy;
 };
 struct linux_utimensat_args {
-	register_t dummy;
+	char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)];
+	char pathname_l_[PADL_(const char *)]; const char * pathname; char pathname_r_[PADR_(const char *)];
+	char times_l_[PADL_(const struct l_timespec *)]; const struct l_timespec * times; char times_r_[PADR_(const struct l_timespec *)];
+	char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
 };
 struct linux_signalfd_args {
 	register_t dummy;
@@ -1652,7 +1655,7 @@
 #define	LINUX_SYS_AUE_linux_move_pages	AUE_NULL
 #define	LINUX_SYS_AUE_linux_getcpu	AUE_NULL
 #define	LINUX_SYS_AUE_linux_epoll_pwait	AUE_NULL
-#define	LINUX_SYS_AUE_linux_utimensat	AUE_NULL
+#define	LINUX_SYS_AUE_linux_utimensat	AUE_FUTIMESAT
 #define	LINUX_SYS_AUE_linux_signalfd	AUE_NULL
 #define	LINUX_SYS_AUE_linux_timerfd_create	AUE_NULL
 #define	LINUX_SYS_AUE_linux_eventfd	AUE_NULL
Index: sys/amd64/linux32/linux32_sysent.c
===================================================================
--- sys/amd64/linux32/linux32_sysent.c	(revision 277871)
+++ sys/amd64/linux32/linux32_sysent.c	(working copy)
@@ -339,7 +339,7 @@
 	{ 0, (sy_call_t *)linux_move_pages, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC },	/* 317 = linux_move_pages */
 	{ 0, (sy_call_t *)linux_getcpu, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC },	/* 318 = linux_getcpu */
 	{ 0, (sy_call_t *)linux_epoll_pwait, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC },	/* 319 = linux_epoll_pwait */
-	{ 0, (sy_call_t *)linux_utimensat, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC },	/* 320 = linux_utimensat */
+	{ AS(linux_utimensat_args), (sy_call_t *)linux_utimensat, AUE_FUTIMESAT, NULL, 0, 0, 0, SY_THR_STATIC },	/* 320 = linux_utimensat */
 	{ 0, (sy_call_t *)linux_signalfd, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC },	/* 321 = linux_signalfd */
 	{ 0, (sy_call_t *)linux_timerfd_create, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC },	/* 322 = linux_timerfd_create */
 	{ 0, (sy_call_t *)linux_eventfd, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC },	/* 323 = linux_eventfd */
Index: sys/amd64/linux32/linux32_systrace_args.c
===================================================================
--- sys/amd64/linux32/linux32_systrace_args.c	(revision 277871)
+++ sys/amd64/linux32/linux32_systrace_args.c	(working copy)
@@ -2130,7 +2130,12 @@
 	}
 	/* linux_utimensat */
 	case 320: {
-		*n_args = 0;
+		struct linux_utimensat_args *p = params;
+		iarg[0] = p->dfd; /* l_int */
+		uarg[1] = (intptr_t) p->pathname; /* const char * */
+		uarg[2] = (intptr_t) p->times; /* const struct l_timespec * */
+		iarg[3] = p->flags; /* l_int */
+		*n_args = 4;
 		break;
 	}
 	/* linux_signalfd */
@@ -5395,6 +5400,22 @@
 		break;
 	/* linux_utimensat */
 	case 320:
+		switch(ndx) {
+		case 0:
+			p = "l_int";
+			break;
+		case 1:
+			p = "const char *";
+			break;
+		case 2:
+			p = "const struct l_timespec *";
+			break;
+		case 3:
+			p = "l_int";
+			break;
+		default:
+			break;
+		};
 		break;
 	/* linux_signalfd */
 	case 321:
@@ -6684,6 +6705,9 @@
 	case 319:
 	/* linux_utimensat */
 	case 320:
+		if (ndx == 0 || ndx == 1)
+			p = "int";
+		break;
 	/* linux_signalfd */
 	case 321:
 	/* linux_timerfd_create */
Index: sys/amd64/linux32/syscalls.master
===================================================================
--- sys/amd64/linux32/syscalls.master	(revision 277871)
+++ sys/amd64/linux32/syscalls.master	(working copy)
@@ -524,7 +524,8 @@
 318	AUE_NULL	STD	{ int linux_getcpu(void); }
 319	AUE_NULL	STD	{ int linux_epoll_pwait(void); }
 ; linux 2.6.22:
-320	AUE_NULL	STD	{ int linux_utimensat(void); }
+320	AUE_FUTIMESAT	STD	{ int linux_utimensat(l_int dfd, const char *pathname, \
+					const struct l_timespec *times, l_int flags); }
 321	AUE_NULL	STD	{ int linux_signalfd(void); }
 322	AUE_NULL	STD	{ int linux_timerfd_create(void); }
 323	AUE_NULL	STD	{ int linux_eventfd(void); }
Index: sys/compat/linux/linux_misc.c
===================================================================
--- sys/compat/linux/linux_misc.c	(revision 277871)
+++ sys/compat/linux/linux_misc.c	(working copy)
@@ -816,6 +816,89 @@
 	return (error);
 }
 
+int 
+linux_utimensat(struct thread *td, struct linux_utimensat_args *args)
+{
+	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;
+
+#ifdef DEBUG
+	if (ldebug(utimensat))
+		printf(ARGS(utimensat, "%d, *"), dfd);
+#endif
+
+	if (args->flags & ~LINUX_AT_SYMLINK_NOFOLLOW)
+		return (EINVAL);
+
+	if (args->times != NULL) {
+		if ((error = copyin(args->times, l_times, sizeof l_times)))
+			return (error);
+
+		if (l_times[0].tv_nsec > 999999999 ||
+			l_times[1].tv_nsec > 999999999)
+			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;
+		}
+		timesp = times;
+	}
+
+	if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT) {
+		/* 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. */
+		return (0);
+	}
+
+	if (args->pathname != NULL)
+		LCONVPATHEXIST_AT(td, args->pathname, &path, dfd);
+	else {
+		if (args->flags != 0)
+			return (EINVAL);
+	}
+
+	if (args->flags & LINUX_AT_SYMLINK_NOFOLLOW)
+		flags |= AT_SYMLINK_NOFOLLOW;
+
+	if (path == NULL)
+		error = kern_futimens(td, dfd, timesp, UIO_SYSSPACE);
+	else
+	{
+		error = kern_utimensat(td, dfd, path, UIO_SYSSPACE, timesp,
+	    		UIO_SYSSPACE, flags);
+		LFREEPATH(path);
+	}
+
+	return (error);
+}
+
 int
 linux_futimesat(struct thread *td, struct linux_futimesat_args *args)
 {
Index: sys/compat/linux/linux_misc.h
===================================================================
--- sys/compat/linux/linux_misc.h	(revision 277871)
+++ sys/compat/linux/linux_misc.h	(working copy)
@@ -113,6 +113,9 @@
 #define	LINUX_CLOCK_REALTIME_HR		4
 #define	LINUX_CLOCK_MONOTONIC_HR	5
 
+#define LINUX_UTIME_NOW			0x3FFFFFFF
+#define LINUX_UTIME_OMIT		0x3FFFFFFE
+
 extern int stclohz;
 
 #define __WCLONE 0x80000000
Index: sys/i386/linux/linux_dummy.c
===================================================================
--- sys/i386/linux/linux_dummy.c	(revision 277871)
+++ sys/i386/linux/linux_dummy.c	(working copy)
@@ -107,7 +107,6 @@
 DUMMY(getcpu);
 DUMMY(epoll_pwait);
 /* linux 2.6.22: */
-DUMMY(utimensat);
 DUMMY(signalfd);
 DUMMY(timerfd_create);
 DUMMY(eventfd);
Index: sys/i386/linux/linux_proto.h
===================================================================
--- sys/i386/linux/linux_proto.h	(revision 277871)
+++ sys/i386/linux/linux_proto.h	(working copy)
@@ -1031,7 +1031,10 @@
 	register_t dummy;
 };
 struct linux_utimensat_args {
-	register_t dummy;
+	char dfd_l_[PADL_(l_int)]; l_int dfd; char dfd_r_[PADR_(l_int)];
+	char pathname_l_[PADL_(const char *)]; const char * pathname; char pathname_r_[PADR_(const char *)];
+	char times_l_[PADL_(const struct l_timespec *)]; const struct l_timespec * times; char times_r_[PADR_(const struct l_timespec *)];
+	char flags_l_[PADL_(l_int)]; l_int flags; char flags_r_[PADR_(l_int)];
 };
 struct linux_signalfd_args {
 	register_t dummy;
@@ -1668,7 +1671,7 @@
 #define	LINUX_SYS_AUE_linux_move_pages	AUE_NULL
 #define	LINUX_SYS_AUE_linux_getcpu	AUE_NULL
 #define	LINUX_SYS_AUE_linux_epoll_pwait	AUE_NULL
-#define	LINUX_SYS_AUE_linux_utimensat	AUE_NULL
+#define	LINUX_SYS_AUE_linux_utimensat	AUE_FUTIMESAT
 #define	LINUX_SYS_AUE_linux_signalfd	AUE_NULL
 #define	LINUX_SYS_AUE_linux_timerfd_create	AUE_NULL
 #define	LINUX_SYS_AUE_linux_eventfd	AUE_NULL
Index: sys/i386/linux/linux_sysent.c
===================================================================
--- sys/i386/linux/linux_sysent.c	(revision 277871)
+++ sys/i386/linux/linux_sysent.c	(working copy)
@@ -338,7 +338,7 @@
 	{ 0, (sy_call_t *)linux_move_pages, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC },	/* 317 = linux_move_pages */
 	{ 0, (sy_call_t *)linux_getcpu, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC },	/* 318 = linux_getcpu */
 	{ 0, (sy_call_t *)linux_epoll_pwait, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC },	/* 319 = linux_epoll_pwait */
-	{ 0, (sy_call_t *)linux_utimensat, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC },	/* 320 = linux_utimensat */
+	{ AS(linux_utimensat_args), (sy_call_t *)linux_utimensat, AUE_FUTIMESAT, NULL, 0, 0, 0, SY_THR_STATIC },	/* 320 = linux_utimensat */
 	{ 0, (sy_call_t *)linux_signalfd, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC },	/* 321 = linux_signalfd */
 	{ 0, (sy_call_t *)linux_timerfd_create, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC },	/* 322 = linux_timerfd_create */
 	{ 0, (sy_call_t *)linux_eventfd, AUE_NULL, NULL, 0, 0, 0, SY_THR_STATIC },	/* 323 = linux_eventfd */
Index: sys/i386/linux/linux_systrace_args.c
===================================================================
--- sys/i386/linux/linux_systrace_args.c	(revision 277871)
+++ sys/i386/linux/linux_systrace_args.c	(working copy)
@@ -2206,7 +2206,12 @@
 	}
 	/* linux_utimensat */
 	case 320: {
-		*n_args = 0;
+		struct linux_utimensat_args *p = params;
+		iarg[0] = p->dfd; /* l_int */
+		uarg[1] = (intptr_t) p->pathname; /* const char * */
+		uarg[2] = (intptr_t) p->times; /* const struct l_timespec * */
+		iarg[3] = p->flags; /* l_int */
+		*n_args = 4;
 		break;
 	}
 	/* linux_signalfd */
@@ -5626,6 +5631,22 @@
 		break;
 	/* linux_utimensat */
 	case 320:
+		switch(ndx) {
+		case 0:
+			p = "l_int";
+			break;
+		case 1:
+			p = "const char *";
+			break;
+		case 2:
+			p = "const struct l_timespec *";
+			break;
+		case 3:
+			p = "l_int";
+			break;
+		default:
+			break;
+		};
 		break;
 	/* linux_signalfd */
 	case 321:
@@ -6962,6 +6983,9 @@
 	case 319:
 	/* linux_utimensat */
 	case 320:
+		if (ndx == 0 || ndx == 1)
+			p = "int";
+		break;
 	/* linux_signalfd */
 	case 321:
 	/* linux_timerfd_create */
Index: sys/i386/linux/syscalls.master
===================================================================
--- sys/i386/linux/syscalls.master	(revision 277871)
+++ sys/i386/linux/syscalls.master	(working copy)
@@ -532,7 +532,8 @@
 318	AUE_NULL	STD	{ int linux_getcpu(void); }
 319	AUE_NULL	STD	{ int linux_epoll_pwait(void); }
 ; linux 2.6.22:
-320	AUE_NULL	STD	{ int linux_utimensat(void); }
+320	AUE_FUTIMESAT	STD	{ int linux_utimensat(l_int dfd, const char *pathname, \
+					const struct l_timespec *times, l_int flags); }
 321	AUE_NULL	STD	{ int linux_signalfd(void); }
 322	AUE_NULL	STD	{ int linux_timerfd_create(void); }
 323	AUE_NULL	STD	{ int linux_eventfd(void); }
home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?004f01d03d34$3caed970$b60c8c50$>