Using utimensat(2) allows us to set times in nanosecond precision. Also logic is
simplified and code shrinked.

function                                             old     new   delta
touch_main                                           513     479     -34
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-34)             Total: -34 bytes

Signed-off-by: Xabier Oneca <xon...@gmail.com>
---
 coreutils/touch.c | 65 +++++++++++++++++++++--------------------------
 1 file changed, 29 insertions(+), 36 deletions(-)

diff --git a/coreutils/touch.c b/coreutils/touch.c
index 690517e66..a00ee0ba4 100644
--- a/coreutils/touch.c
+++ b/coreutils/touch.c
@@ -25,7 +25,6 @@
 //config:    depends on TOUCH
 //config:    help
 //config:    Enable touch to have the -h option.
-//config:    This requires libc support for lutimes() function.
 //config:
 //config:config FEATURE_TOUCH_SUSV3
 //config:    bool "Add support for SUSV3 features (-a -d -m -t -r)"
@@ -98,7 +97,7 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
         OPT_m = (1 << (5+ENABLE_FEATURE_TOUCH_NODEREF)) *
ENABLE_FEATURE_TOUCH_SUSV3,
     };
     /* NULL = use current time */
-    const struct timeval *newtime = NULL;
+    const struct timespec *newtime = NULL;
 #if ENABLE_LONG_OPTS
     static const char touch_longopts[] ALIGN1 =
         /* name, has_arg, val */
@@ -112,12 +111,13 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
     char *reference_file = NULL;
     char *date_str = NULL;
     /* timebuf[0] is atime, timebuf[1] is mtime */
-    struct timeval timebuf[2];
-    timebuf[1].tv_usec = timebuf[0].tv_usec = 0;
+    struct timespec timebuf[2];
+    //timebuf[0].tv_sec  = timebuf[1].tv_sec = 0; /* -- needed on
Linux <=v2.6.26? */
+    timebuf[0].tv_nsec = timebuf[1].tv_nsec = UTIME_NOW;
 #else
 # define reference_file NULL
 # define date_str       NULL
-# define timebuf        ((struct timeval*)NULL)
+# define timebuf        NULL
 #endif

     /* -d and -t both set time. In coreutils,
@@ -143,12 +143,11 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
     if (reference_file) {
         struct stat stbuf;
         xstat(reference_file, &stbuf);
-        timebuf[0].tv_sec = stbuf.st_atime;
-        timebuf[1].tv_sec = stbuf.st_mtime;
-        /* Can use .st_mtim.tv_nsec
-         * (or is it .st_mtimensec?? see date.c)
-         * to set microseconds too.
+        /* NOTE: Some toolchains use .st_atime and .st_atimensec instead of
+         * .st_atim.
          */
+        memcpy(&timebuf[0], &stbuf.st_atim, sizeof(timebuf[0]));
+        memcpy(&timebuf[1], &stbuf.st_mtim, sizeof(timebuf[1]));
         newtime = timebuf;
     }

@@ -166,39 +165,33 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
         tm_time.tm_isdst = -1;  /* Be sure to recheck dst */
         t = validate_tm_time(date_str, &tm_time);

-        timebuf[1].tv_sec = timebuf[0].tv_sec = t;
+        /* atime */
+        timebuf[0].tv_sec = t;
+        timebuf[0].tv_nsec = 0;
+
+        /* mtime */
+        timebuf[1].tv_sec = t;
+        timebuf[1].tv_nsec = 0;
+
         newtime = timebuf;
     }

