]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Add new assertions to verify atime, birthtime, and mtime, both
authorTim Kientzle <kientzle@gmail.com>
Sun, 6 Sep 2009 06:44:40 +0000 (02:44 -0400)
committerTim Kientzle <kientzle@gmail.com>
Sun, 6 Sep 2009 06:44:40 +0000 (02:44 -0400)
for exact values and "recent".  Sketch in support for FreeBSD
(including high-res), Windows (including high-res and birthtime),
and generic POSIX.

Inspired in part by the discovery that stat() on Windows shifts mtime
values depending on DST settings, so the old code that used stat()
breaks in weird ways on Windows.  GetFileTime() seems to not have this
problem, but using that requires that we not use stat().

SVN-Revision: 1431

libarchive/test/main.c
libarchive/test/test.h
libarchive/test/test_write_disk.c
libarchive/test/test_write_disk_times.c

index bcfbb2d6e517947239cc010122cf1c2677b9f1a9..beb6cadfc74052aea4d0bf0b0201f783e6b226a4 100644 (file)
@@ -809,6 +809,145 @@ assertion_file_hardlinks(const char *file, int line,
        return (1);
 }
 
+/* Verify a/b/mtime of 'pathname'. */
+/* If 'recent', verify that it's within last 10 seconds. */
+static int
+assertion_file_time(const char *file, int line,
+    const char *pathname, long t, long nsec, char type, int recent)
+{
+       long filet, filet_nsec;
+       int r;
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+       FILETIME ftime, fbirthtime, fatime, fmtime;
+       ULARGE_INTEGER wintm;
+       HANDLE h;
+
+       assertion_count(file, line);
+       h = CreateFile(pathname, FILE_READ_ATTRIBUTES, 0, NULL,
+           OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+       if (h == INVALID_HANDLE_VALUE) {
+               failure_start(file, line, "Can't access %s\n", pathname);
+               failure_finish(NULL);
+               return (0);
+       }
+       r = GetFileTime(h, &fbirthtime, &fatime, &fmtime);
+       switch (type) {
+       case 'a': ftime = fatime; break;
+       case 'm': ftime = fmtime; break;
+       case 'b': ftime = fbirthtime; break;
+       }
+       CloseHandle(h);
+       if (r == 0) {
+               failure_start(file, line, "Can't GetFileTime %s\n", pathname);
+               failure_finish(NULL);
+               return (0);
+       }
+       wintm.LowPart = fmtime.dwLowDateTime;
+       wintm.HighPart = fmtime.dwHighDateTime;
+       filet = (wintm.QuadPart - EPOC_TIME) / 10000000;
+       filet_nsec = ((wintm.QuadPart - EPOC_TIME) % 10000000) * 100;
+       nsec = (nsec / 100) * 100; /* Round the request */
+#else
+       struct stat st;
+
+       assertion_count(file, line);
+       r = lstat(pathname, &st);
+       if (r != 0) {
+               failure_start(file, line, "Can't stat %s\n", pathname);
+               failure_finish(NULL);
+               return (0);
+       }
+       switch (type) {
+       case 'a': filet = st.st_atime; break;
+       case 'm': filet = st.st_mtime; break;
+       case 'b': filet = 0; break;
+       default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type);
+               exit(1);
+       }
+#if defined(__FreeBSD__)
+       switch (type) {
+       case 'a': filet_nsec = st.st_atimespec.tv_nsec; break;
+       case 'b': filet = st.st_birthtime;
+               filet_nsec = st.st_birthtimespec.tv_nsec; break;
+       case 'm': filet_nsec = st.st_mtimespec.tv_nsec; break;
+       default: fprintf(stderr, "INTERNAL: Bad type %c for file time", type);
+               exit(1);
+       }
+       /* FreeBSD generally only stores to microsecond res, so round. */
+       filet_nsec = (filet_nsec / 1000) * 1000;
+       nsec = (nsec / 1000) * 1000;
+#else
+       filet_nsec = nsec = 0;  /* Generic POSIX only has whole seconds. */
+       if (type == 'b') return (1); /* Generic POSIX doesn't have birthtime */
+#endif
+#endif
+       if (recent) {
+               /* Check that requested time is up-to-date. */
+               time_t now = time(NULL);
+               if (filet < now - 10 || filet > now + 1) {
+                       failure_start(file, line,
+                           "File %s has %ctime %ld, %ld seconds ago\n",
+                           pathname, type, filet, now - filet);
+                       failure_finish(NULL);
+                       return (0);
+               }
+       } else if (filet != t || filet_nsec != nsec) {
+               failure_start(file, line,
+                   "File %s has %ctime %ld.%09ld, expected %ld.%09ld",
+                   pathname, type, filet, filet_nsec, t, nsec);
+               failure_finish(NULL);
+               return (0);
+       }
+       return (1);
+}
+
+/* Verify atime of 'pathname'. */
+int
+assertion_file_atime(const char *file, int line,
+    const char *pathname, long t, long nsec)
+{
+       return assertion_file_time(file, line, pathname, t, nsec, 'a', 0);
+}
+
+/* Verify atime of 'pathname' is up-to-date. */
+int
+assertion_file_atime_recent(const char *file, int line, const char *pathname)
+{
+       return assertion_file_time(file, line, pathname, 0, 0, 'a', 1);
+}
+
+/* Verify birthtime of 'pathname'. */
+int
+assertion_file_birthtime(const char *file, int line,
+    const char *pathname, long t, long nsec)
+{
+       return assertion_file_time(file, line, pathname, t, nsec, 'b', 0);
+}
+
+/* Verify birthtime of 'pathname' is up-to-date. */
+int
+assertion_file_birthtime_recent(const char *file, int line,
+    const char *pathname)
+{
+       return assertion_file_time(file, line, pathname, 0, 0, 'b', 1);
+}
+
+/* Verify mtime of 'pathname'. */
+int
+assertion_file_mtime(const char *file, int line,
+    const char *pathname, long t, long nsec)
+{
+       return assertion_file_time(file, line, pathname, t, nsec, 'm', 0);
+}
+
+/* Verify mtime of 'pathname' is up-to-date. */
+int
+assertion_file_mtime_recent(const char *file, int line, const char *pathname)
+{
+       return assertion_file_time(file, line, pathname, 0, 0, 'm', 1);
+}
+
 /* Verify number of links to 'pathname'. */
 int
 assertion_file_nlinks(const char *file, int line,
index 3ffd16396452b34e0e655e0f272c99e0d7a66449..d5c06b331091541b9a9a926c97a5117b708955a8 100644 (file)
 /* Assert that a file is not empty; supports printf-style arguments. */
 #define assertNonEmptyFile             \
   assertion_setup(__FILE__, __LINE__);assertion_non_empty_file
+#define assertFileAtime(pathname, sec, nsec)   \
+  assertion_file_atime(__FILE__, __LINE__, pathname, sec, nsec)
+#define assertFileAtimeRecent(pathname)        \
+  assertion_file_atime_recent(__FILE__, __LINE__, pathname)
+#define assertFileBirthtime(pathname, sec, nsec)       \
+  assertion_file_birthtime(__FILE__, __LINE__, pathname, sec, nsec)
+#define assertFileBirthtimeRecent(pathname) \
+  assertion_file_birthtime_recent(__FILE__, __LINE__, pathname)
 /* Assert that a file exists; supports printf-style arguments. */
 #define assertFileExists               \
   assertion_setup(__FILE__, __LINE__);assertion_file_exists
   assertion_setup(__FILE__, __LINE__);assertion_file_contents
 #define assertFileHardlinks(path1, path2)      \
   assertion_file_hardlinks(__FILE__, __LINE__, path1, path2)
+#define assertFileMtime(pathname, sec, nsec)   \
+  assertion_file_mtime(__FILE__, __LINE__, pathname, sec, nsec)
+#define assertFileMtimeRecent(pathname) \
+  assertion_file_mtime_recent(__FILE__, __LINE__, pathname)
 #define assertFileNLinks(pathname, nlinks)  \
   assertion_file_nlinks(__FILE__, __LINE__, pathname, nlinks)
 #define assertFileSize(pathname, size)  \
@@ -202,9 +214,15 @@ int assertion_equal_int(const char *, int, long long, const char *, long long, c
 int assertion_equal_mem(const char *, int, const void *, const char *, const void *, const char *, size_t, const char *, void *);
 int assertion_equal_string(const char *, int, const char *v1, const char *, const char *v2, const char *, void *);
 int assertion_equal_wstring(const char *, int, const wchar_t *v1, const char *, const wchar_t *v2, const char *, void *);
+int assertion_file_atime(const char *, int, const char *, long, long);
+int assertion_file_atime_recent(const char *, int, const char *);
+int assertion_file_birthtime(const char *, int, const char *, long, long);
+int assertion_file_birthtime_recent(const char *, int, const char *);
 int assertion_file_contents(const void *, int, const char *, ...);
 int assertion_file_exists(const char *, ...);
 int assertion_file_hardlinks(const char *, int, const char *, const char *);
+int assertion_file_mtime(const char *, int, const char *, long, long);
+int assertion_file_mtime_recent(const char *, int, const char *);
 int assertion_file_nlinks(const char *, int, const char *, int);
 int assertion_file_not_exists(const char *, ...);
 int assertion_file_size(const char *, int, const char *, long);
index bf77d5314a9af894bfa3aef933b3a80f80d4637e..adcc215c88762b3b27d15e629ec60fc507fece8d 100644 (file)
@@ -61,8 +61,6 @@ static void create_reg_file(struct archive_entry *ae, const char *msg)
 {
        static const char data[]="abcdefghijklmnopqrstuvwxyz";
        struct archive *ad;
-       struct stat st;
-       time_t now;
 
        /* Write the entry to disk. */
        assert((ad = archive_write_disk_new()) != NULL);
@@ -99,11 +97,9 @@ static void create_reg_file(struct archive_entry *ae, const char *msg)
        assertIsReg(archive_entry_pathname(ae), archive_entry_mode(ae) & 0777);
        assertFileSize(archive_entry_pathname(ae), sizeof(data));
        /* test_write_disk_times has more detailed tests of this area. */
-       assert(0 == stat(archive_entry_pathname(ae), &st));
-        assertEqualInt(st.st_mtime, 123456789);
-        failure("No atime was specified, so atime should get set to current time");
-       now = time(NULL);
-        assert(st.st_atime <= now && st.st_atime > now - 5);
+       assertFileMtime(archive_entry_pathname(ae), 123456789, 0);
+        failure("No atime given, so atime should get set to current time");
+       assertFileAtimeRecent(archive_entry_pathname(ae));
 }
 
 static void create_reg_file2(struct archive_entry *ae, const char *msg)
index 2891e3612aff8d76888050595b9623cab195a5bf..13841cfddafe4d7c1f641eb906d102b4578367f6 100644 (file)
@@ -35,8 +35,6 @@ DEFINE_TEST(test_write_disk_times)
 {
        struct archive *a;
        struct archive_entry *ae;
-       struct stat st;
-       time_t now = time(NULL);
 
        /* Create an archive_write_disk object. */
        assert((a = archive_write_disk_new()) != NULL);
@@ -55,9 +53,8 @@ DEFINE_TEST(test_write_disk_times)
        assertEqualInt(ARCHIVE_OK, archive_write_finish_entry(a));
        archive_entry_free(ae);
        /* Verify */
-       assertEqualInt(0, stat("file1", &st));
-       assertEqualInt(123456, st.st_atime);
-       assertEqualInt(234567, st.st_mtime);
+       assertFileAtime("file1", 123456, 0);
+       assertFileMtime("file1", 234567, 0);
 
        /*
         * mtime specified, but not atime
@@ -69,11 +66,8 @@ DEFINE_TEST(test_write_disk_times)
        assertEqualInt(ARCHIVE_OK, archive_write_header(a, ae));
        assertEqualInt(ARCHIVE_OK, archive_write_finish_entry(a));
        archive_entry_free(ae);
-       /* Verify: Current atime and mtime as specified. */
-       assertEqualInt(0, stat("file2", &st));
-       assertEqualInt(234567, st.st_mtime);
-       failure("now: %ld st.st_atime: %ld", (long)now, (long)st.st_atime);
-       assert(st.st_atime >= now && st.st_atime < now + 3);
+       assertFileMtime("file2", 234567, 0);
+       assertFileAtimeRecent("file2");
 
        /*
         * atime specified, but not mtime
@@ -86,10 +80,8 @@ DEFINE_TEST(test_write_disk_times)
        assertEqualInt(ARCHIVE_OK, archive_write_finish_entry(a));
        archive_entry_free(ae);
        /* Verify: Current mtime and atime as specified. */
-       assertEqualInt(0, stat("file3", &st));
-       assertEqualInt(345678, st.st_atime);
-       failure("now: %ld st.st_mtime: %ld", (long)now, (long)st.st_mtime);
-       assert(st.st_mtime >= now && st.st_mtime < now + 3);
+       assertFileAtime("file3", 345678, 0);
+       assertFileMtimeRecent("file3");
 
        /*
         * Neither atime nor mtime specified.
@@ -101,11 +93,8 @@ DEFINE_TEST(test_write_disk_times)
        assertEqualInt(ARCHIVE_OK, archive_write_finish_entry(a));
        archive_entry_free(ae);
        /* Verify: Current mtime and atime. */
-       assertEqualInt(0, stat("file4", &st));
-       failure("now: %ld st.st_atime: %ld", (long)now, (long)st.st_atime);
-       assert(st.st_atime >= now && st.st_atime < now + 3);
-       failure("now: %ld st.st_mtime: %ld", (long)now, (long)st.st_mtime);
-       assert(st.st_mtime >= now && st.st_mtime < now + 3);
+       assertFileAtimeRecent("file4");
+       assertFileMtimeRecent("file4");
 
 #if defined(__FreeBSD__)
        /*
@@ -120,12 +109,8 @@ DEFINE_TEST(test_write_disk_times)
        assertEqualInt(ARCHIVE_OK, archive_write_finish_entry(a));
        archive_entry_free(ae);
        /* Verify */
-       /* FreeBSD can only store usec resolution, hence rounding here. */
-       assertEqualInt(0, stat("file10", &st));
-       assertEqualInt(1234567, st.st_atime);
-       assertEqualInt(23000, st.st_atimespec.tv_nsec);
-       assertEqualInt(2345678, st.st_mtime);
-       assertEqualInt(4000, st.st_mtimespec.tv_nsec);
+       assertFileMtime("file10", 2345678, 4567);
+       assertFileAtime("file10", 1234567, 23456);
 
        /*
         * Birthtime, mtime and atime on FreeBSD
@@ -141,14 +126,9 @@ DEFINE_TEST(test_write_disk_times)
        assertEqualInt(ARCHIVE_OK, archive_write_finish_entry(a));
        archive_entry_free(ae);
        /* Verify */
-       /* FreeBSD can only store usec resolution, hence rounding here. */
-       assertEqualInt(0, stat("file11", &st));
-       assertEqualInt(1234567, st.st_atime);
-       assertEqualInt(23000, st.st_atimespec.tv_nsec);
-       assertEqualInt(3456789, st.st_birthtime);
-       assertEqualInt(12000, st.st_birthtimespec.tv_nsec);
-       assertEqualInt(12345678, st.st_mtime);
-       assertEqualInt(4000, st.st_mtimespec.tv_nsec);
+       assertFileAtime("file11", 1234567, 23456);
+       assertFileBirthtime("file11", 3456789, 12345);
+       assertFileMtime("file11", 12345678, 4567);
 
        /*
         * Birthtime only on FreeBSD.
@@ -161,14 +141,9 @@ DEFINE_TEST(test_write_disk_times)
        assertEqualInt(ARCHIVE_OK, archive_write_finish_entry(a));
        archive_entry_free(ae);
        /* Verify */
-       /* FreeBSD can only store usec resolution, hence rounding here. */
-       assertEqualInt(0, stat("file12", &st));
-       assertEqualInt(3456789, st.st_birthtime);
-       assertEqualInt(12000, st.st_birthtimespec.tv_nsec);
-       failure("now: %ld st.st_atime: %ld", (long)now, (long)st.st_atime);
-       assert(st.st_atime >= now && st.st_atime < now + 3);
-       failure("now: %ld st.st_mtime: %ld", (long)now, (long)st.st_mtime);
-       assert(st.st_mtime >= now && st.st_mtime < now + 3);
+       assertFileAtimeRecent("file12");
+       assertFileBirthtime("file12", 3456789, 12345);
+       assertFileMtimeRecent("file12");
 
        /*
         * mtime only on FreeBSD.
@@ -181,14 +156,9 @@ DEFINE_TEST(test_write_disk_times)
        assertEqualInt(ARCHIVE_OK, archive_write_finish_entry(a));
        archive_entry_free(ae);
        /* Verify */
-       /* FreeBSD can only store usec resolution, hence rounding here. */
-       assertEqualInt(0, stat("file13", &st));
-       assertEqualInt(4567890, st.st_birthtime);
-       assertEqualInt(23000, st.st_birthtimespec.tv_nsec);
-       assertEqualInt(4567890, st.st_mtime);
-       assertEqualInt(23000, st.st_mtimespec.tv_nsec);
-       failure("now: %ld st.st_atime: %ld", (long)now, (long)st.st_atime);
-       assert(st.st_atime >= now && st.st_atime < now + 3);
+       assertFileAtimeRecent("file13");
+       assertFileBirthtime("file13", 4567890, 23456);
+       assertFileMtime("file13", 4567890, 23456);
 #else
        skipping("Platform-specific time restore tests");
 #endif