summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--nt/gnulib-cfg.mk2
-rw-r--r--nt/inc/sys/stat.h5
-rw-r--r--nt/mingw-cfg.site4
-rw-r--r--src/fileio.c2
-rw-r--r--src/w32.c121
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)