diff options
| -rwxr-xr-x | admin/merge-gnulib | 4 | ||||
| -rw-r--r-- | doc/lispref/files.texi | 10 | ||||
| -rw-r--r-- | etc/NEWS | 4 | ||||
| -rw-r--r-- | lib/futimens.c | 37 | ||||
| -rw-r--r-- | lib/gnulib.mk.in | 28 | ||||
| -rw-r--r-- | lib/utimensat.c | 160 | ||||
| -rw-r--r-- | lisp/files.el | 7 | ||||
| -rw-r--r-- | lisp/gnus/gnus-cloud.el | 4 | ||||
| -rw-r--r-- | lisp/net/tramp-adb.el | 6 | ||||
| -rw-r--r-- | lisp/net/tramp-gvfs.el | 4 | ||||
| -rw-r--r-- | lisp/net/tramp-sh.el | 3 | ||||
| -rw-r--r-- | lisp/net/tramp-smb.el | 3 | ||||
| -rw-r--r-- | lisp/net/tramp-sudoedit.el | 3 | ||||
| -rw-r--r-- | lisp/tar-mode.el | 2 | ||||
| -rw-r--r-- | m4/futimens.m4 | 65 | ||||
| -rw-r--r-- | m4/gnulib-comp.m4 | 38 | ||||
| -rw-r--r-- | m4/utimensat.m4 | 69 | ||||
| -rw-r--r-- | src/fileio.c | 51 | ||||
| -rw-r--r-- | src/sysdep.c | 15 | ||||
| -rw-r--r-- | src/systime.h | 3 | ||||
| -rw-r--r-- | src/w32.c | 15 | ||||
| -rw-r--r-- | test/lisp/filenotify-tests.el | 8 | ||||
| -rw-r--r-- | test/lisp/files-tests.el | 4 | ||||
| -rw-r--r-- | test/lisp/net/tramp-tests.el | 3 |
24 files changed, 476 insertions, 70 deletions
diff --git a/admin/merge-gnulib b/admin/merge-gnulib index 557119441e4..768e5051f0b 100755 --- a/admin/merge-gnulib +++ b/admin/merge-gnulib | |||
| @@ -34,7 +34,7 @@ GNULIB_MODULES=' | |||
| 34 | d-type diffseq dosname double-slash-root dtoastr dtotimespec dup2 | 34 | d-type diffseq dosname double-slash-root dtoastr dtotimespec dup2 |
| 35 | environ execinfo explicit_bzero faccessat | 35 | environ execinfo explicit_bzero faccessat |
| 36 | fchmodat fcntl fcntl-h fdopendir | 36 | fchmodat fcntl fcntl-h fdopendir |
| 37 | filemode filevercmp flexmember fpieee fstatat fsusage fsync | 37 | filemode filevercmp flexmember fpieee fstatat fsusage fsync futimens |
| 38 | getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog | 38 | getloadavg getopt-gnu gettime gettimeofday gitlog-to-changelog |
| 39 | ieee754-h ignore-value intprops largefile lstat | 39 | ieee754-h ignore-value intprops largefile lstat |
| 40 | manywarnings memmem-simple mempcpy memrchr minmax mkostemp mktime nstrftime | 40 | manywarnings memmem-simple mempcpy memrchr minmax mkostemp mktime nstrftime |
| @@ -43,7 +43,7 @@ GNULIB_MODULES=' | |||
| 43 | sig2str socklen stat-time std-gnu11 stdalign stddef stdio | 43 | sig2str socklen stat-time std-gnu11 stdalign stddef stdio |
| 44 | stpcpy strnlen strtoimax symlink sys_stat sys_time | 44 | stpcpy strnlen strtoimax symlink sys_stat sys_time |
| 45 | tempname time time_r time_rz timegm timer-time timespec-add timespec-sub | 45 | tempname time time_r time_rz timegm timer-time timespec-add timespec-sub |
| 46 | update-copyright unlocked-io utimens | 46 | update-copyright unlocked-io utimensat |
| 47 | vla warnings | 47 | vla warnings |
| 48 | ' | 48 | ' |
| 49 | 49 | ||
diff --git a/doc/lispref/files.texi b/doc/lispref/files.texi index a69a4e5dd38..b3ad9b99649 100644 --- a/doc/lispref/files.texi +++ b/doc/lispref/files.texi | |||
| @@ -1909,11 +1909,19 @@ omitted or @code{nil}, it defaults to 0, i.e., no access rights at | |||
| 1909 | all. | 1909 | all. |
| 1910 | @end defun | 1910 | @end defun |
| 1911 | 1911 | ||
| 1912 | @defun set-file-times filename &optional time | 1912 | @defun set-file-times filename &optional time flag |
| 1913 | This function sets the access and modification times of @var{filename} | 1913 | This function sets the access and modification times of @var{filename} |
| 1914 | to @var{time}. The return value is @code{t} if the times are successfully | 1914 | to @var{time}. The return value is @code{t} if the times are successfully |
| 1915 | set, otherwise it is @code{nil}. @var{time} defaults to the current | 1915 | set, otherwise it is @code{nil}. @var{time} defaults to the current |
| 1916 | time and must be a time value (@pxref{Time of Day}). | 1916 | time and must be a time value (@pxref{Time of Day}). |
| 1917 | |||
| 1918 | By default this function follows symbolic links. However, if the | ||
| 1919 | optional argument @var{flag} is the symbol @code{nofollow}, this | ||
| 1920 | function does not follow @var{filename} if it is a symbolic link; | ||
| 1921 | this can help prevent inadvertently changing the times of a file | ||
| 1922 | somewhere else. On platforms that do not support changing times | ||
| 1923 | on a symbolic link, this function signals an error when @var{filename} | ||
| 1924 | is a symbolic link and @var{flag} is @code{nofollow}. | ||
| 1917 | @end defun | 1925 | @end defun |
| 1918 | 1926 | ||
| 1919 | @defun set-file-extended-attributes filename attribute-alist | 1927 | @defun set-file-extended-attributes filename attribute-alist |
| @@ -225,8 +225,8 @@ called when the function object is garbage-collected. Use | |||
| 225 | 'set_function_finalizer' to set the finalizer and | 225 | 'set_function_finalizer' to set the finalizer and |
| 226 | 'get_function_finalizer' to retrieve it. | 226 | 'get_function_finalizer' to retrieve it. |
| 227 | 227 | ||
| 228 | ** 'file-modes' and 'set-file-modes' now have an optional argument | 228 | ** 'file-modes', 'set-file-modes', and 'set-file-times' now have an |
| 229 | specifying whether to follow symbolic links. | 229 | optional argument specifying whether to follow symbolic links. |
| 230 | 230 | ||
| 231 | ** 'parse-time-string' can now parse ISO 8601 format strings, | 231 | ** 'parse-time-string' can now parse ISO 8601 format strings, |
| 232 | such as "2020-01-15T16:12:21-08:00". | 232 | such as "2020-01-15T16:12:21-08:00". |
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 | ||
diff --git a/lisp/files.el b/lisp/files.el index 2e7694d7677..8ce0187f5b7 100644 --- a/lisp/files.el +++ b/lisp/files.el | |||
| @@ -5944,9 +5944,10 @@ into NEWNAME instead." | |||
| 5944 | ;; Set directory attributes. | 5944 | ;; Set directory attributes. |
| 5945 | (let ((modes (file-modes directory)) | 5945 | (let ((modes (file-modes directory)) |
| 5946 | (times (and keep-time (file-attribute-modification-time | 5946 | (times (and keep-time (file-attribute-modification-time |
| 5947 | (file-attributes directory))))) | 5947 | (file-attributes directory)))) |
| 5948 | (if modes (set-file-modes newname modes (unless follow 'nofollow))) | 5948 | (follow-flag (unless follow 'nofollow))) |
| 5949 | (if times (set-file-times newname times)))))) | 5949 | (if modes (set-file-modes newname modes follow-flag)) |
| 5950 | (if times (set-file-times newname times follow-flag)))))) | ||
| 5950 | 5951 | ||
| 5951 | 5952 | ||
| 5952 | ;; At time of writing, only info uses this. | 5953 | ;; At time of writing, only info uses this. |
diff --git a/lisp/gnus/gnus-cloud.el b/lisp/gnus/gnus-cloud.el index 4d8764bacca..da6231d7330 100644 --- a/lisp/gnus/gnus-cloud.el +++ b/lisp/gnus/gnus-cloud.el | |||
| @@ -285,8 +285,8 @@ Use old data if FORCE-OLDER is not nil." | |||
| 285 | (insert new-contents) | 285 | (insert new-contents) |
| 286 | (when (file-exists-p file-name) | 286 | (when (file-exists-p file-name) |
| 287 | (rename-file file-name (car (find-backup-file-name file-name)))) | 287 | (rename-file file-name (car (find-backup-file-name file-name)))) |
| 288 | (write-region (point-min) (point-max) file-name) | 288 | (write-region (point-min) (point-max) file-name nil nil nil 'excl) |
| 289 | (set-file-times file-name (parse-iso8601-time-string date)))) | 289 | (set-file-times file-name (parse-iso8601-time-string date) 'nofollow))) |
| 290 | 290 | ||
| 291 | (defun gnus-cloud-file-covered-p (file-name) | 291 | (defun gnus-cloud-file-covered-p (file-name) |
| 292 | (let ((matched nil)) | 292 | (let ((matched nil)) |
diff --git a/lisp/net/tramp-adb.el b/lisp/net/tramp-adb.el index 2c9674fa36f..7ee740f93cb 100644 --- a/lisp/net/tramp-adb.el +++ b/lisp/net/tramp-adb.el | |||
| @@ -674,8 +674,9 @@ But handle the case, if the \"test\" command is not available." | |||
| 674 | (tramp-adb-send-command-and-check | 674 | (tramp-adb-send-command-and-check |
| 675 | v (format "chmod %o %s" mode localname))))) | 675 | v (format "chmod %o %s" mode localname))))) |
| 676 | 676 | ||
| 677 | (defun tramp-adb-handle-set-file-times (filename &optional time) | 677 | (defun tramp-adb-handle-set-file-times (filename &optional time flag) |
| 678 | "Like `set-file-times' for Tramp files." | 678 | "Like `set-file-times' for Tramp files." |
| 679 | flag ;; FIXME: Support 'nofollow'. | ||
| 679 | (with-parsed-tramp-file-name filename nil | 680 | (with-parsed-tramp-file-name filename nil |
| 680 | (tramp-flush-file-properties v localname) | 681 | (tramp-flush-file-properties v localname) |
| 681 | (let ((time (if (or (null time) | 682 | (let ((time (if (or (null time) |
| @@ -777,7 +778,8 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." | |||
| 777 | (set-file-times | 778 | (set-file-times |
| 778 | newname | 779 | newname |
| 779 | (tramp-compat-file-attribute-modification-time | 780 | (tramp-compat-file-attribute-modification-time |
| 780 | (file-attributes filename)))))) | 781 | (file-attributes filename)) |
| 782 | (unless ok-if-already-exists 'nofollow))))) | ||
| 781 | 783 | ||
| 782 | (defun tramp-adb-handle-rename-file | 784 | (defun tramp-adb-handle-rename-file |
| 783 | (filename newname &optional ok-if-already-exists) | 785 | (filename newname &optional ok-if-already-exists) |
diff --git a/lisp/net/tramp-gvfs.el b/lisp/net/tramp-gvfs.el index 3ce7bbbd4a3..1ad57c59a5b 100644 --- a/lisp/net/tramp-gvfs.el +++ b/lisp/net/tramp-gvfs.el | |||
| @@ -1571,7 +1571,7 @@ If FILE-SYSTEM is non-nil, return file system attributes." | |||
| 1571 | (tramp-gvfs-url-file-name (tramp-make-tramp-file-name v)) | 1571 | (tramp-gvfs-url-file-name (tramp-make-tramp-file-name v)) |
| 1572 | "unix::mode" (number-to-string mode)))) | 1572 | "unix::mode" (number-to-string mode)))) |
| 1573 | 1573 | ||
| 1574 | (defun tramp-gvfs-handle-set-file-times (filename &optional time) | 1574 | (defun tramp-gvfs-handle-set-file-times (filename &optional time flag) |
| 1575 | "Like `set-file-times' for Tramp files." | 1575 | "Like `set-file-times' for Tramp files." |
| 1576 | (with-parsed-tramp-file-name filename nil | 1576 | (with-parsed-tramp-file-name filename nil |
| 1577 | (tramp-flush-file-properties v localname) | 1577 | (tramp-flush-file-properties v localname) |
| @@ -1582,7 +1582,7 @@ If FILE-SYSTEM is non-nil, return file system attributes." | |||
| 1582 | (current-time) | 1582 | (current-time) |
| 1583 | time))) | 1583 | time))) |
| 1584 | (tramp-gvfs-send-command | 1584 | (tramp-gvfs-send-command |
| 1585 | v "gvfs-set-attribute" "-t" "uint64" | 1585 | v "gvfs-set-attribute" (if flag "-nt" "-t") "uint64" |
| 1586 | (tramp-gvfs-url-file-name (tramp-make-tramp-file-name v)) | 1586 | (tramp-gvfs-url-file-name (tramp-make-tramp-file-name v)) |
| 1587 | "time::modified" (format-time-string "%s" time))))) | 1587 | "time::modified" (format-time-string "%s" time))))) |
| 1588 | 1588 | ||
diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index 84b8191bd3d..560941c4d5b 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el | |||
| @@ -1495,11 +1495,12 @@ of." | |||
| 1495 | mode (tramp-shell-quote-argument localname)) | 1495 | mode (tramp-shell-quote-argument localname)) |
| 1496 | "Error while changing file's mode %s" filename)))) | 1496 | "Error while changing file's mode %s" filename)))) |
| 1497 | 1497 | ||
| 1498 | (defun tramp-sh-handle-set-file-times (filename &optional time) | 1498 | (defun tramp-sh-handle-set-file-times (filename &optional time flag) |
| 1499 | "Like `set-file-times' for Tramp files." | 1499 | "Like `set-file-times' for Tramp files." |
| 1500 | (with-parsed-tramp-file-name filename nil | 1500 | (with-parsed-tramp-file-name filename nil |
| 1501 | (when (tramp-get-remote-touch v) | 1501 | (when (tramp-get-remote-touch v) |
| 1502 | (tramp-flush-file-properties v localname) | 1502 | (tramp-flush-file-properties v localname) |
| 1503 | flag ;; FIXME: Support 'nofollow'. | ||
| 1503 | (let ((time | 1504 | (let ((time |
| 1504 | (if (or (null time) | 1505 | (if (or (null time) |
| 1505 | (tramp-compat-time-equal-p time tramp-time-doesnt-exist) | 1506 | (tramp-compat-time-equal-p time tramp-time-doesnt-exist) |
diff --git a/lisp/net/tramp-smb.el b/lisp/net/tramp-smb.el index 42954cbda3d..d91362c879c 100644 --- a/lisp/net/tramp-smb.el +++ b/lisp/net/tramp-smb.el | |||
| @@ -619,7 +619,8 @@ PRESERVE-UID-GID and PRESERVE-EXTENDED-ATTRIBUTES are completely ignored." | |||
| 619 | (set-file-times | 619 | (set-file-times |
| 620 | newname | 620 | newname |
| 621 | (tramp-compat-file-attribute-modification-time | 621 | (tramp-compat-file-attribute-modification-time |
| 622 | (file-attributes filename)))))) | 622 | (file-attributes filename)) |
| 623 | (unless ok-if-already-exists 'nofollow))))) | ||
| 623 | 624 | ||
| 624 | (defun tramp-smb-handle-delete-directory (directory &optional recursive _trash) | 625 | (defun tramp-smb-handle-delete-directory (directory &optional recursive _trash) |
| 625 | "Like `delete-directory' for Tramp files." | 626 | "Like `delete-directory' for Tramp files." |
diff --git a/lisp/net/tramp-sudoedit.el b/lisp/net/tramp-sudoedit.el index 7d8c8a90618..c054f405e3d 100644 --- a/lisp/net/tramp-sudoedit.el +++ b/lisp/net/tramp-sudoedit.el | |||
| @@ -523,10 +523,11 @@ the result will be a local, non-Tramp, file name." | |||
| 523 | (string-to-number (match-string 2))) | 523 | (string-to-number (match-string 2))) |
| 524 | (string-to-number (match-string 3))))))))) | 524 | (string-to-number (match-string 3))))))))) |
| 525 | 525 | ||
| 526 | (defun tramp-sudoedit-handle-set-file-times (filename &optional time) | 526 | (defun tramp-sudoedit-handle-set-file-times (filename &optional time flag) |
| 527 | "Like `set-file-times' for Tramp files." | 527 | "Like `set-file-times' for Tramp files." |
| 528 | (with-parsed-tramp-file-name filename nil | 528 | (with-parsed-tramp-file-name filename nil |
| 529 | (tramp-flush-file-properties v localname) | 529 | (tramp-flush-file-properties v localname) |
| 530 | flag ;; FIXME: Support 'nofollow'. | ||
| 530 | (let ((time | 531 | (let ((time |
| 531 | (if (or (null time) | 532 | (if (or (null time) |
| 532 | (tramp-compat-time-equal-p time tramp-time-doesnt-exist) | 533 | (tramp-compat-time-equal-p time tramp-time-doesnt-exist) |
diff --git a/lisp/tar-mode.el b/lisp/tar-mode.el index 97d883eebd9..a3c1715b1e1 100644 --- a/lisp/tar-mode.el +++ b/lisp/tar-mode.el | |||
| @@ -1056,7 +1056,7 @@ extracted file." | |||
| 1056 | (write-region start end to-file nil nil nil t)) | 1056 | (write-region start end to-file nil nil nil t)) |
| 1057 | (when (and tar-copy-preserve-time | 1057 | (when (and tar-copy-preserve-time |
| 1058 | date) | 1058 | date) |
| 1059 | (set-file-times to-file date))) | 1059 | (set-file-times to-file date 'nofollow))) |
| 1060 | (message "Copied tar entry %s to %s" name to-file))) | 1060 | (message "Copied tar entry %s to %s" name to-file))) |
| 1061 | 1061 | ||
| 1062 | (defun tar-new-entry (filename &optional index) | 1062 | (defun tar-new-entry (filename &optional index) |
diff --git a/m4/futimens.m4 b/m4/futimens.m4 new file mode 100644 index 00000000000..dc5cfa94119 --- /dev/null +++ b/m4/futimens.m4 | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | # serial 8 | ||
| 2 | # See if we need to provide futimens replacement. | ||
| 3 | |||
| 4 | dnl Copyright (C) 2009-2020 Free Software Foundation, Inc. | ||
| 5 | dnl This file is free software; the Free Software Foundation | ||
| 6 | dnl gives unlimited permission to copy and/or distribute it, | ||
| 7 | dnl with or without modifications, as long as this notice is preserved. | ||
| 8 | |||
| 9 | # Written by Eric Blake. | ||
| 10 | |||
| 11 | AC_DEFUN([gl_FUNC_FUTIMENS], | ||
| 12 | [ | ||
| 13 | AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS]) | ||
| 14 | AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles | ||
| 15 | AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) | ||
| 16 | AC_CHECK_FUNCS_ONCE([futimens]) | ||
| 17 | if test $ac_cv_func_futimens = no; then | ||
| 18 | HAVE_FUTIMENS=0 | ||
| 19 | else | ||
| 20 | AC_CACHE_CHECK([whether futimens works], | ||
| 21 | [gl_cv_func_futimens_works], | ||
| 22 | [AC_RUN_IFELSE([AC_LANG_PROGRAM([[ | ||
| 23 | #include <fcntl.h> | ||
| 24 | #include <sys/stat.h> | ||
| 25 | #include <unistd.h> | ||
| 26 | #include <errno.h> | ||
| 27 | ]], [[struct timespec ts[2]; | ||
| 28 | int fd = creat ("conftest.file", 0600); | ||
| 29 | struct stat st; | ||
| 30 | if (fd < 0) return 1; | ||
| 31 | ts[0].tv_sec = 1; | ||
| 32 | ts[0].tv_nsec = UTIME_OMIT; | ||
| 33 | ts[1].tv_sec = 1; | ||
| 34 | ts[1].tv_nsec = UTIME_NOW; | ||
| 35 | errno = 0; | ||
| 36 | if (futimens (AT_FDCWD, NULL) == 0) return 2; | ||
| 37 | if (errno != EBADF) return 3; | ||
| 38 | if (futimens (fd, ts)) return 4; | ||
| 39 | sleep (1); | ||
| 40 | ts[0].tv_nsec = UTIME_NOW; | ||
| 41 | ts[1].tv_nsec = UTIME_OMIT; | ||
| 42 | if (futimens (fd, ts)) return 5; | ||
| 43 | if (fstat (fd, &st)) return 6; | ||
| 44 | if (st.st_ctime < st.st_atime) return 7; | ||
| 45 | ]])], | ||
| 46 | [gl_cv_func_futimens_works=yes], | ||
| 47 | [gl_cv_func_futimens_works=no], | ||
| 48 | [case "$host_os" in | ||
| 49 | # Guess no on glibc systems. | ||
| 50 | *-gnu* | gnu*) gl_cv_func_futimens_works="guessing no" ;; | ||
| 51 | # Guess no on musl systems. | ||
| 52 | *-musl*) gl_cv_func_futimens_works="guessing no" ;; | ||
| 53 | # Guess yes otherwise. | ||
| 54 | *) gl_cv_func_futimens_works="guessing yes" ;; | ||
| 55 | esac | ||
| 56 | ]) | ||
| 57 | rm -f conftest.file]) | ||
| 58 | case "$gl_cv_func_futimens_works" in | ||
| 59 | *yes) ;; | ||
| 60 | *) | ||
| 61 | REPLACE_FUTIMENS=1 | ||
| 62 | ;; | ||
| 63 | esac | ||
| 64 | fi | ||
| 65 | ]) | ||
diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index 1465ce811b8..3228aa42b57 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 | |||
| @@ -95,6 +95,7 @@ AC_DEFUN([gl_EARLY], | |||
| 95 | # Code from module fstatat: | 95 | # Code from module fstatat: |
| 96 | # Code from module fsusage: | 96 | # Code from module fsusage: |
| 97 | # Code from module fsync: | 97 | # Code from module fsync: |
| 98 | # Code from module futimens: | ||
| 98 | # Code from module getdtablesize: | 99 | # Code from module getdtablesize: |
| 99 | # Code from module getgroups: | 100 | # Code from module getgroups: |
| 100 | # Code from module getloadavg: | 101 | # Code from module getloadavg: |
| @@ -179,6 +180,7 @@ AC_DEFUN([gl_EARLY], | |||
| 179 | # Code from module unlocked-io: | 180 | # Code from module unlocked-io: |
| 180 | # Code from module update-copyright: | 181 | # Code from module update-copyright: |
| 181 | # Code from module utimens: | 182 | # Code from module utimens: |
| 183 | # Code from module utimensat: | ||
| 182 | # Code from module vararrays: | 184 | # Code from module vararrays: |
| 183 | # Code from module verify: | 185 | # Code from module verify: |
| 184 | # Code from module vla: | 186 | # Code from module vla: |
| @@ -297,6 +299,11 @@ AC_DEFUN([gl_INIT], | |||
| 297 | gl_PREREQ_FSYNC | 299 | gl_PREREQ_FSYNC |
| 298 | fi | 300 | fi |
| 299 | gl_UNISTD_MODULE_INDICATOR([fsync]) | 301 | gl_UNISTD_MODULE_INDICATOR([fsync]) |
| 302 | gl_FUNC_FUTIMENS | ||
| 303 | if test $HAVE_FUTIMENS = 0 || test $REPLACE_FUTIMENS = 1; then | ||
| 304 | AC_LIBOBJ([futimens]) | ||
| 305 | fi | ||
| 306 | gl_SYS_STAT_MODULE_INDICATOR([futimens]) | ||
| 300 | gl_GETLOADAVG | 307 | gl_GETLOADAVG |
| 301 | if test $HAVE_GETLOADAVG = 0; then | 308 | if test $HAVE_GETLOADAVG = 0; then |
| 302 | AC_LIBOBJ([getloadavg]) | 309 | AC_LIBOBJ([getloadavg]) |
| @@ -466,7 +473,11 @@ AC_DEFUN([gl_INIT], | |||
| 466 | gl_TIMESPEC | 473 | gl_TIMESPEC |
| 467 | gl_UNISTD_H | 474 | gl_UNISTD_H |
| 468 | gl_FUNC_GLIBC_UNLOCKED_IO | 475 | gl_FUNC_GLIBC_UNLOCKED_IO |
| 469 | gl_UTIMENS | 476 | gl_FUNC_UTIMENSAT |
| 477 | if test $HAVE_UTIMENSAT = 0 || test $REPLACE_UTIMENSAT = 1; then | ||
| 478 | AC_LIBOBJ([utimensat]) | ||
| 479 | fi | ||
| 480 | gl_SYS_STAT_MODULE_INDICATOR([utimensat]) | ||
| 470 | AC_C_VARARRAYS | 481 | AC_C_VARARRAYS |
| 471 | gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=false | 482 | gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b=false |
| 472 | gl_gnulib_enabled_cloexec=false | 483 | gl_gnulib_enabled_cloexec=false |
| @@ -485,6 +496,7 @@ AC_DEFUN([gl_INIT], | |||
| 485 | gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=false | 496 | gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7=false |
| 486 | gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false | 497 | gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c=false |
| 487 | gl_gnulib_enabled_strtoll=false | 498 | gl_gnulib_enabled_strtoll=false |
| 499 | gl_gnulib_enabled_utimens=false | ||
| 488 | gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false | 500 | gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec=false |
| 489 | func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b () | 501 | func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b () |
| 490 | { | 502 | { |
| @@ -663,6 +675,13 @@ AC_DEFUN([gl_INIT], | |||
| 663 | gl_gnulib_enabled_strtoll=true | 675 | gl_gnulib_enabled_strtoll=true |
| 664 | fi | 676 | fi |
| 665 | } | 677 | } |
| 678 | func_gl_gnulib_m4code_utimens () | ||
| 679 | { | ||
| 680 | if ! $gl_gnulib_enabled_utimens; then | ||
| 681 | gl_UTIMENS | ||
| 682 | gl_gnulib_enabled_utimens=true | ||
| 683 | fi | ||
| 684 | } | ||
| 666 | func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec () | 685 | func_gl_gnulib_m4code_682e609604ccaac6be382e4ee3a4eaec () |
| 667 | { | 686 | { |
| 668 | if ! $gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec; then | 687 | if ! $gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec; then |
| @@ -705,6 +724,9 @@ AC_DEFUN([gl_INIT], | |||
| 705 | if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then | 724 | if test $HAVE_FSTATAT = 0 || test $REPLACE_FSTATAT = 1; then |
| 706 | func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 | 725 | func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 |
| 707 | fi | 726 | fi |
| 727 | if test $HAVE_FUTIMENS = 0 || test $REPLACE_FUTIMENS = 1; then | ||
| 728 | func_gl_gnulib_m4code_utimens | ||
| 729 | fi | ||
| 708 | if test $REPLACE_GETOPT = 1; then | 730 | if test $REPLACE_GETOPT = 1; then |
| 709 | func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36 | 731 | func_gl_gnulib_m4code_be453cec5eecf5731a274f2de7f2db36 |
| 710 | fi | 732 | fi |
| @@ -729,6 +751,15 @@ AC_DEFUN([gl_INIT], | |||
| 729 | if test $HAVE_TIMEGM = 0 || test $REPLACE_TIMEGM = 1; then | 751 | if test $HAVE_TIMEGM = 0 || test $REPLACE_TIMEGM = 1; then |
| 730 | func_gl_gnulib_m4code_5264294aa0a5557541b53c8c741f7f31 | 752 | func_gl_gnulib_m4code_5264294aa0a5557541b53c8c741f7f31 |
| 731 | fi | 753 | fi |
| 754 | if test $HAVE_UTIMENSAT = 0 || test $REPLACE_UTIMENSAT = 1; then | ||
| 755 | func_gl_gnulib_m4code_260941c0e5dc67ec9e87d1fb321c300b | ||
| 756 | fi | ||
| 757 | if test $HAVE_UTIMENSAT = 0 || test $REPLACE_UTIMENSAT = 1; then | ||
| 758 | func_gl_gnulib_m4code_03e0aaad4cb89ca757653bd367a6ccb7 | ||
| 759 | fi | ||
| 760 | if test $HAVE_UTIMENSAT = 0 || test $REPLACE_UTIMENSAT = 1; then | ||
| 761 | func_gl_gnulib_m4code_utimens | ||
| 762 | fi | ||
| 732 | m4_pattern_allow([^gl_GNULIB_ENABLED_]) | 763 | m4_pattern_allow([^gl_GNULIB_ENABLED_]) |
| 733 | AM_CONDITIONAL([gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b], [$gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b]) | 764 | AM_CONDITIONAL([gl_GNULIB_ENABLED_260941c0e5dc67ec9e87d1fb321c300b], [$gl_gnulib_enabled_260941c0e5dc67ec9e87d1fb321c300b]) |
| 734 | AM_CONDITIONAL([gl_GNULIB_ENABLED_cloexec], [$gl_gnulib_enabled_cloexec]) | 765 | AM_CONDITIONAL([gl_GNULIB_ENABLED_cloexec], [$gl_gnulib_enabled_cloexec]) |
| @@ -747,6 +778,7 @@ AC_DEFUN([gl_INIT], | |||
| 747 | AM_CONDITIONAL([gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7], [$gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7]) | 778 | AM_CONDITIONAL([gl_GNULIB_ENABLED_03e0aaad4cb89ca757653bd367a6ccb7], [$gl_gnulib_enabled_03e0aaad4cb89ca757653bd367a6ccb7]) |
| 748 | AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c]) | 779 | AM_CONDITIONAL([gl_GNULIB_ENABLED_6099e9737f757db36c47fa9d9f02e88c], [$gl_gnulib_enabled_6099e9737f757db36c47fa9d9f02e88c]) |
| 749 | AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoll], [$gl_gnulib_enabled_strtoll]) | 780 | AM_CONDITIONAL([gl_GNULIB_ENABLED_strtoll], [$gl_gnulib_enabled_strtoll]) |
| 781 | AM_CONDITIONAL([gl_GNULIB_ENABLED_utimens], [$gl_gnulib_enabled_utimens]) | ||
| 750 | AM_CONDITIONAL([gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec], [$gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec]) | 782 | AM_CONDITIONAL([gl_GNULIB_ENABLED_682e609604ccaac6be382e4ee3a4eaec], [$gl_gnulib_enabled_682e609604ccaac6be382e4ee3a4eaec]) |
| 751 | # End of code from modules | 783 | # End of code from modules |
| 752 | m4_ifval(gl_LIBSOURCES_LIST, [ | 784 | m4_ifval(gl_LIBSOURCES_LIST, [ |
| @@ -956,6 +988,7 @@ AC_DEFUN([gl_FILE_LIST], [ | |||
| 956 | lib/fsync.c | 988 | lib/fsync.c |
| 957 | lib/ftoastr.c | 989 | lib/ftoastr.c |
| 958 | lib/ftoastr.h | 990 | lib/ftoastr.h |
| 991 | lib/futimens.c | ||
| 959 | lib/get-permissions.c | 992 | lib/get-permissions.c |
| 960 | lib/getdtablesize.c | 993 | lib/getdtablesize.c |
| 961 | lib/getgroups.c | 994 | lib/getgroups.c |
| @@ -1063,6 +1096,7 @@ AC_DEFUN([gl_FILE_LIST], [ | |||
| 1063 | lib/unlocked-io.h | 1096 | lib/unlocked-io.h |
| 1064 | lib/utimens.c | 1097 | lib/utimens.c |
| 1065 | lib/utimens.h | 1098 | lib/utimens.h |
| 1099 | lib/utimensat.c | ||
| 1066 | lib/verify.h | 1100 | lib/verify.h |
| 1067 | lib/vla.h | 1101 | lib/vla.h |
| 1068 | lib/warn-on-use.h | 1102 | lib/warn-on-use.h |
| @@ -1103,6 +1137,7 @@ AC_DEFUN([gl_FILE_LIST], [ | |||
| 1103 | m4/fstatat.m4 | 1137 | m4/fstatat.m4 |
| 1104 | m4/fsusage.m4 | 1138 | m4/fsusage.m4 |
| 1105 | m4/fsync.m4 | 1139 | m4/fsync.m4 |
| 1140 | m4/futimens.m4 | ||
| 1106 | m4/getdtablesize.m4 | 1141 | m4/getdtablesize.m4 |
| 1107 | m4/getgroups.m4 | 1142 | m4/getgroups.m4 |
| 1108 | m4/getloadavg.m4 | 1143 | m4/getloadavg.m4 |
| @@ -1184,6 +1219,7 @@ AC_DEFUN([gl_FILE_LIST], [ | |||
| 1184 | m4/unistd_h.m4 | 1219 | m4/unistd_h.m4 |
| 1185 | m4/unlocked-io.m4 | 1220 | m4/unlocked-io.m4 |
| 1186 | m4/utimens.m4 | 1221 | m4/utimens.m4 |
| 1222 | m4/utimensat.m4 | ||
| 1187 | m4/utimes.m4 | 1223 | m4/utimes.m4 |
| 1188 | m4/vararrays.m4 | 1224 | m4/vararrays.m4 |
| 1189 | m4/warn-on-use.m4 | 1225 | m4/warn-on-use.m4 |
diff --git a/m4/utimensat.m4 b/m4/utimensat.m4 new file mode 100644 index 00000000000..2bc1bfebb5d --- /dev/null +++ b/m4/utimensat.m4 | |||
| @@ -0,0 +1,69 @@ | |||
| 1 | # serial 6 | ||
| 2 | # See if we need to provide utimensat replacement. | ||
| 3 | |||
| 4 | dnl Copyright (C) 2009-2020 Free Software Foundation, Inc. | ||
| 5 | dnl This file is free software; the Free Software Foundation | ||
| 6 | dnl gives unlimited permission to copy and/or distribute it, | ||
| 7 | dnl with or without modifications, as long as this notice is preserved. | ||
| 8 | |||
| 9 | # Written by Eric Blake. | ||
| 10 | |||
| 11 | AC_DEFUN([gl_FUNC_UTIMENSAT], | ||
| 12 | [ | ||
| 13 | AC_REQUIRE([gl_SYS_STAT_H_DEFAULTS]) | ||
| 14 | AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) | ||
| 15 | AC_CHECK_FUNCS_ONCE([utimensat]) | ||
| 16 | if test $ac_cv_func_utimensat = no; then | ||
| 17 | HAVE_UTIMENSAT=0 | ||
| 18 | else | ||
| 19 | AC_CACHE_CHECK([whether utimensat works], | ||
| 20 | [gl_cv_func_utimensat_works], | ||
| 21 | [AC_RUN_IFELSE( | ||
| 22 | [AC_LANG_PROGRAM([[ | ||
| 23 | #include <fcntl.h> | ||
| 24 | #include <sys/stat.h> | ||
| 25 | #include <unistd.h> | ||
| 26 | ]], [[int result = 0; | ||
| 27 | const char *f = "conftest.file"; | ||
| 28 | if (close (creat (f, 0600))) | ||
| 29 | return 1; | ||
| 30 | /* Test whether the AT_SYMLINK_NOFOLLOW flag is supported. */ | ||
| 31 | { | ||
| 32 | if (utimensat (AT_FDCWD, f, NULL, AT_SYMLINK_NOFOLLOW)) | ||
| 33 | result |= 2; | ||
| 34 | } | ||
| 35 | /* Test whether UTIME_NOW and UTIME_OMIT work. */ | ||
| 36 | { | ||
| 37 | struct timespec ts[2]; | ||
| 38 | ts[0].tv_sec = 1; | ||
| 39 | ts[0].tv_nsec = UTIME_OMIT; | ||
| 40 | ts[1].tv_sec = 1; | ||
| 41 | ts[1].tv_nsec = UTIME_NOW; | ||
| 42 | if (utimensat (AT_FDCWD, f, ts, 0)) | ||
| 43 | result |= 4; | ||
| 44 | } | ||
| 45 | sleep (1); | ||
| 46 | { | ||
| 47 | struct stat st; | ||
| 48 | struct timespec ts[2]; | ||
| 49 | ts[0].tv_sec = 1; | ||
| 50 | ts[0].tv_nsec = UTIME_NOW; | ||
| 51 | ts[1].tv_sec = 1; | ||
| 52 | ts[1].tv_nsec = UTIME_OMIT; | ||
| 53 | if (utimensat (AT_FDCWD, f, ts, 0)) | ||
| 54 | result |= 8; | ||
| 55 | if (stat (f, &st)) | ||
| 56 | result |= 16; | ||
| 57 | else if (st.st_ctime < st.st_atime) | ||
| 58 | result |= 32; | ||
| 59 | } | ||
| 60 | return result; | ||
| 61 | ]])], | ||
| 62 | [gl_cv_func_utimensat_works=yes], | ||
| 63 | [gl_cv_func_utimensat_works=no], | ||
| 64 | [gl_cv_func_utimensat_works="guessing yes"])]) | ||
| 65 | if test "$gl_cv_func_utimensat_works" = no; then | ||
| 66 | REPLACE_UTIMENSAT=1 | ||
| 67 | fi | ||
| 68 | fi | ||
| 69 | ]) | ||
diff --git a/src/fileio.c b/src/fileio.c index 2532f5233c4..82fd7989206 100644 --- a/src/fileio.c +++ b/src/fileio.c | |||
| @@ -2253,9 +2253,8 @@ permissions. */) | |||
| 2253 | 2253 | ||
| 2254 | if (!NILP (keep_time)) | 2254 | if (!NILP (keep_time)) |
| 2255 | { | 2255 | { |
| 2256 | struct timespec atime = get_stat_atime (&st); | 2256 | struct timespec ts[] = { get_stat_atime (&st), get_stat_mtime (&st) }; |
| 2257 | struct timespec mtime = get_stat_mtime (&st); | 2257 | if (futimens (ofd, ts) != 0) |
| 2258 | if (set_file_times (ofd, SSDATA (encoded_newname), atime, mtime) != 0) | ||
| 2259 | xsignal2 (Qfile_date_error, | 2258 | xsignal2 (Qfile_date_error, |
| 2260 | build_string ("Cannot set file date"), newname); | 2259 | build_string ("Cannot set file date"), newname); |
| 2261 | } | 2260 | } |
| @@ -3430,39 +3429,41 @@ The value is an integer. */) | |||
| 3430 | } | 3429 | } |
| 3431 | 3430 | ||
| 3432 | 3431 | ||
| 3433 | DEFUN ("set-file-times", Fset_file_times, Sset_file_times, 1, 2, 0, | 3432 | DEFUN ("set-file-times", Fset_file_times, Sset_file_times, 1, 3, 0, |
| 3434 | doc: /* Set times of file FILENAME to TIMESTAMP. | 3433 | doc: /* Set times of file FILENAME to TIMESTAMP. |
| 3435 | Set both access and modification times. | 3434 | If optional FLAG is `nofollow', do not follow FILENAME if it is a |
| 3436 | Return t on success, else nil. | 3435 | symbolic link. Set both access and modification times. Return t on |
| 3437 | Use the current time if TIMESTAMP is nil. TIMESTAMP is in the format of | 3436 | success, else nil. Use the current time if TIMESTAMP is nil. |
| 3438 | `current-time'. */) | 3437 | TIMESTAMP is in the format of `current-time'. */) |
| 3439 | (Lisp_Object filename, Lisp_Object timestamp) | 3438 | (Lisp_Object filename, Lisp_Object timestamp, Lisp_Object flag) |
| 3440 | { | 3439 | { |
| 3441 | Lisp_Object absname, encoded_absname; | 3440 | int nofollow = symlink_nofollow_flag (flag); |
| 3442 | Lisp_Object handler; | ||
| 3443 | struct timespec t = lisp_time_argument (timestamp); | ||
| 3444 | 3441 | ||
| 3445 | absname = Fexpand_file_name (filename, BVAR (current_buffer, directory)); | 3442 | struct timespec ts[2]; |
| 3443 | if (!NILP (timestamp)) | ||
| 3444 | ts[0] = ts[1] = lisp_time_argument (timestamp); | ||
| 3445 | else | ||
| 3446 | ts[0].tv_nsec = ts[1].tv_nsec = UTIME_NOW; | ||
| 3446 | 3447 | ||
| 3447 | /* If the file name has special constructs in it, | 3448 | /* If the file name has special constructs in it, |
| 3448 | call the corresponding file name handler. */ | 3449 | call the corresponding file name handler. */ |
| 3449 | handler = Ffind_file_name_handler (absname, Qset_file_times); | 3450 | Lisp_Object |
| 3451 | absname = Fexpand_file_name (filename, BVAR (current_buffer, directory)), | ||
| 3452 | handler = Ffind_file_name_handler (absname, Qset_file_times); | ||
| 3450 | if (!NILP (handler)) | 3453 | if (!NILP (handler)) |
| 3451 | return call3 (handler, Qset_file_times, absname, timestamp); | 3454 | return call4 (handler, Qset_file_times, absname, timestamp, flag); |
| 3452 | 3455 | ||
| 3453 | encoded_absname = ENCODE_FILE (absname); | 3456 | Lisp_Object encoded_absname = ENCODE_FILE (absname); |
| 3454 | 3457 | ||
| 3455 | { | 3458 | if (utimensat (AT_FDCWD, SSDATA (encoded_absname), ts, nofollow) != 0) |
| 3456 | if (set_file_times (-1, SSDATA (encoded_absname), t, t) != 0) | 3459 | { |
| 3457 | { | ||
| 3458 | #ifdef MSDOS | 3460 | #ifdef MSDOS |
| 3459 | /* Setting times on a directory always fails. */ | 3461 | /* Setting times on a directory always fails. */ |
| 3460 | if (file_directory_p (encoded_absname)) | 3462 | if (file_directory_p (encoded_absname)) |
| 3461 | return Qnil; | 3463 | return Qnil; |
| 3462 | #endif | 3464 | #endif |
| 3463 | report_file_error ("Setting file times", absname); | 3465 | report_file_error ("Setting file times", absname); |
| 3464 | } | 3466 | } |
| 3465 | } | ||
| 3466 | 3467 | ||
| 3467 | return Qt; | 3468 | return Qt; |
| 3468 | } | 3469 | } |
diff --git a/src/sysdep.c b/src/sysdep.c index e8e8bbfb502..149d80f19ec 100644 --- a/src/sysdep.c +++ b/src/sysdep.c | |||
| @@ -2752,21 +2752,6 @@ emacs_perror (char const *message) | |||
| 2752 | errno = err; | 2752 | errno = err; |
| 2753 | } | 2753 | } |
| 2754 | 2754 | ||
| 2755 | /* Set the access and modification time stamps of FD (a.k.a. FILE) to be | ||
| 2756 | ATIME and MTIME, respectively. | ||
| 2757 | FD must be either negative -- in which case it is ignored -- | ||
| 2758 | or a file descriptor that is open on FILE. | ||
| 2759 | If FD is nonnegative, then FILE can be NULL. */ | ||
| 2760 | int | ||
| 2761 | set_file_times (int fd, const char *filename, | ||
| 2762 | struct timespec atime, struct timespec mtime) | ||
| 2763 | { | ||
| 2764 | struct timespec timespec[2]; | ||
| 2765 | timespec[0] = atime; | ||
| 2766 | timespec[1] = mtime; | ||
| 2767 | return fdutimens (fd, filename, timespec); | ||
| 2768 | } | ||
| 2769 | |||
| 2770 | /* Rename directory SRCFD's entry SRC to directory DSTFD's entry DST. | 2755 | /* Rename directory SRCFD's entry SRC to directory DSTFD's entry DST. |
| 2771 | This is like renameat except that it fails if DST already exists, | 2756 | This is like renameat except that it fails if DST already exists, |
| 2772 | or if this operation is not supported atomically. Return 0 if | 2757 | or if this operation is not supported atomically. Return 0 if |
diff --git a/src/systime.h b/src/systime.h index 00ca4a1c58d..b59a3d1c690 100644 --- a/src/systime.h +++ b/src/systime.h | |||
| @@ -67,9 +67,6 @@ timespec_valid_p (struct timespec t) | |||
| 67 | return t.tv_nsec >= 0; | 67 | return t.tv_nsec >= 0; |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | /* defined in sysdep.c */ | ||
| 71 | extern int set_file_times (int, const char *, struct timespec, struct timespec); | ||
| 72 | |||
| 73 | /* defined in keyboard.c */ | 70 | /* defined in keyboard.c */ |
| 74 | extern void set_waiting_for_input (struct timespec *); | 71 | extern void set_waiting_for_input (struct timespec *); |
| 75 | 72 | ||
| @@ -3189,6 +3189,21 @@ fdutimens (int fd, char const *file, struct timespec const timespec[2]) | |||
| 3189 | } | 3189 | } |
| 3190 | } | 3190 | } |
| 3191 | 3191 | ||
| 3192 | /* Set the access and modification time stamps of FD (a.k.a. FILE) to be | ||
| 3193 | ATIME and MTIME, respectively. | ||
| 3194 | FD must be either negative -- in which case it is ignored -- | ||
| 3195 | or a file descriptor that is open on FILE. | ||
| 3196 | If FD is nonnegative, then FILE can be NULL. */ | ||
| 3197 | static int | ||
| 3198 | set_file_times (int fd, const char *filename, | ||
| 3199 | struct timespec atime, struct timespec mtime) | ||
| 3200 | { | ||
| 3201 | struct timespec timespec[2]; | ||
| 3202 | timespec[0] = atime; | ||
| 3203 | timespec[1] = mtime; | ||
| 3204 | return fdutimens (fd, filename, timespec); | ||
| 3205 | } | ||
| 3206 | |||
| 3192 | 3207 | ||
| 3193 | /* ------------------------------------------------------------------------- */ | 3208 | /* ------------------------------------------------------------------------- */ |
| 3194 | /* IO support and wrapper functions for the Windows API. */ | 3209 | /* IO support and wrapper functions for the Windows API. */ |
diff --git a/test/lisp/filenotify-tests.el b/test/lisp/filenotify-tests.el index 39156fbb5dc..a184fabb9ff 100644 --- a/test/lisp/filenotify-tests.el +++ b/test/lisp/filenotify-tests.el | |||
| @@ -771,9 +771,9 @@ delivered." | |||
| 771 | (copy-file file-notify--test-tmpfile file-notify--test-tmpfile1) | 771 | (copy-file file-notify--test-tmpfile file-notify--test-tmpfile1) |
| 772 | ;; The next two events shall not be visible. | 772 | ;; The next two events shall not be visible. |
| 773 | (file-notify--test-read-event) | 773 | (file-notify--test-read-event) |
| 774 | (set-file-modes file-notify--test-tmpfile 000) | 774 | (set-file-modes file-notify--test-tmpfile 000 'nofollow) |
| 775 | (file-notify--test-read-event) | 775 | (file-notify--test-read-event) |
| 776 | (set-file-times file-notify--test-tmpfile '(0 0)) | 776 | (set-file-times file-notify--test-tmpfile '(0 0) 'nofollow) |
| 777 | (file-notify--test-read-event) | 777 | (file-notify--test-read-event) |
| 778 | (delete-directory file-notify--test-tmpdir 'recursive)) | 778 | (delete-directory file-notify--test-tmpdir 'recursive)) |
| 779 | (file-notify-rm-watch file-notify--test-desc) | 779 | (file-notify-rm-watch file-notify--test-desc) |
| @@ -864,9 +864,9 @@ delivered." | |||
| 864 | (write-region | 864 | (write-region |
| 865 | "any text" nil file-notify--test-tmpfile nil 'no-message) | 865 | "any text" nil file-notify--test-tmpfile nil 'no-message) |
| 866 | (file-notify--test-read-event) | 866 | (file-notify--test-read-event) |
| 867 | (set-file-modes file-notify--test-tmpfile 000) | 867 | (set-file-modes file-notify--test-tmpfile 000 'nofollow) |
| 868 | (file-notify--test-read-event) | 868 | (file-notify--test-read-event) |
| 869 | (set-file-times file-notify--test-tmpfile '(0 0)) | 869 | (set-file-times file-notify--test-tmpfile '(0 0) 'nofollow) |
| 870 | (file-notify--test-read-event) | 870 | (file-notify--test-read-event) |
| 871 | (delete-file file-notify--test-tmpfile)) | 871 | (delete-file file-notify--test-tmpfile)) |
| 872 | (file-notify-rm-watch file-notify--test-desc) | 872 | (file-notify-rm-watch file-notify--test-desc) |
diff --git a/test/lisp/files-tests.el b/test/lisp/files-tests.el index ac56a7732f2..05d9ceebf1d 100644 --- a/test/lisp/files-tests.el +++ b/test/lisp/files-tests.el | |||
| @@ -1003,9 +1003,9 @@ unquoted file names." | |||
| 1003 | 1003 | ||
| 1004 | (ert-deftest files-tests-file-name-non-special-set-file-times () | 1004 | (ert-deftest files-tests-file-name-non-special-set-file-times () |
| 1005 | (files-tests--with-temp-non-special (tmpfile nospecial) | 1005 | (files-tests--with-temp-non-special (tmpfile nospecial) |
| 1006 | (set-file-times nospecial)) | 1006 | (set-file-times nospecial nil 'nofollow)) |
| 1007 | (files-tests--with-temp-non-special-and-file-name-handler (tmpfile nospecial) | 1007 | (files-tests--with-temp-non-special-and-file-name-handler (tmpfile nospecial) |
| 1008 | (should-error (set-file-times nospecial)))) | 1008 | (should-error (set-file-times nospecial nil 'nofollow)))) |
| 1009 | 1009 | ||
| 1010 | (ert-deftest files-tests-file-name-non-special-set-visited-file-modtime () | 1010 | (ert-deftest files-tests-file-name-non-special-set-visited-file-modtime () |
| 1011 | (files-tests--with-temp-non-special (tmpfile nospecial) | 1011 | (files-tests--with-temp-non-special (tmpfile nospecial) |
diff --git a/test/lisp/net/tramp-tests.el b/test/lisp/net/tramp-tests.el index be0f418c943..dcf376e70b4 100644 --- a/test/lisp/net/tramp-tests.el +++ b/test/lisp/net/tramp-tests.el | |||
| @@ -3743,7 +3743,8 @@ This tests also `make-symbolic-link', `file-truename' and `add-name-to-file'." | |||
| 3743 | (file-attributes tmp-name1)))) | 3743 | (file-attributes tmp-name1)))) |
| 3744 | ;; Skip the test, if the remote handler is not able to set | 3744 | ;; Skip the test, if the remote handler is not able to set |
| 3745 | ;; the correct time. | 3745 | ;; the correct time. |
| 3746 | (skip-unless (set-file-times tmp-name1 (seconds-to-time 1))) | 3746 | (skip-unless (set-file-times tmp-name1 (seconds-to-time 1) |
| 3747 | 'nofollow)) | ||
| 3747 | ;; Dumb remote shells without perl(1) or stat(1) are not | 3748 | ;; Dumb remote shells without perl(1) or stat(1) are not |
| 3748 | ;; able to return the date correctly. They say "don't know". | 3749 | ;; able to return the date correctly. They say "don't know". |
| 3749 | (unless (tramp-compat-time-equal-p | 3750 | (unless (tramp-compat-time-equal-p |