-    if ((opts & (OPT_a | OPT_m)) && !newtime) {
-        time(&timebuf[0].tv_sec);
-        timebuf[1].tv_sec = timebuf[0].tv_sec;
+    if ((opts & OPT_a) && !(opts & OPT_m)) {
+        /* only -a: do not update mtime */
+        //timebuf[1].tv_sec = 0;
+        timebuf[1].tv_nsec = UTIME_OMIT;
+        newtime = timebuf;
+    } else if ((opts & OPT_m) && !(opts & OPT_a)) {
+        /* only -m: do not update atime */
+        //timebuf[0].tv_sec = 0;
+        timebuf[0].tv_nsec = UTIME_OMIT;
         newtime = timebuf;
     }

     do {
         int result;

-        if (opts & (OPT_a | OPT_m)) {
-            /* Save original times */
-            struct stat stbuf;
-            if (stat(*argv, &stbuf) == 0) {
-                /* As we must set both times, we lose original
-                 * file time microseconds.
-                 * Can use .st_mtim.tv_nsec
-                 * (or is it .st_mtimensec?? see date.c)
-                 * to set microseconds too.
-                 * Also, utimensat(2) allows to omit one of the
-                 * times to be set. But it is SUSv4.
-                 */
-                if (!(opts & OPT_a))
-                    timebuf[0].tv_sec = stbuf.st_atime;
-                if (!(opts & OPT_m))
-                    timebuf[1].tv_sec = stbuf.st_mtime;
-            }
-        }
-
-        result = (ENABLE_FEATURE_TOUCH_NODEREF && (opts & OPT_h) ?
lutimes : utimes)(*argv, newtime);
+        result = utimensat(AT_FDCWD, *argv, newtime, (opts & OPT_h) ?
AT_SYMLINK_NOFOLLOW : 0);

         if (result != 0) {
             if (errno == ENOENT) { /* no such file? */
@@ -209,9 +202,9 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
                 /* Try to create the file */
                 fd = open(*argv, O_RDWR | O_CREAT, 0666);
                 if (fd >= 0) {
-                    xclose(fd);
                     if (reference_file || date_str)
-                        utimes(*argv, newtime);
+                        futimens(fd, newtime);
+                    xclose(fd);
                     continue;
                 }
             }
--
2.30.2
From 4b0e69ac83e9a0ee5d845f50f22dcc73ef1a8362 Mon Sep 17 00:00:00 2001
From: Xabier Oneca <xoneca@gmail.com>
Date: Mon, 12 Apr 2021 21:30:31 +0200
Subject: [PATCH 1/3] touch: implement nanosecond precision times

Using utimensat(2) allows us to set times in nanosecond precision. Also logic is
simplified and code shrinked.

function                                             old     new   delta
touch_main                                           513     479     -34
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-34)             Total: -34 bytes

Signed-off-by: Xabier Oneca <xoneca@gmail.com>
---
 coreutils/touch.c | 65 +++++++++++++++++++++--------------------------
 1 file changed, 29 insertions(+), 36 deletions(-)

diff --git a/coreutils/touch.c b/coreutils/touch.c
index 690517e66..a00ee0ba4 100644
--- a/coreutils/touch.c
+++ b/coreutils/touch.c
@@ -25,7 +25,6 @@
 //config:	depends on TOUCH
 //config:	help
 //config:	Enable touch to have the -h option.
-//config:	This requires libc support for lutimes() function.
 //config:
 //config:config FEATURE_TOUCH_SUSV3
 //config:	bool "Add support for SUSV3 features (-a -d -m -t -r)"
@@ -98,7 +97,7 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
 		OPT_m = (1 << (5+ENABLE_FEATURE_TOUCH_NODEREF)) * ENABLE_FEATURE_TOUCH_SUSV3,
 	};
 	/* NULL = use current time */
-	const struct timeval *newtime = NULL;
+	const struct timespec *newtime = NULL;
 #if ENABLE_LONG_OPTS
 	static const char touch_longopts[] ALIGN1 =
 		/* name, has_arg, val */
@@ -112,12 +111,13 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
 	char *reference_file = NULL;
 	char *date_str = NULL;
 	/* timebuf[0] is atime, timebuf[1] is mtime */
-	struct timeval timebuf[2];
-	timebuf[1].tv_usec = timebuf[0].tv_usec = 0;
+	struct timespec timebuf[2];
+	//timebuf[0].tv_sec  = timebuf[1].tv_sec = 0; /* -- needed on Linux <=v2.6.26? */
+	timebuf[0].tv_nsec = timebuf[1].tv_nsec = UTIME_NOW;
 #else
 # define reference_file NULL
 # define date_str       NULL
-# define timebuf        ((struct timeval*)NULL)
+# define timebuf        NULL
 #endif
 
 	/* -d and -t both set time. In coreutils,
@@ -143,12 +143,11 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
 	if (reference_file) {
 		struct stat stbuf;
 		xstat(reference_file, &stbuf);
-		timebuf[0].tv_sec = stbuf.st_atime;
-		timebuf[1].tv_sec = stbuf.st_mtime;
-		/* Can use .st_mtim.tv_nsec
-		 * (or is it .st_mtimensec?? see date.c)
-		 * to set microseconds too.
+		/* NOTE: Some toolchains use .st_atime and .st_atimensec instead of
+		 * .st_atim.
 		 */
+		memcpy(&timebuf[0], &stbuf.st_atim, sizeof(timebuf[0]));
+		memcpy(&timebuf[1], &stbuf.st_mtim, sizeof(timebuf[1]));
 		newtime = timebuf;
 	}
 
