[PATCH 1/3] touch: implement nanosecond precision times

Xabier Oneca -- xOneca xoneca at gmail.com
Mon Apr 12 21:31:16 UTC 2021


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 at 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
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 0001-touch-implement-nanosecond-precision-times.patch
Type: application/x-patch
Size: 4845 bytes
Desc: not available
URL: <http://lists.busybox.net/pipermail/busybox/attachments/20210412/c2688907/attachment-0001.bin>


More information about the busybox mailing list