enum { NFILES = 4 };
-static void
+static int
force_unlink (const char *filename)
{
/* This chmod is necessary on mingw, where unlink() of a read-only file
fails with EPERM. */
chmod (filename, 0600);
- unlink (filename);
+ return unlink (filename);
}
static void
static long delay;
if (!delay)
{
- /* Initialize only once, by sleeping for 1 millisecond. If that
- was enough to observe a difference, then we are set;
- otherwise fall back to 2 seconds. */
+ /* Initialize only once, by sleeping for 20 milliseconds (needed
+ since xfs has a quantization of about 10 milliseconds, even
+ though it has a granularity of 1 nanosecond, and since NTFS
+ has a default quantization of 15.25 milliseconds, even though
+ it has a granularity of 100 nanoseconds). If the seconds
+ differ, repeat the test one more time (in case we crossed a
+ quantization boundary on a file system with 1 second
+ resolution). If we can't observe a difference in only the
+ nanoseconds, then fall back to 2 seconds. However, note that
+ usleep (2000000) is allowed to fail with EINVAL. */
struct stat st1;
struct stat st2;
ASSERT (stat ("t-stt-stamp1", &st1) == 0);
- ASSERT (unlink ("t-stt-stamp1") == 0);
- usleep (delay = 1000);
+ ASSERT (force_unlink ("t-stt-stamp1") == 0);
+ delay = 20000;
+ usleep (delay);
create_file ("t-stt-stamp1");
ASSERT (stat ("t-stt-stamp1", &st2) == 0);
- if (! (st1.st_mtime < st2.st_mtime
- || (st1.st_mtime == st2.st_mtime
- && get_stat_mtime_ns (&st1) < get_stat_mtime_ns (&st2))))
+ if (st1.st_mtime != st2.st_mtime)
+ {
+ /* Seconds differ, give it one more shot. */
+ st1 = st2;
+ ASSERT (force_unlink ("t-stt-stamp1") == 0);
+ usleep (delay);
+ create_file ("t-stt-stamp1");
+ ASSERT (stat ("t-stt-stamp1", &st2) == 0);
+ }
+ if (! (st1.st_mtime == st2.st_mtime
+ && get_stat_mtime_ns (&st1) < get_stat_mtime_ns (&st2)))
delay = 2000000;
}
- usleep (delay);
+ if (delay == 2000000)
+ sleep (2);
+ else
+ usleep (delay);
#endif /* HAVE_USLEEP */
}
}
}
-#if !((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
+#if (defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__
+/* Skip the ctime tests on native Windows platforms, because their
+ st_ctime is either the same as st_mtime (plus or minus an offset)
+ or set to the file _creation_ time, and is not influenced by rename
+ or chmod. */
+# define test_ctime(ignored) ((void) 0)
+#else
static void
test_ctime (const struct stat *statinfo)
{
+ /* On some buggy NFS clients, mtime and ctime are disproportionately
+ skewed from one another. Skip this test in that case. */
+ if (statinfo[0].st_mtime != statinfo[0].st_ctime)
+ return;
+
/* mtime(stamp2) < ctime(renamed) */
ASSERT (statinfo[2].st_mtime < statinfo[1].st_ctime
|| (statinfo[2].st_mtime == statinfo[1].st_ctime
{
int i;
- /* Collect the birth times.. */
+ /* Collect the birth times. */
for (i = 0; i < NFILES; ++i)
{
birthtimes[i] = get_stat_birthtime (&statinfo[i]);
cleanup (0);
prepare_test (statinfo, modtimes);
test_mtime (statinfo, modtimes);
- /* Skip the ctime tests on native Windows platforms, because there st_ctime
- is either the same as st_mtime (plus or minus an offset) or set to the
- file _creation_ time, and is not influenced by rename or chmod. */
-#if !((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__)
test_ctime (statinfo);
-#endif
test_birthtime (statinfo, modtimes, birthtimes);
cleanup (0);