@@ -166,39 +165,33 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
 		tm_time.tm_isdst = -1;  /* Be sure to recheck dst */
 		t = validate_tm_time(date_str, &tm_time);
 
-		timebuf[1].tv_sec = timebuf[0].tv_sec = t;
+		/* atime */
+		timebuf[0].tv_sec = t;
+		timebuf[0].tv_nsec = 0;
+
+		/* mtime */
+		timebuf[1].tv_sec = t;
+		timebuf[1].tv_nsec = 0;
+
 		newtime = timebuf;
 	}
 
-	if ((opts & (OPT_a | OPT_m)) && !newtime) {
-		time(&timebuf[0].tv_sec);
-		timebuf[1].tv_sec = timebuf[0].tv_sec;
+	if ((opts & OPT_a) && !(opts & OPT_m)) {
+		/* only -a: do not update mtime */
+		//timebuf[1].tv_sec = 0;
+		timebuf[1].tv_nsec = UTIME_OMIT;
+		newtime = timebuf;
+	} else if ((opts & OPT_m) && !(opts & OPT_a)) {
+		/* only -m: do not update atime */
+		//timebuf[0].tv_sec = 0;
+		timebuf[0].tv_nsec = UTIME_OMIT;
 		newtime = timebuf;
 	}
 
 	do {
 		int result;
 
-		if (opts & (OPT_a | OPT_m)) {
-			/* Save original times */
-			struct stat stbuf;
-			if (stat(*argv, &stbuf) == 0) {
-				/* As we must set both times, we lose original
-				 * file time microseconds.
-				 * Can use .st_mtim.tv_nsec
-				 * (or is it .st_mtimensec?? see date.c)
-				 * to set microseconds too.
-				 * Also, utimensat(2) allows to omit one of the
-				 * times to be set. But it is SUSv4.
-				 */
-				if (!(opts & OPT_a))
-					timebuf[0].tv_sec = stbuf.st_atime;
-				if (!(opts & OPT_m))
-					timebuf[1].tv_sec = stbuf.st_mtime;
-			}
-		}
-
-		result = (ENABLE_FEATURE_TOUCH_NODEREF && (opts & OPT_h) ? lutimes : utimes)(*argv, newtime);
+		result = utimensat(AT_FDCWD, *argv, newtime, (opts & OPT_h) ? AT_SYMLINK_NOFOLLOW : 0);
 
 		if (result != 0) {
 			if (errno == ENOENT) { /* no such file? */
@@ -209,9 +202,9 @@ int touch_main(int argc UNUSED_PARAM, char **argv)
 				/* Try to create the file */
 				fd = open(*argv, O_RDWR | O_CREAT, 0666);
 				if (fd >= 0) {
-					xclose(fd);
 					if (reference_file || date_str)
-						utimes(*argv, newtime);
+						futimens(fd, newtime);
+					xclose(fd);
 					continue;
 				}
 			}
-- 
2.30.2

_______________________________________________
busybox mailing list
busybox@busybox.net
http://lists.busybox.net/mailman/listinfo/busybox

Reply via email to