From 72c342e64e7edbca6304b85eb0be75662b35b364 Mon Sep 17 00:00:00 2001 From: Tim Kientzle Date: Sun, 6 Sep 2009 02:44:40 -0400 Subject: [PATCH] Add new assertions to verify atime, birthtime, and mtime, both 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 | 139 ++++++++++++++++++++++++ libarchive/test/test.h | 18 +++ libarchive/test/test_write_disk.c | 10 +- libarchive/test/test_write_disk_times.c | 68 ++++-------- 4 files changed, 179 insertions(+), 56 deletions(-) diff --git a/libarchive/test/main.c b/libarchive/test/main.c index bcfbb2d6e..beb6cadfc 100644 --- a/libarchive/test/main.c +++ b/libarchive/test/main.c @@ -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, diff --git a/libarchive/test/test.h b/libarchive/test/test.h index 3ffd16396..d5c06b331 100644 --- a/libarchive/test/test.h +++ b/libarchive/test/test.h @@ -148,6 +148,14 @@ /* 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 @@ -159,6 +167,10 @@ 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); diff --git a/libarchive/test/test_write_disk.c b/libarchive/test/test_write_disk.c index bf77d5314..adcc215c8 100644 --- a/libarchive/test/test_write_disk.c +++ b/libarchive/test/test_write_disk.c @@ -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) diff --git a/libarchive/test/test_write_disk_times.c b/libarchive/test/test_write_disk_times.c index 2891e3612..13841cfdd 100644 --- a/libarchive/test/test_write_disk_times.c +++ b/libarchive/test/test_write_disk_times.c @@ -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 -- 2.47.3