diff options
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/futimens.c | 37 | ||||
| -rw-r--r-- | lib/gnulib.mk.in | 28 | ||||
| -rw-r--r-- | lib/utimensat.c | 160 |
3 files changed, 224 insertions, 1 deletions
diff --git a/lib/futimens.c b/lib/futimens.c new file mode 100644 index 00000000000..83fb27cb6aa --- /dev/null +++ b/lib/futimens.c | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | /* Set the access and modification time of an open fd. | ||
| 2 | Copyright (C) 2009-2020 Free Software Foundation, Inc. | ||
| 3 | |||
| 4 | This program is free software: you can redistribute it and/or modify | ||
| 5 | it under the terms of the GNU General Public License as published by | ||
| 6 | the Free Software Foundation; either version 3 of the License, or | ||
| 7 | (at your option) any later version. | ||
| 8 | |||
| 9 | This program is distributed in the hope that it will be useful, | ||
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | GNU General Public License for more details. | ||
| 13 | |||
| 14 | You should have received a copy of the GNU General Public License | ||
| 15 | along with this program. If not, see <https://www.gnu.org/licenses/>. */ | ||
| 16 | |||
| 17 | /* written by Eric Blake */ | ||
| 18 | |||
| 19 | #include <config.h> | ||
| 20 | |||
| 21 | #include <sys/stat.h> | ||
| 22 | |||
| 23 | #include "utimens.h" | ||
| 24 | |||
| 25 | /* Set the access and modification timestamps of FD to be | ||
| 26 | TIMESPEC[0] and TIMESPEC[1], respectively. | ||
| 27 | Fail with ENOSYS on systems without futimes (or equivalent). | ||
| 28 | If TIMESPEC is null, set the timestamps to the current time. | ||
| 29 | Return 0 on success, -1 (setting errno) on failure. */ | ||
| 30 | int | ||
| 31 | futimens (int fd, struct timespec const times[2]) | ||
| 32 | { | ||
| 33 | /* fdutimens also works around bugs in native futimens, when running | ||
| 34 | with glibc compiled against newer headers but on a Linux kernel | ||
| 35 | older than 2.6.32. */ | ||
| 36 | return fdutimens (fd, NULL, times); | ||
| 37 | } | ||
diff --git a/lib/gnulib.mk.in b/lib/gnulib.mk.in index d4dc6a3df33..e90d2e39049 100644 --- a/lib/gnulib.mk.in +++ b/lib/gnulib.mk.in | |||
| @@ -106,6 +106,7 @@ | |||
| 106 | # fstatat \ | 106 | # fstatat \ |
| 107 | # fsusage \ | 107 | # fsusage \ |
| 108 | # fsync \ | 108 | # fsync \ |
| 109 | # futimens \ | ||
| 109 | # getloadavg \ | 110 | # getloadavg \ |
| 110 | # getopt-gnu \ | 111 | # getopt-gnu \ |
| 111 | # gettime \ | 112 | # gettime \ |
| @@ -155,7 +156,7 @@ | |||
| 155 | # timespec-sub \ | 156 | # timespec-sub \ |
| 156 | # unlocked-io \ | 157 | # unlocked-io \ |
| 157 | # update-copyright \ | 158 | # update-copyright \ |
| 158 | # utimens \ | 159 | # utimensat \ |
| 159 | # vla \ | 160 | # vla \ |
| 160 | # warnings | 161 | # warnings |
| 161 | 162 | ||
| @@ -1087,6 +1088,7 @@ gl_GNULIB_ENABLED_lchmod = @gl_GNULIB_ENABLED_lchmod@ | |||
| 1087 | gl_GNULIB_ENABLED_malloca = @gl_GNULIB_ENABLED_malloca@ | 1088 | gl_GNULIB_ENABLED_malloca = @gl_GNULIB_ENABLED_malloca@ |
| 1088 | gl_GNULIB_ENABLED_open = @gl_GNULIB_ENABLED_open@ | 1089 | gl_GNULIB_ENABLED_open = @gl_GNULIB_ENABLED_open@ |
| 1089 | gl_GNULIB_ENABLED_strtoll = @gl_GNULIB_ENABLED_strtoll@ | 1090 | gl_GNULIB_ENABLED_strtoll = @gl_GNULIB_ENABLED_strtoll@ |
| 1091 | gl_GNULIB_ENABLED_utimens = @gl_GNULIB_ENABLED_utimens@ | ||
| 1090 | gl_LIBOBJS = @gl_LIBOBJS@ | 1092 | gl_LIBOBJS = @gl_LIBOBJS@ |
| 1091 | gl_LTLIBOBJS = @gl_LTLIBOBJS@ | 1093 | gl_LTLIBOBJS = @gl_LTLIBOBJS@ |
| 1092 | gltests_LIBOBJS = @gltests_LIBOBJS@ | 1094 | gltests_LIBOBJS = @gltests_LIBOBJS@ |
| @@ -1733,6 +1735,17 @@ EXTRA_libgnu_a_SOURCES += fsync.c | |||
| 1733 | endif | 1735 | endif |
| 1734 | ## end gnulib module fsync | 1736 | ## end gnulib module fsync |
| 1735 | 1737 | ||
| 1738 | ## begin gnulib module futimens | ||
| 1739 | ifeq (,$(OMIT_GNULIB_MODULE_futimens)) | ||
| 1740 | |||
| 1741 | |||
| 1742 | EXTRA_DIST += futimens.c | ||
| 1743 | |||
| 1744 | EXTRA_libgnu_a_SOURCES += futimens.c | ||
| 1745 | |||
| 1746 | endif | ||
| 1747 | ## end gnulib module futimens | ||
| 1748 | |||
| 1736 | ## begin gnulib module getdtablesize | 1749 | ## begin gnulib module getdtablesize |
| 1737 | ifeq (,$(OMIT_GNULIB_MODULE_getdtablesize)) | 1750 | ifeq (,$(OMIT_GNULIB_MODULE_getdtablesize)) |
| 1738 | 1751 | ||
| @@ -3375,13 +3388,26 @@ endif | |||
| 3375 | ## begin gnulib module utimens | 3388 | ## begin gnulib module utimens |
| 3376 | ifeq (,$(OMIT_GNULIB_MODULE_utimens)) | 3389 | ifeq (,$(OMIT_GNULIB_MODULE_utimens)) |
| 3377 | 3390 | ||
| 3391 | ifneq (,$(gl_GNULIB_ENABLED_utimens)) | ||
| 3378 | libgnu_a_SOURCES += utimens.c | 3392 | libgnu_a_SOURCES += utimens.c |
| 3379 | 3393 | ||
| 3394 | endif | ||
| 3380 | EXTRA_DIST += utimens.h | 3395 | EXTRA_DIST += utimens.h |
| 3381 | 3396 | ||
| 3382 | endif | 3397 | endif |
| 3383 | ## end gnulib module utimens | 3398 | ## end gnulib module utimens |
| 3384 | 3399 | ||
| 3400 | ## begin gnulib module utimensat | ||
| 3401 | ifeq (,$(OMIT_GNULIB_MODULE_utimensat)) | ||
| 3402 | |||
| 3403 | |||
| 3404 | EXTRA_DIST += at-func.c utimensat.c | ||
| 3405 | |||
| 3406 | EXTRA_libgnu_a_SOURCES += at-func.c utimensat.c | ||
| 3407 | |||
| 3408 | endif | ||
| 3409 | ## end gnulib module utimensat | ||
| 3410 | |||
| 3385 | ## begin gnulib module verify | 3411 | ## begin gnulib module verify |
| 3386 | ifeq (,$(OMIT_GNULIB_MODULE_verify)) | 3412 | ifeq (,$(OMIT_GNULIB_MODULE_verify)) |
| 3387 | 3413 | ||
diff --git a/lib/utimensat.c b/lib/utimensat.c new file mode 100644 index 00000000000..63788d56480 --- /dev/null +++ b/lib/utimensat.c | |||
| @@ -0,0 +1,160 @@ | |||
| 1 | /* Set the access and modification time of a file relative to directory fd. | ||
| 2 | Copyright (C) 2009-2020 Free Software Foundation, Inc. | ||
| 3 | |||
| 4 | This program is free software: you can redistribute it and/or modify | ||
| 5 | it under the terms of the GNU General Public License as published by | ||
| 6 | the Free Software Foundation; either version 3 of the License, or | ||
| 7 | (at your option) any later version. | ||
| 8 | |||
| 9 | This program is distributed in the hope that it will be useful, | ||
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 12 | GNU General Public License for more details. | ||
| 13 | |||
| 14 | You should have received a copy of the GNU General Public License | ||
| 15 | along with this program. If not, see <https://www.gnu.org/licenses/>. */ | ||
| 16 | |||
| 17 | /* written by Eric Blake */ | ||
| 18 | |||
| 19 | #include <config.h> | ||
| 20 | |||
| 21 | /* Specification. */ | ||
| 22 | #include <sys/stat.h> | ||
| 23 | |||
| 24 | #include <errno.h> | ||
| 25 | #include <fcntl.h> | ||
| 26 | #include <stdlib.h> | ||
| 27 | |||
| 28 | #include "stat-time.h" | ||
| 29 | #include "timespec.h" | ||
| 30 | #include "utimens.h" | ||
| 31 | |||
| 32 | #if HAVE_UTIMENSAT | ||
| 33 | |||
| 34 | # undef utimensat | ||
| 35 | |||
| 36 | /* If we have a native utimensat, but are compiling this file, then | ||
| 37 | utimensat was defined to rpl_utimensat by our replacement | ||
| 38 | sys/stat.h. We assume the native version might fail with ENOSYS, | ||
| 39 | or succeed without properly affecting ctime (as is the case when | ||
| 40 | using newer glibc but older Linux kernel). In this scenario, | ||
| 41 | rpl_utimensat checks whether the native version is usable, and | ||
| 42 | local_utimensat provides the fallback manipulation. */ | ||
| 43 | |||
| 44 | static int local_utimensat (int, char const *, struct timespec const[2], int); | ||
| 45 | # define AT_FUNC_NAME local_utimensat | ||
| 46 | |||
| 47 | /* Like utimensat, but work around native bugs. */ | ||
| 48 | |||
| 49 | int | ||
| 50 | rpl_utimensat (int fd, char const *file, struct timespec const times[2], | ||
| 51 | int flag) | ||
| 52 | { | ||
| 53 | # if defined __linux__ || defined __sun | ||
| 54 | struct timespec ts[2]; | ||
| 55 | # endif | ||
| 56 | |||
| 57 | /* See comments in utimens.c for details. */ | ||
| 58 | static int utimensat_works_really; /* 0 = unknown, 1 = yes, -1 = no. */ | ||
| 59 | if (0 <= utimensat_works_really) | ||
| 60 | { | ||
| 61 | int result; | ||
| 62 | # if defined __linux__ || defined __sun | ||
| 63 | struct stat st; | ||
| 64 | /* As recently as Linux kernel 2.6.32 (Dec 2009), several file | ||
| 65 | systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT, | ||
| 66 | but work if both times are either explicitly specified or | ||
| 67 | UTIME_NOW. Work around it with a preparatory [l]stat prior | ||
| 68 | to calling utimensat; fortunately, there is not much timing | ||
| 69 | impact due to the extra syscall even on file systems where | ||
| 70 | UTIME_OMIT would have worked. | ||
| 71 | |||
| 72 | The same bug occurs in Solaris 11.1 (Apr 2013). | ||
| 73 | |||
| 74 | FIXME: Simplify this in 2024, when these file system bugs are | ||
| 75 | no longer common on Gnulib target platforms. */ | ||
| 76 | if (times && (times[0].tv_nsec == UTIME_OMIT | ||
| 77 | || times[1].tv_nsec == UTIME_OMIT)) | ||
| 78 | { | ||
| 79 | if (fstatat (fd, file, &st, flag)) | ||
| 80 | return -1; | ||
| 81 | if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT) | ||
| 82 | return 0; | ||
| 83 | if (times[0].tv_nsec == UTIME_OMIT) | ||
| 84 | ts[0] = get_stat_atime (&st); | ||
| 85 | else | ||
| 86 | ts[0] = times[0]; | ||
| 87 | if (times[1].tv_nsec == UTIME_OMIT) | ||
| 88 | ts[1] = get_stat_mtime (&st); | ||
| 89 | else | ||
| 90 | ts[1] = times[1]; | ||
| 91 | times = ts; | ||
| 92 | } | ||
| 93 | # ifdef __hppa__ | ||
| 94 | /* Linux kernel 2.6.22.19 on hppa does not reject invalid tv_nsec | ||
| 95 | values. */ | ||
| 96 | else if (times | ||
| 97 | && ((times[0].tv_nsec != UTIME_NOW | ||
| 98 | && ! (0 <= times[0].tv_nsec | ||
| 99 | && times[0].tv_nsec < TIMESPEC_HZ)) | ||
| 100 | || (times[1].tv_nsec != UTIME_NOW | ||
| 101 | && ! (0 <= times[1].tv_nsec | ||
| 102 | && times[1].tv_nsec < TIMESPEC_HZ)))) | ||
| 103 | { | ||
| 104 | errno = EINVAL; | ||
| 105 | return -1; | ||
| 106 | } | ||
| 107 | # endif | ||
| 108 | # endif | ||
| 109 | result = utimensat (fd, file, times, flag); | ||
| 110 | /* Linux kernel 2.6.25 has a bug where it returns EINVAL for | ||
| 111 | UTIME_NOW or UTIME_OMIT with non-zero tv_sec, which | ||
| 112 | local_utimensat works around. Meanwhile, EINVAL for a bad | ||
| 113 | flag is indeterminate whether the native utimensat works, but | ||
| 114 | local_utimensat will also reject it. */ | ||
| 115 | if (result == -1 && errno == EINVAL && (flag & ~AT_SYMLINK_NOFOLLOW)) | ||
| 116 | return result; | ||
| 117 | if (result == 0 || (errno != ENOSYS && errno != EINVAL)) | ||
| 118 | { | ||
| 119 | utimensat_works_really = 1; | ||
| 120 | return result; | ||
| 121 | } | ||
| 122 | } | ||
| 123 | /* No point in trying openat/futimens, since on Linux, futimens is | ||
| 124 | implemented with the same syscall as utimensat. Only avoid the | ||
| 125 | native utimensat due to an ENOSYS failure; an EINVAL error was | ||
| 126 | data-dependent, and the next caller may pass valid data. */ | ||
| 127 | if (0 <= utimensat_works_really && errno == ENOSYS) | ||
| 128 | utimensat_works_really = -1; | ||
| 129 | return local_utimensat (fd, file, times, flag); | ||
| 130 | } | ||
| 131 | |||
| 132 | #else /* !HAVE_UTIMENSAT */ | ||
| 133 | |||
| 134 | # define AT_FUNC_NAME utimensat | ||
| 135 | |||
| 136 | #endif /* !HAVE_UTIMENSAT */ | ||
| 137 | |||
| 138 | /* Set the access and modification timestamps of FILE to be | ||
| 139 | TIMESPEC[0] and TIMESPEC[1], respectively; relative to directory | ||
| 140 | FD. If flag is AT_SYMLINK_NOFOLLOW, change the times of a symlink, | ||
| 141 | or fail with ENOSYS if not possible. If TIMESPEC is null, set the | ||
| 142 | timestamps to the current time. If possible, do it without | ||
| 143 | changing the working directory. Otherwise, resort to using | ||
| 144 | save_cwd/fchdir, then utimens/restore_cwd. If either the save_cwd | ||
| 145 | or the restore_cwd fails, then give a diagnostic and exit nonzero. | ||
| 146 | Return 0 on success, -1 (setting errno) on failure. */ | ||
| 147 | |||
| 148 | /* AT_FUNC_NAME is now utimensat or local_utimensat. */ | ||
| 149 | #define AT_FUNC_F1 lutimens | ||
| 150 | #define AT_FUNC_F2 utimens | ||
| 151 | #define AT_FUNC_USE_F1_COND AT_SYMLINK_NOFOLLOW | ||
| 152 | #define AT_FUNC_POST_FILE_PARAM_DECLS , struct timespec const ts[2], int flag | ||
| 153 | #define AT_FUNC_POST_FILE_ARGS , ts | ||
| 154 | #include "at-func.c" | ||
| 155 | #undef AT_FUNC_NAME | ||
| 156 | #undef AT_FUNC_F1 | ||
| 157 | #undef AT_FUNC_F2 | ||
| 158 | #undef AT_FUNC_USE_F1_COND | ||
| 159 | #undef AT_FUNC_POST_FILE_PARAM_DECLS | ||
| 160 | #undef AT_FUNC_POST_FILE_ARGS | ||