From: Tim Kientzle Date: Fri, 11 Sep 2009 05:28:37 +0000 (-0400) Subject: Real hardlink detection on Windows (yes, Windows stat() really does seem to be that... X-Git-Tag: v2.8.0~356 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=89eaf435abccc3c0e1314dc8b8df1b8ead7d638a;p=thirdparty%2Flibarchive.git Real hardlink detection on Windows (yes, Windows stat() really does seem to be that broken!). Also, expose the core hardlink test as a non-assertion so we can assert it false. In particular, this fixes test_option_l on Windows. SVN-Revision: 1452 --- diff --git a/cpio/test/main.c b/cpio/test/main.c index e8371b05a..6100eccea 100644 --- a/cpio/test/main.c +++ b/cpio/test/main.c @@ -133,6 +133,21 @@ my_CreateHardLinkA(const char *linkname, const char *target) } return f == NULL ? 0 : (*f)(linkname, target, NULL); } + +int +my_GetFileInformationByName(const char *path, BY_HANDLE_FILE_INFORMATION *bhfi) +{ + HANDLE h; + int r; + + h = CreateFile(path, FILE_READ_ATTRIBUTES, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (h == INVALID_HANDLE_VALUE) + return (0); + r = GetFileInformationByHandle(h, bhfi); + CloseHandle(h); + return (r); +} #endif #if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__GNUC__) @@ -794,11 +809,32 @@ assertion_text_file_contents(const char *buff, const char *fn) return (0); } -/* Verify that two paths point to the same file. */ +/* Test that two paths point to the same file. */ +/* As a side-effect, asserts that both files exist. */ int -assertion_file_hardlinks(const char *file, int line, +is_file_hardlinks(const char *file, int line, const char *path1, const char *path2) { +#if defined(_WIN32) && !defined(__CYGWIN__) + BY_HANDLE_FILE_INFORMATION bhfi1, bhfi2; + int r; + + assertion_count(file, line); + r = my_GetFileInformationByName(path1, &bhfi1); + if (r == 0) { + failure_start(file, line, "File %s can't be inspected?", path1); + failure_finish(NULL); + return (0); + } + r = my_GetFileInformationByName(path2, &bhfi2); + if (r == 0) { + failure_start(file, line, "File %s can't be inspected?", path2); + failure_finish(NULL); + return (0); + } + return (bhfi1.nFileIndexHigh == bhfi2.nFileIndexHigh + && bhfi1.nFileIndexLow == bhfi2.nFileIndexLow); +#else struct stat st1, st2; int r; @@ -815,13 +851,20 @@ assertion_file_hardlinks(const char *file, int line, failure_finish(NULL); return (0); } - if (st1.st_ino != st2.st_ino || st1.st_dev != st2.st_dev) { - failure_start(file, line, - "Files %s and %s are not hardlinked", path1, path2); - failure_finish(NULL); - return (0); - } - return (1); + return (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev); +#endif +} + +int +assertion_file_hardlinks(const char *file, int line, + const char *path1, const char *path2) +{ + if (is_file_hardlinks(file, line, path1, path2)) + return (1); + failure_start(file, line, + "Files %s and %s are not hardlinked", path1, path2); + failure_finish(NULL); + return (0); } /* Verify a/b/mtime of 'pathname'. */ @@ -972,19 +1015,11 @@ assertion_file_nlinks(const char *file, int line, const char *pathname, int nlinks) { #if defined(_WIN32) && !defined(__CYGWIN__) - HANDLE h; BY_HANDLE_FILE_INFORMATION bhfi; int r; 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", pathname); - failure_finish(NULL); - return (0); - } - r = GetFileInformationByHandle(h, &bhfi); + r = my_GetFileInformationByName(pathname, &bhfi); if (r != 0 && bhfi.nNumberOfLinks == nlinks) return (1); failure_start(file, line, "File %s has %d links, expected %d", diff --git a/cpio/test/test.h b/cpio/test/test.h index c96181345..5d89a45c4 100644 --- a/cpio/test/test.h +++ b/cpio/test/test.h @@ -196,6 +196,11 @@ #define assertUmask(mask) \ assertion_umask(__FILE__, __LINE__, mask) +/* + */ +#define isFileHardlinks(path1, path2) \ + is_file_hardlinks(__FILE__, __LINE__, path1, path2) + /* * This would be simple with C99 variadic macros, but I don't want to * require that. Instead, I insert a function call before each @@ -238,6 +243,8 @@ int assertion_non_empty_file(const char *, ...); int assertion_text_file_contents(const char *buff, const char *f); int assertion_umask(const char *, int, int); void assertion_setup(const char *, int); + +int is_file_hardlinks(const char *, int, const char *, const char *); void test_skipping(const char *fmt, ...); /* Like sprintf, then system() */ diff --git a/cpio/test/test_option_l.c b/cpio/test/test_option_l.c index 81135d58d..f2cd5bfbf 100644 --- a/cpio/test/test_option_l.c +++ b/cpio/test/test_option_l.c @@ -27,23 +27,18 @@ __FBSDID("$FreeBSD$"); DEFINE_TEST(test_option_l) { - struct stat st, st2; int r; /* Create a file. */ assertMakeFile("f", 0644, "a"); - /* Stat it. */ - assertEqualInt(0, stat("f", &st)); - /* Copy the file to the "copy" dir. */ r = systemf("echo f | %s -pd copy >copy.out 2>copy.err", testprog); assertEqualInt(r, 0); /* Check that the copy is a true copy and not a link. */ - assertEqualInt(0, stat("copy/f", &st2)); - assert(st2.st_ino != st.st_ino); + assert(!isFileHardlinks("f", "copy/f")); /* Copy the file to the "link" dir with the -l option. */ r = systemf("echo f | %s -pld link >link.out 2>link.err", @@ -51,6 +46,5 @@ DEFINE_TEST(test_option_l) assertEqualInt(r, 0); /* Check that this is a link and not a copy. */ - assertEqualInt(0, stat("link/f", &st2)); - assert(st2.st_ino == st.st_ino); + assertFileHardlinks("f", "link/f"); }