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