Discussion:
windows-stat-timespec: new module
Bruno Haible
2017-05-13 00:52:32 UTC
Permalink
The windows-stat-timespec module turns the timestamps in 'struct stat'
to 'struct timespec', on native Windows.


2017-05-13 Bruno Haible <***@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
Bruno Haible
2017-05-14 15:41:40 UTC
Permalink
One more adaptation of module 'stat-time' for the 'struct stat'
with 'struct timespec' fields on Windows:


2017-05-14 Bruno Haible <***@clisp.org>

stat-time: Adapt for windows-stat-timespec.
* lib/stat-time.h (get_stat_birthtime) [_GL_WINDOWS_STAT_TIMESPEC]: Use
entire st_ctim field.

diff --git a/lib/stat-time.h b/lib/stat-time.h
index 88dcc7f..9e45e85 100644
--- a/lib/stat-time.h
+++ b/lib/stat-time.h
@@ -170,8 +170,12 @@ get_stat_birthtime (struct stat const *st)
/* Native Windows platforms (but not Cygwin) put the "file creation
time" in st_ctime (!). See
<https://msdn.microsoft.com/en-us/library/14h5k7ff(VS.80).aspx>. */
+# if _GL_WINDOWS_STAT_TIMESPEC
+ t = st->st_ctim;
+# else
t.tv_sec = st->st_ctime;
t.tv_nsec = 0;
+# endif
#else
/* Birth time is not supported. */
t.tv_sec = -1;
Bruno Haible
2017-05-14 15:48:55 UTC
Permalink
On native Windows, the 'test-stat-time' test works fine the first time I run it,
but fails in subsequent runs in the same build directory.

Strangely enough, when you create a file, delete it, create it again (with the
same name), then the st_ctime field will contain the time of the first creation.
(See log appended below.) Reproduced with Windows 10 on NTFS.

As a workaround/fix: Let the stat-time test create files with different names
each time.


2017-05-14 Bruno Haible <***@clisp.org>

stat-time tests: Workaround for native Windows.
* tests/test-stat-time.c: Include <stdio.h>, <time.h>.
(filename_stamp1, filename_testfile, filename_stamp2, filename_stamp3):
New variables.
(initialize_filenames): New function.
(main): Invoke it.
(cleanup, prepare_test): Update.

diff --git a/tests/test-stat-time.c b/tests/test-stat-time.c
index 7b9cebf..25fe6c3 100644
--- a/tests/test-stat-time.c
+++ b/tests/test-stat-time.c
@@ -22,8 +22,10 @@

#include <fcntl.h>
#include <signal.h>
+#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
+#include <time.h>

#include "macros.h"

@@ -32,6 +34,25 @@

enum { NFILES = 4 };

