From b17df5e194724f7e83fa028471f00db1a78f511a Mon Sep 17 00:00:00 2001 From: Martin Matuska Date: Sat, 13 Apr 2019 21:51:03 +0200 Subject: Windows symlink bugfixes and improvements Treat targets ending with /. and /.. as directory symlinks Explicitly test for file and directory symlinks Improve debug output on test failure Fix two memory allocations --- cpio/test/test_basic.c | 2 +- cpio/test/test_gcpio_compat.c | 2 +- cpio/test/test_option_L_upper.c | 4 +- libarchive/archive_write_disk_windows.c | 34 ++++---- libarchive/test/test_read_extract.c | 2 +- libarchive/test/test_write_disk_symlink.c | 129 +++++++++++++++++++++++++++++- tar/test/test_basic.c | 2 +- tar/test/test_copy.c | 2 +- tar/test/test_option_H_upper.c | 26 +++--- tar/test/test_option_L_upper.c | 18 ++--- tar/test/test_option_U_upper.c | 6 +- tar/test/test_option_s.c | 16 ++-- tar/test/test_strip_components.c | 4 +- tar/test/test_symlink_dir.c | 4 +- test_utils/test_common.h | 6 +- test_utils/test_main.c | 82 +++++++++++++++---- 16 files changed, 261 insertions(+), 78 deletions(-) diff --git a/cpio/test/test_basic.c b/cpio/test/test_basic.c index 855793fb..a8fedf89 100644 --- a/cpio/test/test_basic.c +++ b/cpio/test/test_basic.c @@ -46,7 +46,7 @@ verify_files(const char *msg) /* Symlink */ if (canSymlink()) - assertIsSymlink("symlink", "file"); + assertIsSymlink("symlink", "file", 0); /* Another file with 1 link and different permissions. */ failure(msg); diff --git a/cpio/test/test_gcpio_compat.c b/cpio/test/test_gcpio_compat.c index 461e427c..9bb98899 100644 --- a/cpio/test/test_gcpio_compat.c +++ b/cpio/test/test_gcpio_compat.c @@ -71,7 +71,7 @@ unpack_test(const char *from, const char *options, const char *se) /* Symlink */ if (canSymlink()) - assertIsSymlink("symlink", "file"); + assertIsSymlink("symlink", "file", 0); /* dir */ assertIsDir("dir", 0775); diff --git a/cpio/test/test_option_L_upper.c b/cpio/test/test_option_L_upper.c index bf3a9c43..cab41b61 100644 --- a/cpio/test/test_option_L_upper.c +++ b/cpio/test/test_option_L_upper.c @@ -63,7 +63,7 @@ DEFINE_TEST(test_option_L_upper) assertTextFileContents("1 block\n", "copy.err"); failure("Regular -p without -L should preserve symlinks."); - assertIsSymlink("copy/symlink", NULL); + assertIsSymlink("copy/symlink", NULL, 0); r = systemf(CAT " filelist | %s -pd -L copy-L >copy-L.out 2>copy-L.err", testprog); assertEqualInt(r, 0); @@ -86,7 +86,7 @@ DEFINE_TEST(test_option_L_upper) assertTextFileContents("1 block\n", "unpack.err"); assertChdir(".."); - assertIsSymlink("unpack/symlink", NULL); + assertIsSymlink("unpack/symlink", NULL, 0); r = systemf(CAT " filelist | %s -oL >archive-L.out 2>archive-L.err", testprog); failure("Error invoking %s -oL", testprog); diff --git a/libarchive/archive_write_disk_windows.c b/libarchive/archive_write_disk_windows.c index 811e7772..b29389c1 100644 --- a/libarchive/archive_write_disk_windows.c +++ b/libarchive/archive_write_disk_windows.c @@ -558,8 +558,10 @@ la_CreateHardLinkW(wchar_t *linkname, wchar_t *target) set = 1; f = la_GetFunctionKernel32("CreateHardLinkW"); } - if (!f) + if (!f) { + errno = ENOTSUP; return (0); + } ret = (*f)(linkname, target, NULL); if (!ret) { /* Under windows 2000, it is necessary to remove @@ -595,6 +597,7 @@ la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target) { static BOOLEAN (WINAPI *f)(LPCWSTR, LPCWSTR, DWORD); static int set; wchar_t *ttarget, *p; + int len; DWORD attrs = 0; DWORD flags = 0; DWORD newflags = 0; @@ -607,11 +610,16 @@ la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target) { if (!f) return (0); + len = wcslen(target); + if (len == 0) { + errno = EINVAL; + return(0); + } /* * When writing path targets, we need to translate slashes * to backslashes */ - ttarget = malloc((wcslen(target) + 1) * sizeof(wchar_t)); + ttarget = malloc((len + 1) * sizeof(wchar_t)); if (ttarget == NULL) return(0); @@ -628,23 +636,19 @@ la_CreateSymbolicLinkW(const wchar_t *linkname, const wchar_t *target) { *p = L'\0'; /* - * If the target equals ".", ".." or ends with a backslash, it always - * points to a directory. In this case we can safely set the directory - * flag. All other symlinks are created as file symlinks. + * If the target equals ".", "..", ends with a backslash or a + * backslash followed by "." or ".." it always points to a directory. + * In this case we can safely set the directory flag. + * All other symlinks are created as file symlinks. */ - if (wcscmp(ttarget, L".") == 0 || wcscmp(ttarget, L"..") == 0 || - *(p - 1) == L'\\') { + if (*(p - 1) == L'\\' || (*(p - 1) == L'.' && ( + len == 1 || *(p - 2) == L'\\' || ( *(p - 2) == L'.' && ( + len == 2 || *(p - 3) == L'\\'))))) { #if defined(SYMBOLIC_LINK_FLAG_DIRECTORY) flags |= SYMBOLIC_LINK_FLAG_DIRECTORY; #else flags |= 0x1; #endif - /* Now we remove trailing backslashes, if any */ - p--; - while(*p == L'\\') { - *p = L'\0'; - p--; - } } #if defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) @@ -1633,9 +1637,11 @@ create_filesystem_object(struct archive_write_disk *a) #if HAVE_SYMLINK return symlink(linkname, a->name) ? errno : 0; #else + errno = 0; r = la_CreateSymbolicLinkW((const wchar_t *)a->name, linkname); if (r == 0) { - la_dosmaperr(GetLastError()); + if (errno == 0) + la_dosmaperr(GetLastError()); r = errno; } else r = 0; diff --git a/libarchive/test/test_read_extract.c b/libarchive/test/test_read_extract.c index c537e4f9..cd06096e 100644 --- a/libarchive/test/test_read_extract.c +++ b/libarchive/test/test_read_extract.c @@ -161,7 +161,7 @@ DEFINE_TEST(test_read_extract) assertIsDir("dir4/b", 0755); assertIsDir("dir4/c", 0711); if (canSymlink()) - assertIsSymlink("symlink", "file"); + assertIsSymlink("symlink", "file", 0); free(buff); free(file_buff); diff --git a/libarchive/test/test_write_disk_symlink.c b/libarchive/test/test_write_disk_symlink.c index 13089c78..43796a45 100644 --- a/libarchive/test/test_write_disk_symlink.c +++ b/libarchive/test/test_write_disk_symlink.c @@ -99,6 +99,116 @@ DEFINE_TEST(test_write_disk_symlink) assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); archive_entry_free(ae); + /* Symbolic link: dot -> . */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "dot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: dotdot -> .. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "dotdot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, ".."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: slash -> / */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "slash"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "/"); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: sldot -> /. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "sldot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "/."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: sldotdot -> /.. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "sldotdot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "/.."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Dir: d1 */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1"); + archive_entry_set_mode(ae, AE_IFDIR | 0777); + archive_entry_unset_size(ae); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: d1nosl -> d1 */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1nosl"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1"); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: d1slash -> d1/ */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1slash"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1/"); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: d1sldot -> d1/. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1sldot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1/."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + /* Symbolic link: d1slddot -> d1/.. */ + assert((ae = archive_entry_new()) != NULL); + archive_entry_copy_pathname(ae, "d1slddot"); + archive_entry_set_mode(ae, AE_IFLNK | 0642); + archive_entry_unset_size(ae); + archive_entry_copy_symlink(ae, "d1/.."); + assertEqualIntA(ad, 0, r = archive_write_header(ad, ae)); + if (r >= ARCHIVE_WARN) + assertEqualIntA(ad, 0, archive_write_finish_entry(ad)); + archive_entry_free(ae); + + assertEqualInt(ARCHIVE_OK, archive_write_free(ad)); /* Test the entries on disk. */ @@ -107,11 +217,26 @@ DEFINE_TEST(test_write_disk_symlink) assertIsReg("link1a", -1); assertFileSize("link1a", sizeof(data)); assertFileNLinks("link1a", 1); - assertIsSymlink("link1b", "link1a"); + assertIsSymlink("link1b", "link1a", 0); /* Test #2: Should produce identical results to test #1 */ assertIsReg("link2a", -1); assertFileSize("link2a", sizeof(data)); assertFileNLinks("link2a", 1); - assertIsSymlink("link2b", "link2a"); + assertIsSymlink("link2b", "link2a", 0); + + /* Test #3: Special symlinks */ + assertIsSymlink("dot", ".", 1); + assertIsSymlink("dotdot", "..", 1); + assertIsSymlink("slash", "/", 1); + assertIsSymlink("sldot", "/.", 1); + assertIsSymlink("sldotdot", "/..", 1); + + /* Test #4: Directory symlink mixed with . and .. */ + assertIsDir("d1", -1); + /* On Windows, d1nosl should be a file symlink */ + assertIsSymlink("d1nosl", "d1", 0); + assertIsSymlink("d1slash", "d1/", 1); + assertIsSymlink("d1sldot", "d1/.", 1); + assertIsSymlink("d1slddot", "d1/..", 1); } diff --git a/tar/test/test_basic.c b/tar/test/test_basic.c index 91282cde..9bb966a0 100644 --- a/tar/test/test_basic.c +++ b/tar/test/test_basic.c @@ -78,7 +78,7 @@ verify_files(const char *target) /* Symlink */ if (canSymlink()) - assertIsSymlink("symlink", "file"); + assertIsSymlink("symlink", "file", 0); /* dir */ failure("%s", target); diff --git a/tar/test/test_copy.c b/tar/test/test_copy.c index 1e59e192..b828666b 100644 --- a/tar/test/test_copy.c +++ b/tar/test/test_copy.c @@ -222,7 +222,7 @@ verify_tree(size_t limit) sprintf(name1, "s/%s", filenames[i]); sprintf(name2, "../f/%s", filenames[i]); if (strlen(name2) <= limit) - assertIsSymlink(name1, name2); + assertIsSymlink(name1, name2, 0); } /* Verify dir "d/abcdef...". */ diff --git a/tar/test/test_option_H_upper.c b/tar/test/test_option_H_upper.c index 7c201ce2..2c2ad33c 100644 --- a/tar/test/test_option_H_upper.c +++ b/tar/test/test_option_H_upper.c @@ -55,11 +55,11 @@ DEFINE_TEST(test_option_H_upper) assertChdir("test1"); assertEqualInt(0, systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); - assertIsSymlink("ld1", "d1"); - assertIsSymlink("d1/link1", "file1"); - assertIsSymlink("d1/linkX", "fileX"); - assertIsSymlink("link2", "d1/file2"); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("ld1", "d1", 1); + assertIsSymlink("d1/link1", "file1", 0); + assertIsSymlink("d1/linkX", "fileX", 0); + assertIsSymlink("link2", "d1/file2", 0); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Test 2: With -H, no symlink on command line. */ @@ -69,11 +69,11 @@ DEFINE_TEST(test_option_H_upper) assertChdir("test2"); assertEqualInt(0, systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); - assertIsSymlink("ld1", "d1"); - assertIsSymlink("d1/link1", "file1"); - assertIsSymlink("d1/linkX", "fileX"); - assertIsSymlink("link2", "d1/file2"); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("ld1", "d1", 1); + assertIsSymlink("d1/link1", "file1", 0); + assertIsSymlink("d1/linkX", "fileX", 0); + assertIsSymlink("link2", "d1/file2", 0); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Test 3: With -H, some symlinks on command line. */ @@ -84,9 +84,9 @@ DEFINE_TEST(test_option_H_upper) assertEqualInt(0, systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); assertIsDir("ld1", umasked(0755)); - assertIsSymlink("d1/linkX", "fileX"); - assertIsSymlink("d1/link1", "file1"); + assertIsSymlink("d1/linkX", "fileX", 0); + assertIsSymlink("d1/link1", "file1", 0); assertIsReg("link2", umasked(0644)); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); } diff --git a/tar/test/test_option_L_upper.c b/tar/test/test_option_L_upper.c index 83f69d08..5697b0f2 100644 --- a/tar/test/test_option_L_upper.c +++ b/tar/test/test_option_L_upper.c @@ -55,11 +55,11 @@ DEFINE_TEST(test_option_L_upper) assertChdir("test1"); assertEqualInt(0, systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); - assertIsSymlink("ld1", "d1"); - assertIsSymlink("d1/link1", "file1"); - assertIsSymlink("d1/linkX", "fileX"); - assertIsSymlink("link2", "d1/file2"); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("ld1", "d1", 1); + assertIsSymlink("d1/link1", "file1", 0); + assertIsSymlink("d1/linkX", "fileX", 0); + assertIsSymlink("link2", "d1/file2", 0); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Test 2: With -L, no symlink on command line. */ @@ -71,9 +71,9 @@ DEFINE_TEST(test_option_L_upper) systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); assertIsDir("ld1", umasked(0755)); assertIsReg("d1/link1", umasked(0644)); - assertIsSymlink("d1/linkX", "fileX"); + assertIsSymlink("d1/linkX", "fileX", 0); assertIsReg("link2", umasked(0644)); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); /* Test 3: With -L, some symlinks on command line. */ @@ -85,8 +85,8 @@ DEFINE_TEST(test_option_L_upper) systemf("%s -xf archive.tar >c.out 2>c.err", testprog)); assertIsDir("ld1", umasked(0755)); assertIsReg("d1/link1", umasked(0644)); - assertIsSymlink("d1/linkX", "fileX"); + assertIsSymlink("d1/linkX", "fileX", 0); assertIsReg("link2", umasked(0644)); - assertIsSymlink("linkY", "d1/fileY"); + assertIsSymlink("linkY", "d1/fileY", 0); assertChdir(".."); } diff --git a/tar/test/test_option_U_upper.c b/tar/test/test_option_U_upper.c index 83205486..d864e13c 100644 --- a/tar/test/test_option_U_upper.c +++ b/tar/test/test_option_U_upper.c @@ -82,7 +82,7 @@ DEFINE_TEST(test_option_U_upper) assertMakeSymlink("d1", "realDir", 1); r = systemf("%s -xf ../archive.tar d1/file1 >test.out 2>test.err", testprog); assert(r != 0); - assertIsSymlink("d1", "realDir"); + assertIsSymlink("d1", "realDir", 1); assertFileNotExists("d1/file1"); assertEmptyFile("test.out"); assertNonEmptyFile("test.err"); @@ -108,7 +108,7 @@ DEFINE_TEST(test_option_U_upper) assertMakeSymlink("d1", "realDir", 1); assertEqualInt(0, systemf("%s -xPf ../archive.tar d1/file1 >test.out 2>test.err", testprog)); - assertIsSymlink("d1", "realDir"); + assertIsSymlink("d1", "realDir", 1); assertFileContents("d1/file1", 8, "d1/file1"); assertEmptyFile("test.out"); assertEmptyFile("test.err"); @@ -121,7 +121,7 @@ DEFINE_TEST(test_option_U_upper) assertMakeSymlink("d1", "realDir", 1); assertEqualInt(0, systemf("%s -xPUf ../archive.tar d1/file1 >test.out 2>test.err", testprog)); - assertIsSymlink("d1", "realDir"); + assertIsSymlink("d1", "realDir", 1); assertFileContents("d1/file1", 8, "d1/file1"); assertEmptyFile("test.out"); assertEmptyFile("test.err"); diff --git a/tar/test/test_option_s.c b/tar/test/test_option_s.c index a0896606..09c72ee7 100644 --- a/tar/test/test_option_s.c +++ b/tar/test/test_option_s.c @@ -109,14 +109,14 @@ DEFINE_TEST(test_option_s) testprog, testprog); assertFileContents("realfile", 8, "test6a/in/d2/realfile"); assertFileContents("realfile", 8, "test6a/in/d2/symlink"); - assertIsSymlink("test6a/in/d2/symlink", "realfile"); + assertIsSymlink("test6a/in/d2/symlink", "realfile", 0); /* At creation time. */ assertMakeDir("test6b", 0755); systemf("%s -cf - -s /d1/d2/ in/d1 | %s -xf - -C test6b", testprog, testprog); assertFileContents("realfile", 8, "test6b/in/d2/realfile"); assertFileContents("realfile", 8, "test6b/in/d2/symlink"); - assertIsSymlink("test6b/in/d2/symlink", "realfile"); + assertIsSymlink("test6b/in/d2/symlink", "realfile", 0); } /* @@ -129,14 +129,14 @@ DEFINE_TEST(test_option_s) testprog, testprog); assertFileContents("realfile", 8, "test7a/in/d1/realfile-renamed"); assertFileContents("realfile", 8, "test7a/in/d1/symlink"); - assertIsSymlink("test7a/in/d1/symlink", "realfile-renamed"); + assertIsSymlink("test7a/in/d1/symlink", "realfile-renamed", 0); /* At creation. */ assertMakeDir("test7b", 0755); systemf("%s -cf - -s /realfile/realfile-renamed/ in/d1 | %s -xf - -C test7b", testprog, testprog); assertFileContents("realfile", 8, "test7b/in/d1/realfile-renamed"); assertFileContents("realfile", 8, "test7b/in/d1/symlink"); - assertIsSymlink("test7b/in/d1/symlink", "realfile-renamed"); + assertIsSymlink("test7b/in/d1/symlink", "realfile-renamed", 0); } /* @@ -192,7 +192,7 @@ DEFINE_TEST(test_option_s) assertFileContents("realfile", 8, "test10a/in/d1/foo"); assertFileContents("foo", 3, "test10a/in/d1/realfile"); assertFileContents("foo", 3, "test10a/in/d1/symlink"); - assertIsSymlink("test10a/in/d1/symlink", "realfile"); + assertIsSymlink("test10a/in/d1/symlink", "realfile", 0); /* At creation. */ assertMakeDir("test10b", 0755); systemf("%s -cf - -s /realfile/foo/S -s /foo/realfile/ in/d1 | %s -xf - -C test10b", @@ -200,7 +200,7 @@ DEFINE_TEST(test_option_s) assertFileContents("realfile", 8, "test10b/in/d1/foo"); assertFileContents("foo", 3, "test10b/in/d1/realfile"); assertFileContents("foo", 3, "test10b/in/d1/symlink"); - assertIsSymlink("test10b/in/d1/symlink", "realfile"); + assertIsSymlink("test10b/in/d1/symlink", "realfile", 0); } /* @@ -214,7 +214,7 @@ DEFINE_TEST(test_option_s) assertFileContents("foo", 3, "test11a/in/d1/foo"); assertFileContents("realfile", 8, "test11a/in/d1/realfile"); assertFileContents("foo", 3, "test11a/in/d1/symlink"); - assertIsSymlink("test11a/in/d1/symlink", "foo"); + assertIsSymlink("test11a/in/d1/symlink", "foo", 0); /* At creation. */ assertMakeDir("test11b", 0755); systemf("%s -cf - -s /realfile/foo/R in/d1 | %s -xf - -C test11b", @@ -222,7 +222,7 @@ DEFINE_TEST(test_option_s) assertFileContents("foo", 3, "test11b/in/d1/foo"); assertFileContents("realfile", 8, "test11b/in/d1/realfile"); assertFileContents("foo", 3, "test11b/in/d1/symlink"); - assertIsSymlink("test11b/in/d1/symlink", "foo"); + assertIsSymlink("test11b/in/d1/symlink", "foo", 0); } /* diff --git a/tar/test/test_strip_components.c b/tar/test/test_strip_components.c index caf45a3e..090fb0db 100644 --- a/tar/test/test_strip_components.c +++ b/tar/test/test_strip_components.c @@ -65,7 +65,7 @@ DEFINE_TEST(test_strip_components) /* If platform supports symlinks, target/s2 is a broken symlink. */ /* If platform does not support symlink, target/s2 doesn't exist. */ if (canSymlink()) - assertIsSymlink("target/s2", "d2/f1"); + assertIsSymlink("target/s2", "d2/f1", 0); else assertFileNotExists("target/s2"); failure("d0/d1/d2 should be extracted"); @@ -123,7 +123,7 @@ DEFINE_TEST(test_strip_components) /* If platform supports symlinks, target/s2 is included. */ if (canSymlink()) { failure("d0/d1/s2 is a symlink to something included in archive"); - assertIsSymlink("target2/s2", "d2/f1"); + assertIsSymlink("target2/s2", "d2/f1", 0); } failure("d0/d1/d2 should be archived"); assertIsDir("target2/d2", -1); diff --git a/tar/test/test_symlink_dir.c b/tar/test/test_symlink_dir.c index 485ab32f..5836647c 100644 --- a/tar/test/test_symlink_dir.c +++ b/tar/test/test_symlink_dir.c @@ -132,8 +132,8 @@ DEFINE_TEST(test_symlink_dir) /* dest2/dir and dest2/dir4 symlinks should be followed */ if (canSymlink()) { - assertIsSymlink("dest2/dir", "real_dir"); - assertIsSymlink("dest2/dir4", "real_dir"); + assertIsSymlink("dest2/dir", "real_dir", 1); + assertIsSymlink("dest2/dir4", "real_dir", 1); assertIsDir("dest2/real_dir", -1); } diff --git a/test_utils/test_common.h b/test_utils/test_common.h index 154a6964..7538d8cb 100644 --- a/test_utils/test_common.h +++ b/test_utils/test_common.h @@ -220,8 +220,8 @@ assertion_is_not_hardlink(__FILE__, __LINE__, path1, path2) #define assertIsReg(pathname, mode) \ assertion_is_reg(__FILE__, __LINE__, pathname, mode) -#define assertIsSymlink(pathname, contents) \ - assertion_is_symlink(__FILE__, __LINE__, pathname, contents) +#define assertIsSymlink(pathname, contents, isdir) \ + assertion_is_symlink(__FILE__, __LINE__, pathname, contents, isdir) /* Create a directory, report error if it fails. */ #define assertMakeDir(dirname, mode) \ assertion_make_dir(__FILE__, __LINE__, dirname, mode) @@ -289,7 +289,7 @@ int assertion_is_dir(const char *, int, const char *, int); int assertion_is_hardlink(const char *, int, const char *, const char *); int assertion_is_not_hardlink(const char *, int, const char *, const char *); int assertion_is_reg(const char *, int, const char *, int); -int assertion_is_symlink(const char *, int, const char *, const char *); +int assertion_is_symlink(const char *, int, const char *, const char *, int); int assertion_make_dir(const char *, int, const char *, int); int assertion_make_file(const char *, int, const char *, int, int, const void *); int assertion_make_hardlink(const char *, int, const char *newpath, const char *); diff --git a/test_utils/test_main.c b/test_utils/test_main.c index cd4c772b..d9f3b19f 100644 --- a/test_utils/test_main.c +++ b/test_utils/test_main.c @@ -217,7 +217,7 @@ my_CreateSymbolicLinkA(const char *linkname, const char *target, static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD); DWORD attrs; static int set; - int ret, tmpflags; + int ret, tmpflags, llen, tlen; int flags = 0; char *src, *tgt, *p; if (!set) { @@ -227,10 +227,16 @@ my_CreateSymbolicLinkA(const char *linkname, const char *target, if (f == NULL) return (0); - tgt = malloc(strlen(target) + 1); + tlen = strlen(target); + llen = strlen(linkname); + + if (tlen == 0 || llen == 0) + return (0); + + tgt = malloc((tlen + 1) * sizeof(char)); if (tgt == NULL) return (0); - src = malloc(strlen(linkname) + 1); + src = malloc((llen + 1) * sizeof(char)); if (src == NULL) { free(tgt); return (0); @@ -262,8 +268,8 @@ my_CreateSymbolicLinkA(const char *linkname, const char *target, *p = '\0'; /* - * If the target equals ".", ".." or ends with a slash, it always - * points to a directory. In this case we can set the directory flag. + * Each test has to specify if a file or a directory symlink + * should be created. */ if (targetIsDir) { #if defined(SYMBOLIC_LINK_FLAG_DIRECTORY) @@ -1705,23 +1711,30 @@ assertion_is_reg(const char *file, int line, const char *pathname, int mode) return (1); } -/* Check whether 'pathname' is a symbolic link. If 'contents' is - * non-NULL, verify that the symlink has those contents. */ +/* + * Check whether 'pathname' is a symbolic link. If 'contents' is + * non-NULL, verify that the symlink has those contents. + * + * On platforms with directory symlinks, set isdir to 0 to test for a file + * symlink and to 1 to test for a directory symlink. On other platforms + * the variable is ignored. + */ static int is_symlink(const char *file, int line, - const char *pathname, const char *contents) + const char *pathname, const char *contents, int isdir) { #if defined(_WIN32) && !defined(__CYGWIN__) HANDLE h; DWORD inbytes; REPARSE_DATA_BUFFER *buf; + BY_HANDLE_FILE_INFORMATION st; size_t len, len2; wchar_t *linknamew, *contentsw; const char *p; char *s, *pn; int ret = 0; BYTE *indata; - DWORD flag = FILE_FLAG_BACKUP_SEMANTICS | + static DWORD flag = FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT; /* Replace slashes with backslashes in pathname */ @@ -1741,8 +1754,39 @@ is_symlink(const char *file, int line, h = CreateFileA(pn, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, flag, NULL); free(pn); - if (h == INVALID_HANDLE_VALUE) + if (h == INVALID_HANDLE_VALUE) { + failure_start(file, line, "Can't access %s\n", pathname); + failure_finish(NULL); return (0); + } + ret = GetFileInformationByHandle(h, &st); + if (ret == 0) { + failure_start(file, line, + "Can't stat: %s", pathname); + failure_finish(NULL); + } else if ((st.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) { + failure_start(file, line, + "Not a symlink: %s", pathname); + failure_finish(NULL); + ret = 0; + } + if (isdir && ((st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)) { + failure_start(file, line, + "Not a directory symlink: %s", pathname); + failure_finish(NULL); + ret = 0; + } + if (!isdir && + ((st.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)) { + failure_start(file, line, + "Not a file symlink: %s", pathname); + failure_finish(NULL); + ret = 0; + } + if (ret == 0) { + CloseHandle(h); + return (0); + } indata = malloc(MAXIMUM_REPARSE_DATA_BUFFER_SIZE); ret = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, indata, @@ -1750,6 +1794,9 @@ is_symlink(const char *file, int line, CloseHandle(h); if (ret == 0) { free(indata); + failure_start(file, line, + "Could not retrieve symlink target: %s", pathname); + failure_finish(NULL); return (0); } @@ -1757,7 +1804,9 @@ is_symlink(const char *file, int line, if (buf->ReparseTag != IO_REPARSE_TAG_SYMLINK) { free(indata); /* File is not a symbolic link */ - errno = EINVAL; + failure_start(file, line, + "Not a symlink: %s", pathname); + failure_finish(NULL); return (0); } @@ -1801,6 +1850,7 @@ is_symlink(const char *file, int line, ssize_t linklen; int r; + (void)isdir; /* UNUSED */ assertion_count(file, line); r = lstat(pathname, &st); if (r != 0) { @@ -1829,9 +1879,9 @@ is_symlink(const char *file, int line, /* Assert that path is a symlink that (optionally) contains contents. */ int assertion_is_symlink(const char *file, int line, - const char *path, const char *contents) + const char *path, const char *contents, int isdir) { - if (is_symlink(file, line, path, contents)) + if (is_symlink(file, line, path, contents, isdir)) return (1); if (contents) failure_start(file, line, "File %s is not a symlink to %s", @@ -2405,10 +2455,12 @@ canSymlink(void) * use the Win32 CreateSymbolicLink() function. */ #if defined(_WIN32) && !defined(__CYGWIN__) value = my_CreateSymbolicLinkA("canSymlink.1", "canSymlink.0", 0) - && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0"); + && is_symlink(__FILE__, __LINE__, "canSymlink.1", "canSymlink.0", + 0); #elif HAVE_SYMLINK value = (0 == symlink("canSymlink.0", "canSymlink.1")) - && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0"); + && is_symlink(__FILE__, __LINE__, "canSymlink.1","canSymlink.0", + 0); #endif return (value); } -- cgit v1.2.1