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. */
/* ------------------------------------------------------------------------- */
}
static void
-convert_from_time_t (time_t time, FILETIME * pft)
+convert_from_timespec (struct timespec time, FILETIME * pft)
{
ULARGE_INTEGER tmp;
}
/* 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;
}
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)
{
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];
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
{
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;
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);
{
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)