+static char filename_stamp1[50];
+static char filename_testfile[50];
+static char filename_stamp2[50];
+static char filename_stamp3[50];
+
+/* Use file names that are different at each run.
+ This is necessary for test_birthtime() to pass on native Windows:
+ On this platform, the file system apparently remembers the creation time
+ of a file even after it is removed and created anew. */
+static void
+initialize_filenames (void)
+{
+ long t = (long) time (NULL);
+ sprintf (filename_stamp1, "t-stt-%ld-stamp1", t);
+ sprintf (filename_testfile, "t-stt-%ld-testfile", t);
+ sprintf (filename_stamp2, "t-stt-%ld-stamp2", t);
+ sprintf (filename_stamp3, "t-stt-%ld-stamp3", t);
+}
+
static int
force_unlink (const char *filename)
{
@@ -45,11 +66,10 @@ static void
cleanup (int sig)
{
/* Remove temporary files. */
- force_unlink ("t-stt-stamp1");
- force_unlink ("t-stt-testfile");
- force_unlink ("t-stt-stamp2");
- force_unlink ("t-stt-renamed");
- force_unlink ("t-stt-stamp3");
+ force_unlink (filename_stamp1);
+ force_unlink (filename_testfile);
+ force_unlink (filename_stamp2);
+ force_unlink (filename_stamp3);

if (sig != 0)
_exit (1);
@@ -87,20 +107,20 @@ prepare_test (struct stat *statinfo, struct timespec *modtimes)
{
int i;

- create_file ("t-stt-stamp1");
+ create_file (filename_stamp1);
nap ();
- create_file ("t-stt-testfile");
+ create_file (filename_testfile);
nap ();
- create_file ("t-stt-stamp2");
+ create_file (filename_stamp2);
nap ();
- ASSERT (chmod ("t-stt-testfile", 0400) == 0);
+ ASSERT (chmod (filename_testfile, 0400) == 0);
nap ();
- create_file ("t-stt-stamp3");
+ create_file (filename_stamp3);

- do_stat ("t-stt-stamp1", &statinfo[0]);
- do_stat ("t-stt-testfile", &statinfo[1]);
- do_stat ("t-stt-stamp2", &statinfo[2]);
- do_stat ("t-stt-stamp3", &statinfo[3]);
+ do_stat (filename_stamp1, &statinfo[0]);
+ do_stat (filename_testfile, &statinfo[1]);
+ do_stat (filename_stamp2, &statinfo[2]);
+ do_stat (filename_stamp3, &statinfo[3]);

/* Now use our access functions. */
for (i = 0; i < NFILES; ++i)
@@ -160,7 +180,7 @@ test_ctime (const struct stat *statinfo)
if (statinfo[0].st_mtime != statinfo[0].st_ctime)
return;

- /* mtime(stamp2) < ctime(renamed) */
+ /* mtime(stamp2) < ctime(testfile) */
ASSERT (statinfo[2].st_mtime < statinfo[1].st_ctime
|| (statinfo[2].st_mtime == statinfo[1].st_ctime
&& (get_stat_mtime_ns (&statinfo[2])
@@ -183,11 +203,11 @@ test_birthtime (const struct stat *statinfo,
return;
}

- /* mtime(stamp1) < birthtime(renamed) */
+ /* mtime(stamp1) < birthtime(testfile) */
ASSERT (modtimes[0].tv_sec < birthtimes[1].tv_sec
|| (modtimes[0].tv_sec == birthtimes[1].tv_sec
&& modtimes[0].tv_nsec < birthtimes[1].tv_nsec));
- /* birthtime(renamed) < mtime(stamp2) */
+ /* birthtime(testfile) < mtime(stamp2) */
ASSERT (birthtimes[1].tv_sec < modtimes[2].tv_sec
|| (birthtimes[1].tv_sec == modtimes[2].tv_sec
&& birthtimes[1].tv_nsec < modtimes[2].tv_nsec));
@@ -200,6 +220,8 @@ main (void)
struct timespec modtimes[NFILES];
struct timespec birthtimes[NFILES];

+ initialize_filenames ();
+
#ifdef SIGHUP
signal (SIGHUP, cleanup);
#endif


=================================== EXPERIMENT ==============================

$ gltests/test-stat-time.exe
i=0 modtime=1494769524.508897000 birthtime=1494769524.508897000
i=1 modtime=1494769524.509899300 birthtime=1494769524.509899300
i=2 modtime=1494769524.517378100 birthtime=1494769524.517378100
i=3 modtime=1494769524.535423400 birthtime=1494769524.535423400
File t-stt-stamp1: ret=0, st_ctim = 1494769524.508897000
File t-stt-testfile: ret=0, st_ctim = 1494769524.509899300
File t-stt-stamp2: ret=0, st_ctim = 1494769524.517378100
File t-stt-stamp3: ret=0, st_ctim = 1494769524.535423400

$ echo $?
0

$ gltests/test-stat-time.exe
File t-stt-stamp1: ret=0, st_ctim = 1494769524.508897000
File t-stt-testfile: ret=0, st_ctim = 1494769524.509899300
File t-stt-stamp2: ret=0, st_ctim = 1494769524.517378100
File t-stt-stamp3: ret=0, st_ctim = 1494769524.535423400
../../gltests/test-stat-time.c:193: assertion 'modtimes[0].tv_sec < birthtimes[1].tv_sec || (modtimes[0].tv_sec == birthtimes[1].tv_sec && modtimes[0].tv_nsec < birthtimes[1].tv_nsec)' failed

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
i=0 modtime=1494769535.271086600 birthtime=1494769524.508897000
i=1 modtime=1494769535.272055700 birthtime=1494769524.509899300
i=2 modtime=1494769535.275064100 birthtime=1494769524.517378100
i=3 modtime=1494769535.283084700 birthtime=1494769524.535423400

$ rm -f t-stt-*

$ gltests/test-stat-time.exe
File t-stt-stamp1: ret=0, st_ctim = 1494769524.508897000
File t-stt-testfile: ret=0, st_ctim = 1494769524.509899300
File t-stt-stamp2: ret=0, st_ctim = 1494769524.517378100
File t-stt-stamp3: ret=0, st_ctim = 1494769524.535423400
../../gltests/test-stat-time.c:193: assertion 'modtimes[0].tv_sec < birthtimes[1].tv_sec || (modtimes[0].tv_sec == birthtimes[1].tv_sec && modtimes[0].tv_nsec < birthtimes[1].tv_nsec)' failed

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
i=0 modtime=1494769595.727543200 birthtime=1494769524.508897000
i=1 modtime=1494769595.728546200 birthtime=1494769524.509899300
i=2 modtime=1494769595.731553900 birthtime=1494769524.517378100
i=3 modtime=1494769595.738220100 birthtime=1494769524.535423400
Paul Eggert
2017-05-14 19:12:06 UTC
Permalink
Post by Bruno Haible
Strangely enough, when you create a file, delete it, create it again (with the
same name), then the st_ctime field will contain the time of the first creation.
This misfeature is by design. It's called "file system tunneling". See, for example:

http://dfstream.blogspot.com/2012/02/file-system-tunneling-in-windows.html
Bruno Haible
2017-05-14 19:36:38 UTC
Permalink
Post by Paul Eggert
http://dfstream.blogspot.com/2012/02/file-system-tunneling-in-windows.html
Thanks. It's worth extending the comment:


2017-05-14 Bruno Haible <***@clisp.org>

stat-time tests: Improve comment.
* tests/test-stat-time.c: Add hyperlink, from Paul Eggert.

diff --git a/tests/test-stat-time.c b/tests/test-stat-time.c
index 25fe6c3..47849de 100644
--- a/tests/test-stat-time.c
+++ b/tests/test-stat-time.c
@@ -42,7 +42,9 @@ static char filename_stamp3[50];
/* Use file names that are different at each run.
This is necessary for test_birthtime() to pass on native Windows:
On this platform, the file system apparently remembers the creation time
- of a file even after it is removed and created anew. */
+ of a file even after it is removed and created anew. See
+ "Windows NT Contains File System Tunneling Capabilities"
+ <https://support.microsoft.com/en-us/help/172190/> */
static void
initialize_filenames (void)
{

Loading...