From 66bc47d12aba72ff738a9f5575e0b93eefc641ba Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Sun, 8 Mar 2020 17:00:10 +0200 Subject: [PATCH] Fix the MinGW build as followup to recent "nofollow" changes * src/w32.c (fdutimens): Call utimensat instead of utime. (set_file_times): Function deleted. (convert_from_timespec): Renamed from convert_from_time_t and modified to accept 'struct timespec' argument instead of 'time_t'. (utimensat): Renamed from utime and modified to accept 'struct timespec [2]' argument and an additional argument FLAG. Emulate Posix 'utimensat'. Call 'convert_from_timespec'. (w32_copy_file): Call 'utimensat' instead of 'set_file_times'. * src/fileio.c (Fcopy_file) [WINDOWSNT]: Make the error message be identical to that on Posix platforms. * nt/inc/sys/stat.h (utimensat): Provide prototype. * nt/mingw-cfg.site (ac_cv_func_futimens) (gl_cv_func_futimens_works, ac_cv_func_utimensat) (gl_cv_func_utimensat_works): Override Gnulib tests. * nt/gnulib-cfg.mk (OMIT_GNULIB_MODULE_futimens) (OMIT_GNULIB_MODULE_utimensat): Disable these Gnulib modules. --- nt/gnulib-cfg.mk | 2 + nt/inc/sys/stat.h | 5 ++ nt/mingw-cfg.site | 4 ++ src/fileio.c | 2 +- src/w32.c | 121 +++++++++++++++++++++++++++++----------------- 5 files changed, 88 insertions(+), 46 deletions(-) diff --git a/nt/gnulib-cfg.mk b/nt/gnulib-cfg.mk index 1d120a973d1..e3b945720d6 100644 --- a/nt/gnulib-cfg.mk +++ b/nt/gnulib-cfg.mk @@ -65,3 +65,5 @@ OMIT_GNULIB_MODULE_unistd = true OMIT_GNULIB_MODULE_canonicalize-lgpl = true OMIT_GNULIB_MODULE_fchmodat = true OMIT_GNULIB_MODULE_lchmod = true +OMIT_GNULIB_MODULE_futimens = true +OMIT_GNULIB_MODULE_utimensat = true diff --git a/nt/inc/sys/stat.h b/nt/inc/sys/stat.h index 7bf780dbaa2..f58d5ab6573 100644 --- a/nt/inc/sys/stat.h +++ b/nt/inc/sys/stat.h @@ -164,4 +164,9 @@ int __cdecl __MINGW_NOTHROW fstatat (int, char const *, struct stat *, int); int __cdecl __MINGW_NOTHROW chmod (const char*, int); +/* Provide prototypes of library functions that are emulated on w32 + and whose prototypes are usually found in sys/stat.h on POSIX + platforms. */ +extern int utimensat (int, const char *, struct timespec const[2], int); + #endif /* INC_SYS_STAT_H_ */ diff --git a/nt/mingw-cfg.site b/nt/mingw-cfg.site index 5bd5b834634..2271eef98d6 100644 --- a/nt/mingw-cfg.site +++ b/nt/mingw-cfg.site @@ -105,6 +105,10 @@ gl_cv_func_fstatat_zero_flag=yes ac_cv_func_fchmodat=yes gl_cv_func_fchmodat_works="not-needed-so-yes" ac_cv_func_lchmod=yes +ac_cv_func_futimens=not-needed +gl_cv_func_futimens_works="not-needed-so-yes" +ac_cv_func_utimensat=yes +gl_cv_func_utimensat_works=yes # Aliased to _commit in ms-w32.h ac_cv_func_fsync=yes ac_cv_func_fdatasync=yes diff --git a/src/fileio.c b/src/fileio.c index 82fd7989206..ffe79559a3f 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -2077,7 +2077,7 @@ permissions. */) report_file_error ("Copying permissions from", file); case -3: xsignal2 (Qfile_date_error, - build_string ("Resetting file times"), newname); + build_string ("Cannot set file date"), newname); case -4: report_file_error ("Copying permissions to", newname); } diff --git a/src/w32.c b/src/w32.c index 40f286ad6cf..698e10e234e 100644 --- a/src/w32.c +++ b/src/w32.c @@ -3178,33 +3178,9 @@ fdutimens (int fd, char const *file, struct timespec const timespec[2]) return _futime (fd, &_ut); } else - { - struct utimbuf ut; - - ut.actime = timespec[0].tv_sec; - ut.modtime = timespec[1].tv_sec; - /* Call 'utime', which is implemented below, not the MS library - function, which fails on directories. */ - return utime (file, &ut); - } + return utimensat (fd, file, timespec, 0); } -/* Set the access and modification time stamps of FD (a.k.a. FILE) to be - ATIME and MTIME, respectively. - FD must be either negative -- in which case it is ignored -- - or a file descriptor that is open on FILE. - If FD is nonnegative, then FILE can be NULL. */ -static int -set_file_times (int fd, const char *filename, - struct timespec atime, struct timespec mtime) -{ - struct timespec timespec[2]; - timespec[0] = atime; - timespec[1] = mtime; - return fdutimens (fd, filename, timespec); -} - - /* ------------------------------------------------------------------------- */ /* IO support and wrapper functions for the Windows API. */ /* ------------------------------------------------------------------------- */ @@ -4985,7 +4961,7 @@ convert_time (FILETIME ft) } static void -convert_from_time_t (time_t time, FILETIME * pft) +convert_from_timespec (struct timespec time, FILETIME * pft) { ULARGE_INTEGER tmp; @@ -4996,7 +4972,8 @@ convert_from_time_t (time_t time, FILETIME * pft) } /* time in 100ns units since 1-Jan-1601 */ - tmp.QuadPart = (ULONGLONG) time * 10000000L + utc_base; + tmp.QuadPart = + (ULONGLONG) time.tv_sec * 10000000L + time.tv_nsec / 100 + utc_base; pft->dwHighDateTime = tmp.HighPart; pft->dwLowDateTime = tmp.LowPart; } @@ -5663,8 +5640,8 @@ fstatat (int fd, char const *name, struct stat *st, int flags) return stat_worker (name, st, ! (flags & AT_SYMLINK_NOFOLLOW)); } -/* Provide fstat and utime as well as stat for consistent handling of - file timestamps. */ +/* Provide fstat and utimensat as well as stat for consistent handling + of file timestamps. */ int fstat (int desc, struct stat * buf) { @@ -5775,23 +5752,65 @@ fstat (int desc, struct stat * buf) return 0; } -/* A version of 'utime' which handles directories as well as - files. */ +/* Emulate utimensat. */ int -utime (const char *name, struct utimbuf *times) +utimensat (int fd, const char *name, const struct timespec times[2], int flag) { - struct utimbuf deftime; + struct timespec ltimes[2]; HANDLE fh; FILETIME mtime; FILETIME atime; + DWORD flags_and_attrs = FILE_FLAG_BACKUP_SEMANTICS; + + /* Rely on a hack: an open directory is modeled as file descriptor 0. + This is good enough for the current usage in Emacs, but is fragile. + + FIXME: Add proper support for utimensat. + Gnulib does this and can serve as a model. */ + char fullname[MAX_UTF8_PATH]; + + if (fd != AT_FDCWD) + { + char lastc = dir_pathname[strlen (dir_pathname) - 1]; + + if (_snprintf (fullname, sizeof fullname, "%s%s%s", + dir_pathname, IS_DIRECTORY_SEP (lastc) ? "" : "/", name) + < 0) + { + errno = ENAMETOOLONG; + return -1; + } + name = fullname; + } if (times == NULL) { - deftime.modtime = deftime.actime = time (NULL); - times = &deftime; + memset (ltimes, 0, sizeof (ltimes)); + ltimes[0] = ltimes[1] = current_timespec (); + } + else + { + if (times[0].tv_nsec == UTIME_OMIT && times[1].tv_nsec == UTIME_OMIT) + return 0; /* nothing to do */ + if ((times[0].tv_nsec != UTIME_NOW && times[0].tv_nsec != UTIME_OMIT + && !(0 <= times[0].tv_nsec && times[0].tv_nsec < 1000000000)) + || (times[1].tv_nsec != UTIME_NOW && times[1].tv_nsec != UTIME_OMIT + && !(0 <= times[1].tv_nsec && times[1].tv_nsec < 1000000000))) + { + errno = EINVAL; /* reject invalid timespec values */ + return -1; + } + + memcpy (ltimes, times, sizeof (ltimes)); + if (ltimes[0].tv_nsec == UTIME_NOW) + ltimes[0] = current_timespec (); + if (ltimes[1].tv_nsec == UTIME_NOW) + ltimes[1] = current_timespec (); } + if (flag == AT_SYMLINK_NOFOLLOW) + flags_and_attrs |= FILE_FLAG_OPEN_REPARSE_POINT; if (w32_unicode_filenames) { wchar_t name_utf16[MAX_PATH]; @@ -5805,7 +5824,7 @@ utime (const char *name, struct utimbuf *times) allows other processes to delete files inside it, while we have the directory open. */ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + 0, OPEN_EXISTING, flags_and_attrs, NULL); } else { @@ -5816,13 +5835,26 @@ utime (const char *name, struct utimbuf *times) fh = CreateFileA (name_ansi, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + 0, OPEN_EXISTING, flags_and_attrs, NULL); } if (fh != INVALID_HANDLE_VALUE) { - convert_from_time_t (times->actime, &atime); - convert_from_time_t (times->modtime, &mtime); - if (!SetFileTime (fh, NULL, &atime, &mtime)) + FILETIME *patime, *pmtime; + if (ltimes[0].tv_nsec == UTIME_OMIT) + patime = NULL; + else + { + convert_from_timespec (ltimes[0], &atime); + patime = &atime; + } + if (ltimes[1].tv_nsec == UTIME_OMIT) + pmtime = NULL; + else + { + convert_from_timespec (ltimes[1], &mtime); + pmtime = &mtime; + } + if (!SetFileTime (fh, NULL, patime, pmtime)) { CloseHandle (fh); errno = EACCES; @@ -6741,16 +6773,16 @@ w32_copy_file (const char *from, const char *to, FIXME? */ else if (!keep_time) { - struct timespec now; + struct timespec tnow[2]; DWORD attributes; + tnow[0] = tnow[1] = current_timespec (); if (w32_unicode_filenames) { /* Ensure file is writable while its times are set. */ attributes = GetFileAttributesW (to_w); SetFileAttributesW (to_w, attributes & ~FILE_ATTRIBUTE_READONLY); - now = current_timespec (); - if (set_file_times (-1, to, now, now)) + if (utimensat (AT_FDCWD, to, tnow, 0)) { /* Restore original attributes. */ SetFileAttributesW (to_w, attributes); @@ -6765,8 +6797,7 @@ w32_copy_file (const char *from, const char *to, { attributes = GetFileAttributesA (to_a); SetFileAttributesA (to_a, attributes & ~FILE_ATTRIBUTE_READONLY); - now = current_timespec (); - if (set_file_times (-1, to, now, now)) + if (utimensat (AT_FDCWD, to, tnow, 0)) { SetFileAttributesA (to_a, attributes); if (acl) -- 2.39.2