The windows-stat-timespec module turns the timestamps in 'struct stat' to 'struct timespec', on native Windows.
2017-05-13 Bruno Haible <br...@clisp.org> windows-stat-timespec: New module. * modules/windows-stat-timespec: New file. * m4/windows-stat-timespec.m4: New file. * m4/sys_stat_h.m4 (gl_HEADER_SYS_STAT_H): Set WINDOWS_STAT_TIMESPEC. * modules/sys_stat (Makefile.am): Substitute WINDOWS_STAT_TIMESPEC. * lib/sys_stat.in.h (struct stat) [WINDOWS_STAT_TIMESPEC]: Declare with fields st_atim, st_mtim, st_ctim. (st_atime, st_mtime, st_ctime): Define as macros. (_GL_WINDOWS_STAT_TIMESPEC): New macro. * lib/stat-w32.h (_gl_convert_FILETIME_to_timespec) [_GL_WINDOWS_STAT_TIMESPEC]: New declaration. * lib/stat-w32.c (_gl_convert_FILETIME_to_timespec) [_GL_WINDOWS_STAT_TIMESPEC]: New function. (_gl_convert_FILETIME_to_POSIX): Adjust coding style. (_gl_fstat_by_handle): If _GL_WINDOWS_STAT_TIMESPEC, convert the FILETIME to 'struct timespec', not 'time_t'. * lib/stat.c (rpl_stat): If _GL_WINDOWS_STAT_TIMESPEC, convert the FILETIME to 'struct timespec', not 'time_t'. * lib/stat-time.h (STAT_TIMESPEC): Define also if _GL_WINDOWS_STAT_TIMESPEC. * doc/windows-stat-timespec.texi: New file. * doc/gnulib.texi: Include it. diff --git a/doc/gnulib.texi b/doc/gnulib.texi index d23ab9c..094a98c 100644 --- a/doc/gnulib.texi +++ b/doc/gnulib.texi @@ -6323,12 +6323,15 @@ to POSIX that it can be treated like any other Unix-like platform. @menu * Libtool and Windows:: +* Precise file timestamps on Windows:: * Windows sockets:: * Native Windows Support without MSVC Support:: @end menu @include windows-libtool.texi +@include windows-stat-timespec.texi + @include windows-sockets.texi @include windows-without-msvc.texi diff --git a/doc/windows-stat-timespec.texi b/doc/windows-stat-timespec.texi new file mode 100644 index 0000000..8aebafc --- /dev/null +++ b/doc/windows-stat-timespec.texi @@ -0,0 +1,13 @@ +@node Precise file timestamps on Windows +@section Precise file timestamps on Windows + +The module @samp{windows-stat-timespec} ensures that, +on native Windows platforms, @code{struct stat} contains +@code{st_atim}, @code{st_mtim}, @code{st_ctim} fields of type +@code{struct timespec}, providing 100 ns resolution for the timestamps +of files. + +Note: On some types of file systems, the timestamp resolution is limited +by the file system. For example, on FAT file systems, @code{st_mtim} +only has a resolution of 2 seconds. For more details, see +@url{https://msdn.microsoft.com/en-us/library/ms724290.aspx}. diff --git a/lib/stat-time.h b/lib/stat-time.h index 154d62a..88dcc7f 100644 --- a/lib/stat-time.h +++ b/lib/stat-time.h @@ -43,8 +43,8 @@ extern "C" { time respectively. These macros are private to stat-time.h. */ -#if defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC -# ifdef TYPEOF_STRUCT_STAT_ST_ATIM_IS_STRUCT_TIMESPEC +#if _GL_WINDOWS_STAT_TIMESPEC || defined HAVE_STRUCT_STAT_ST_ATIM_TV_NSEC +# if _GL_WINDOWS_STAT_TIMESPEC || defined TYPEOF_STRUCT_STAT_ST_ATIM_IS_STRUCT_TIMESPEC # define STAT_TIMESPEC(st, st_xtim) ((st)->st_xtim) # else # define STAT_TIMESPEC_NS(st, st_xtim) ((st)->st_xtim.tv_nsec) diff --git a/lib/stat-w32.c b/lib/stat-w32.c index 4818a57..515311d 100644 --- a/lib/stat-w32.c +++ b/lib/stat-w32.c @@ -53,6 +53,32 @@ initialize (void) } /* Converts a FILETIME to GMT time since 1970-01-01 00:00:00. */ +#if _GL_WINDOWS_STAT_TIMESPEC +struct timespec +_gl_convert_FILETIME_to_timespec (const FILETIME *ft) +{ + struct timespec result; + /* FILETIME: <https://msdn.microsoft.com/en-us/library/ms724284.aspx> */ + unsigned long long since_1601 = + ((unsigned long long) ft->dwHighDateTime << 32) + | (unsigned long long) ft->dwLowDateTime; + if (since_1601 == 0) + { + result.tv_sec = 0; + result.tv_nsec = 0; + } + else + { + /* Between 1601-01-01 and 1970-01-01 there were 280 normal years and 89 + leap years, in total 134774 days. */ + unsigned long long since_1970 = + since_1601 - (unsigned long long) 134774 * (unsigned long long) 86400 * (unsigned long long) 10000000; + result.tv_sec = since_1970 / (unsigned long long) 10000000; + result.tv_nsec = (unsigned long) (since_1970 % (unsigned long long) 10000000) * 100; + } + return result; +} +#else time_t _gl_convert_FILETIME_to_POSIX (const FILETIME *ft) { @@ -62,12 +88,16 @@ _gl_convert_FILETIME_to_POSIX (const FILETIME *ft) | (unsigned long long) ft->dwLowDateTime; if (since_1601 == 0) return 0; - /* Between 1601-01-01 and 1970-01-01 there were 280 normal years and 89 leap - years, in total 134774 days. */ - unsigned long long since_1970 = - since_1601 - (unsigned long long) 134774 * (unsigned long long) 86400 * (unsigned long long) 10000000; - return since_1970 / (unsigned long long) 10000000; + else + { + /* Between 1601-01-01 and 1970-01-01 there were 280 normal years and 89 + leap years, in total 134774 days. */ + unsigned long long since_1970 = + since_1601 - (unsigned long long) 134774 * (unsigned long long) 86400 * (unsigned long long) 10000000; + return since_1970 / (unsigned long long) 10000000; + } } +#endif /* Fill *BUF with information about the file designated by H. PATH is the file name, if known, otherwise NULL. @@ -218,9 +248,15 @@ _gl_fstat_by_handle (HANDLE h, const char *path, struct stat *buf) <https://msdn.microsoft.com/en-us/library/aa364953.aspx> <https://msdn.microsoft.com/en-us/library/aa364217.aspx> The latter requires -D_WIN32_WINNT=_WIN32_WINNT_VISTA or higher. */ +#if _GL_WINDOWS_STAT_TIMESPEC + buf->st_atim = _gl_convert_FILETIME_to_timespec (&info.ftLastAccessTime); + buf->st_mtim = _gl_convert_FILETIME_to_timespec (&info.ftLastWriteTime); + buf->st_ctim = _gl_convert_FILETIME_to_timespec (&info.ftCreationTime); +#else buf->st_atime = _gl_convert_FILETIME_to_POSIX (&info.ftLastAccessTime); buf->st_mtime = _gl_convert_FILETIME_to_POSIX (&info.ftLastWriteTime); buf->st_ctime = _gl_convert_FILETIME_to_POSIX (&info.ftCreationTime); +#endif return 0; } @@ -245,9 +281,15 @@ _gl_fstat_by_handle (HANDLE h, const char *path, struct stat *buf) } else buf->st_size = 0; +#if _GL_WINDOWS_STAT_TIMESPEC + buf->st_atim.tv_sec = 0; buf->st_atim.tv_nsec = 0; + buf->st_mtim.tv_sec = 0; buf->st_mtim.tv_nsec = 0; + buf->st_ctim.tv_sec = 0; buf->st_ctim.tv_nsec = 0; +#else buf->st_atime = 0; buf->st_mtime = 0; buf->st_ctime = 0; +#endif return 0; } else diff --git a/lib/stat-w32.h b/lib/stat-w32.h index 68cac29..5426843 100644 --- a/lib/stat-w32.h +++ b/lib/stat-w32.h @@ -18,7 +18,11 @@ #define _STAT_W32_H 1 /* Converts a FILETIME to GMT time since 1970-01-01 00:00:00. */ +#if _GL_WINDOWS_STAT_TIMESPEC +extern struct timespec _gl_convert_FILETIME_to_timespec (const FILETIME *ft); +#else extern time_t _gl_convert_FILETIME_to_POSIX (const FILETIME *ft); +#endif /* Fill *BUF with information about the file designated by H. PATH is the file name, if known, otherwise NULL. diff --git a/lib/stat.c b/lib/stat.c index 6e4d788..199e216 100644 --- a/lib/stat.c +++ b/lib/stat.c @@ -264,9 +264,15 @@ rpl_stat (char const *name, struct stat *buf) buf->st_size = ((long long) info.nFileSizeHigh << 32) | (long long) info.nFileSizeLow; /* st_atime, st_mtime, st_ctime. */ +# if _GL_WINDOWS_STAT_TIMESPEC + buf->st_atim = _gl_convert_FILETIME_to_timespec (&info.ftLastAccessTime); + buf->st_mtim = _gl_convert_FILETIME_to_timespec (&info.ftLastWriteTime); + buf->st_ctim = _gl_convert_FILETIME_to_timespec (&info.ftCreationTime); +# else buf->st_atime = _gl_convert_FILETIME_to_POSIX (&info.ftLastAccessTime); buf->st_mtime = _gl_convert_FILETIME_to_POSIX (&info.ftLastWriteTime); buf->st_ctime = _gl_convert_FILETIME_to_POSIX (&info.ftCreationTime); +# endif FindClose (h); diff --git a/lib/sys_stat.in.h b/lib/sys_stat.in.h index ef689b9..24d0806 100644 --- a/lib/sys_stat.in.h +++ b/lib/sys_stat.in.h @@ -108,7 +108,7 @@ struct stat blkcnt_t st_blocks; # endif -# if 0 +# if @WINDOWS_STAT_TIMESPEC@ struct timespec st_atim; struct timespec st_mtim; struct timespec st_ctim; @@ -118,10 +118,12 @@ struct stat time_t st_ctime; # endif }; -# if 0 +# if @WINDOWS_STAT_TIMESPEC@ # define st_atime st_atim.tv_sec # define st_mtime st_mtim.tv_sec # define st_ctime st_ctim.tv_sec + /* Indicator, for gnulib internal purposes. */ +# define _GL_WINDOWS_STAT_TIMESPEC 1 # endif # define GNULIB_defined_struct_stat 1 # endif diff --git a/m4/sys_stat_h.m4 b/m4/sys_stat_h.m4 index f0f443e..155667d 100644 --- a/m4/sys_stat_h.m4 +++ b/m4/sys_stat_h.m4 @@ -1,4 +1,4 @@ -# sys_stat_h.m4 serial 29 -*- Autoconf -*- +# sys_stat_h.m4 serial 30 -*- Autoconf -*- dnl Copyright (C) 2006-2017 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -19,6 +19,14 @@ AC_DEFUN([gl_HEADER_SYS_STAT_H], dnl Ensure the type mode_t gets defined. AC_REQUIRE([AC_TYPE_MODE_T]) + dnl Whether to enable precise timestamps in 'struct stat'. + m4_ifdef([gl_WINDOWS_STAT_TIMESPEC], [ + AC_REQUIRE([gl_WINDOWS_STAT_TIMESPEC]) + ], [ + WINDOWS_STAT_TIMESPEC=0 + ]) + AC_SUBST([WINDOWS_STAT_TIMESPEC]) + dnl Whether to override 'struct stat'. m4_ifdef([gl_LARGEFILE], [ AC_REQUIRE([gl_LARGEFILE]) diff --git a/m4/windows-stat-timespec.m4 b/m4/windows-stat-timespec.m4 new file mode 100644 index 0000000..cc758d2 --- /dev/null +++ b/m4/windows-stat-timespec.m4 @@ -0,0 +1,16 @@ +# windows-stat-timespec.m4 serial 1 +dnl Copyright (C) 2017 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +dnl Enable precise timestamps in 'struct stat' on native Windows platforms. + +AC_DEFUN([gl_WINDOWS_STAT_TIMESPEC], +[ + AC_REQUIRE([AC_CANONICAL_HOST]) + case "$host_os" in + mingw*) WINDOWS_STAT_TIMESPEC=1 ;; + *) WINDOWS_STAT_TIMESPEC=0 ;; + esac +]) diff --git a/modules/sys_stat b/modules/sys_stat index 2e0aa75..867cc85 100644 --- a/modules/sys_stat +++ b/modules/sys_stat @@ -33,6 +33,7 @@ sys/stat.h: sys_stat.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(ARG_NONNU -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ -e 's|@''NEXT_SYS_STAT_H''@|$(NEXT_SYS_STAT_H)|g' \ -e 's|@''WINDOWS_64_BIT_ST_SIZE''@|$(WINDOWS_64_BIT_ST_SIZE)|g' \ + -e 's|@''WINDOWS_STAT_TIMESPEC''@|$(WINDOWS_STAT_TIMESPEC)|g' \ -e 's/@''GNULIB_FCHMODAT''@/$(GNULIB_FCHMODAT)/g' \ -e 's/@''GNULIB_FSTAT''@/$(GNULIB_FSTAT)/g' \ -e 's/@''GNULIB_FSTATAT''@/$(GNULIB_FSTATAT)/g' \ diff --git a/modules/windows-stat-timespec b/modules/windows-stat-timespec new file mode 100644 index 0000000..d7d44fb --- /dev/null +++ b/modules/windows-stat-timespec @@ -0,0 +1,28 @@ +Description: +On native Windows platforms, ensure that 'struct stat' contains +st_atim, st_mtim, st_ctim fields of type 'struct timespec', providing +100 ns resolution for the timestamps of files. + +Comment: +This module should not be used as a dependency from a test module, +otherwise when this module occurs as a tests-related module, it will +have side effects on the compilation of the main modules in lib/. + +Files: +m4/windows-stat-timespec.m4 + +Depends-on: +windows-stat-override + +configure.ac: +AC_REQUIRE([gl_WINDOWS_STAT_TIMESPEC]) + +Makefile.am: + +Include: + +License: +LGPLv2+ + +Maintainer: +Bruno Haible