]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Fix various symlink bugs with Windows symlinks
authorMartin Matuska <martin@matuska.org>
Wed, 3 Apr 2019 21:13:35 +0000 (23:13 +0200)
committerMartin Matuska <martin@matuska.org>
Wed, 3 Apr 2019 22:11:32 +0000 (00:11 +0200)
tests: extend assertMakeSymlink with targetIsDir

16 files changed:
cpio/test/test_basic.c
cpio/test/test_format_newc.c
cpio/test/test_option_L_upper.c
cpio/test/test_option_c.c
libarchive/archive_write_disk_windows.c
libarchive/test/test_read_disk_directory_traversals.c
tar/test/test_basic.c
tar/test/test_copy.c
tar/test/test_option_H_upper.c
tar/test/test_option_L_upper.c
tar/test/test_option_U_upper.c
tar/test/test_option_s.c
tar/test/test_strip_components.c
tar/test/test_symlink_dir.c
test_utils/test_common.h
test_utils/test_main.c

index 6e45d18569831a8ebf071b92cf087240950f2d6d..855793fbb054d8e810212fb280059f72bff587c5 100644 (file)
@@ -173,7 +173,7 @@ DEFINE_TEST(test_basic)
 
        /* Symlink to above file. */
        if (canSymlink()) {
-               assertMakeSymlink("symlink", "file");
+               assertMakeSymlink("symlink", "file", 0);
                fprintf(filelist, "symlink\n");
                if (is_LargeInode("symlink")) {
                        strncat(result,
index 258640443fe6121e0b278b3809a12aca27e55c00..2c6694267646da229c60c69ea70e29b7850d9c86 100644 (file)
@@ -114,7 +114,7 @@ DEFINE_TEST(test_format_newc)
 
        /* "symlink" */
        if (canSymlink()) {
-               assertMakeSymlink("symlink", "file1");
+               assertMakeSymlink("symlink", "file1", 0);
                fprintf(list, "symlink\n");
        }
 
index 1774343f6732da432f9f7026f4a84fc40e1be784..7fd10cefc1483c3fd511fb85746227a91210c780 100644 (file)
@@ -51,7 +51,7 @@ DEFINE_TEST(test_option_L_upper)
        fprintf(filelist, "file\n");
 
        /* Symlink to above file. */
-       assertMakeSymlink("symlink", "file");
+       assertMakeSymlink("symlink", "file", 0);
        fprintf(filelist, "symlink\n");
 
        fclose(filelist);
index fa47b7e277d8a2b1bf59d1f10aa458b4f1c2aac5..013caed560307331491d76c4ff10c35dca3a9876 100644 (file)
@@ -85,7 +85,7 @@ DEFINE_TEST(test_option_c)
 
        /* "symlink" */
        if (canSymlink()) {
-               assertMakeSymlink("symlink", "file");
+               assertMakeSymlink("symlink", "file", 0);
                fprintf(filelist, "symlink\n");
        }
 
index 1155c3135c59e35197f4c11bb7b9df239ef49e1d..811e7772c3529d747d5cd91198b55ec01767bd8a 100644 (file)
@@ -1454,28 +1454,45 @@ restore_entry(struct archive_write_disk *a)
                en = create_filesystem_object(a);
        } else if (en == EEXIST) {
                mode_t st_mode;
+               mode_t lst_mode;
+               BY_HANDLE_FILE_INFORMATION lst;
                /*
                 * We know something is in the way, but we don't know what;
                 * we need to find out before we go any further.
                 */
                int r = 0;
+               int dirlnk = 0;
+
                /*
                 * The SECURE_SYMLINK logic has already removed a
                 * symlink to a dir if the client wants that.  So
                 * follow the symlink if we're creating a dir.
-                */
-               if (S_ISDIR(a->mode))
-                       r = file_information(a, a->name, &a->st, &st_mode, 0);
-               /*
                 * If it's not a dir (or it's a broken symlink),
                 * then don't follow it.
+                *
+                * Windows distinguishes file and directory symlinks.
+                * A file symlink may erroneously point to a directory
+                * and a directory symlink to a file. Windows does not follow
+                * such symlinks. We always need both source and target
+                * information.
                 */
-               if (r != 0 || !S_ISDIR(a->mode))
-                       r = file_information(a, a->name, &a->st, &st_mode, 1);
+               r = file_information(a, a->name, &lst, &lst_mode, 1);
                if (r != 0) {
                        archive_set_error(&a->archive, errno,
                            "Can't stat existing object");
                        return (ARCHIVE_FAILED);
+               } else if (S_ISLNK(lst_mode)) {
+                       if (lst.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+                               dirlnk = 1;
+                       /* In case of a symlink we need target information */
+                       r = file_information(a, a->name, &a->st, &st_mode, 0);
+                       if (r != 0) {
+                               a->st = lst;
+                               st_mode = lst_mode;
+                       }
+               } else {
+                       a->st = lst;
+                       st_mode = lst_mode;
                }
 
                /*
@@ -1499,8 +1516,15 @@ restore_entry(struct archive_write_disk *a)
                }
 
                if (!S_ISDIR(st_mode)) {
-                       /* A non-dir is in the way, unlink it. */
-                       if (disk_unlink(a->name) != 0) {
+                       /* Edge case: a directory symlink pointing to a file */
+                       if (dirlnk) {
+                               if (disk_rmdir(a->name) != 0) {
+                                       archive_set_error(&a->archive, errno,
+                                           "Can't unlink directory symlink");
+                                       return (ARCHIVE_FAILED);
+                               }
+                       } else if (disk_unlink(a->name) != 0) {
+                               /* A non-dir is in the way, unlink it. */
                                archive_set_error(&a->archive, errno,
                                    "Can't unlink already-existing object");
                                return (ARCHIVE_FAILED);
@@ -1927,6 +1951,9 @@ check_symlinks(struct archive_write_disk *a)
        p = a->path_safe.s;
        while ((*pn != '\0') && (*p == *pn))
                ++p, ++pn;
+       /* Skip leading backslashes */
+       while (*pn == '\\')
+               ++pn;
        c = pn[0];
        /* Keep going until we've checked the entire name. */
        while (pn[0] != '\0' && (pn[0] != '\\' || pn[1] != '\0')) {
@@ -1944,8 +1971,8 @@ check_symlinks(struct archive_write_disk *a)
                } else if (S_ISLNK(st_mode)) {
                        if (c == '\0') {
                                /*
-                                * Last element is symlink; remove it
-                                * so we can overwrite it with the
+                                * Last element is a file or directory symlink.
+                                * Remove it so we can overwrite it with the
                                 * item being extracted.
                                 */
                                if (st.dwFileAttributes &
@@ -1978,7 +2005,13 @@ check_symlinks(struct archive_write_disk *a)
                                return (0);
                        } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) {
                                /* User asked us to remove problems. */
-                               if (disk_unlink(a->name) != 0) {
+                               if (st.dwFileAttributes &
+                                   FILE_ATTRIBUTE_DIRECTORY) {
+                                       r = disk_rmdir(a->name);
+                               } else {
+                                       r = disk_unlink(a->name);
+                               }
+                               if (r != 0) {
                                        archive_set_error(&a->archive, 0,
                                            "Cannot remove intervening "
                                            "symlink %ls", a->name);
@@ -1994,6 +2027,8 @@ check_symlinks(struct archive_write_disk *a)
                                return (ARCHIVE_FAILED);
                        }
                }
+               pn[0] = c;
+               pn++;
        }
        pn[0] = c;
        /* We've checked and/or cleaned the whole path, so remember it. */
index 82579b23c22d49b8dddb040905bca1e21ae0163e..1e41c3caa333da83f04903808c87d82069529636 100644 (file)
@@ -570,13 +570,13 @@ test_symlink_hybrid(void)
        assertMakeDir("h", 0755);
        assertChdir("h");
        assertMakeDir("d1", 0755);
-       assertMakeSymlink("ld1", "d1/");
+       assertMakeSymlink("ld1", "d1", 1);
        assertMakeFile("d1/file1", 0644, "d1/file1");
        assertMakeFile("d1/file2", 0644, "d1/file2");
-       assertMakeSymlink("d1/link1", "file1");
-       assertMakeSymlink("d1/linkX", "fileX");
-       assertMakeSymlink("link2", "d1/file2");
-       assertMakeSymlink("linkY", "d1/fileY");
+       assertMakeSymlink("d1/link1", "file1", 0);
+       assertMakeSymlink("d1/linkX", "fileX", 0);
+       assertMakeSymlink("link2", "d1/file2", 0);
+       assertMakeSymlink("linkY", "d1/fileY", 0);
        assertChdir("..");
 
        assert((ae = archive_entry_new()) != NULL);
@@ -727,13 +727,13 @@ test_symlink_logical(void)
        assertMakeDir("l", 0755);
        assertChdir("l");
        assertMakeDir("d1", 0755);
-       assertMakeSymlink("ld1", "d1/");
+       assertMakeSymlink("ld1", "d1", 1);
        assertMakeFile("d1/file1", 0644, "d1/file1");
        assertMakeFile("d1/file2", 0644, "d1/file2");
-       assertMakeSymlink("d1/link1", "file1");
-       assertMakeSymlink("d1/linkX", "fileX");
-       assertMakeSymlink("link2", "d1/file2");
-       assertMakeSymlink("linkY", "d1/fileY");
+       assertMakeSymlink("d1/link1", "file1", 0);
+       assertMakeSymlink("d1/linkX", "fileX", 0);
+       assertMakeSymlink("link2", "d1/file2", 0);
+       assertMakeSymlink("linkY", "d1/fileY", 0);
        assertChdir("..");
 
        /* Note: this test uses archive_read_next_header()
@@ -961,8 +961,8 @@ test_symlink_logical_loop(void)
        assertMakeDir("d1/d2/d3", 0755);
        assertMakeDir("d2", 0755);
        assertMakeFile("d2/file1", 0644, "d2/file1");
-       assertMakeSymlink("d1/d2/ld1", "../../d1/");
-       assertMakeSymlink("d1/d2/ld2", "../../d2/");
+       assertMakeSymlink("d1/d2/ld1", "../../d1", 1);
+       assertMakeSymlink("d1/d2/ld2", "../../d2", 1);
        assertChdir("..");
 
        assert((ae = archive_entry_new()) != NULL);
index 0008e1cfb70dab6e4156d2b3ea98357330c2476c..91282cde9a30de8a97bdf2c88ab0782dbd41cf67 100644 (file)
@@ -42,7 +42,7 @@ make_files(void)
 
        /* Symlink to above file. */
        if (canSymlink())
-               assertMakeSymlink("symlink", "file");
+               assertMakeSymlink("symlink", "file", 0);
 
        /* Directory. */
        assertMakeDir("dir", 0775);
index e6e31f45228560d2ebca7d21fbd11cb17988ba12..1e59e1929fbc4d5c3d78376ab8565fe4ed2b9ea3 100644 (file)
@@ -176,7 +176,7 @@ create_tree(void)
                        sprintf(buff, "s/%s", filenames[i]);
                        sprintf(buff2, "../f/%s", filenames[i]);
                        failure("buff=\"%s\" buff2=\"%s\"", buff, buff2);
-                       assertMakeSymlink(buff, buff2);
+                       assertMakeSymlink(buff, buff2, 0);
                }
                /* Create a dir named "d/abcdef...". */
                buff[0] = 'd';
index adc294b55b260958d0df56716605dd3b56599455..7c201ce26270ffc905e71d20fe3ce4a4fe6e6ff0 100644 (file)
@@ -39,13 +39,13 @@ DEFINE_TEST(test_option_H_upper)
        assertMakeDir("in", 0755);
        assertChdir("in");
        assertMakeDir("d1", 0755);
-       assertMakeSymlink("ld1", "d1");
+       assertMakeSymlink("ld1", "d1", 1);
        assertMakeFile("d1/file1", 0644, "d1/file1");
        assertMakeFile("d1/file2", 0644, "d1/file2");
-       assertMakeSymlink("d1/link1", "file1");
-       assertMakeSymlink("d1/linkX", "fileX");
-       assertMakeSymlink("link2", "d1/file2");
-       assertMakeSymlink("linkY", "d1/fileY");
+       assertMakeSymlink("d1/link1", "file1", 0);
+       assertMakeSymlink("d1/linkX", "fileX", 0);
+       assertMakeSymlink("link2", "d1/file2", 0);
+       assertMakeSymlink("linkY", "d1/fileY", 0);
        assertChdir("..");
 
        /* Test 1: Without -H */
index f5a3c5ab4cba6278d29f69f1ed90fa04d56dd040..83f69d085bdd5678de07ddaacc02181eb024cc83 100644 (file)
@@ -39,13 +39,13 @@ DEFINE_TEST(test_option_L_upper)
        assertMakeDir("in", 0755);
        assertChdir("in");
        assertMakeDir("d1", 0755);
-       assertMakeSymlink("ld1", "d1");
+       assertMakeSymlink("ld1", "d1", 1);
        assertMakeFile("d1/file1", 0644, "d1/file1");
        assertMakeFile("d1/file2", 0644, "d1/file2");
-       assertMakeSymlink("d1/link1", "file1");
-       assertMakeSymlink("d1/linkX", "fileX");
-       assertMakeSymlink("link2", "d1/file2");
-       assertMakeSymlink("linkY", "d1/fileY");
+       assertMakeSymlink("d1/link1", "file1", 0);
+       assertMakeSymlink("d1/linkX", "fileX", 0);
+       assertMakeSymlink("link2", "d1/file2", 0);
+       assertMakeSymlink("linkY", "d1/fileY", 0);
        assertChdir("..");
 
        /* Test 1: Without -L */
index 2c43e002df5beb33ff9c6dfddd17d82d0e469c0f..832054860f17720e4b01154ace902741064a17e7 100644 (file)
@@ -79,7 +79,7 @@ DEFINE_TEST(test_option_U_upper)
        assertMakeDir("test3", 0755);
        assertChdir("test3");
        assertMakeDir("realDir", 0755);
-       assertMakeSymlink("d1", "realDir");
+       assertMakeSymlink("d1", "realDir", 1);
        r = systemf("%s -xf ../archive.tar d1/file1 >test.out 2>test.err", testprog);
        assert(r != 0);
        assertIsSymlink("d1", "realDir");
@@ -92,7 +92,7 @@ DEFINE_TEST(test_option_U_upper)
        assertMakeDir("test4", 0755);
        assertChdir("test4");
        assertMakeDir("realDir", 0755);
-       assertMakeSymlink("d1", "realDir");
+       assertMakeSymlink("d1", "realDir", 1);
        assertEqualInt(0,
            systemf("%s -xUf ../archive.tar >test.out 2>test.err", testprog));
        assertIsDir("d1", -1);
@@ -105,7 +105,7 @@ DEFINE_TEST(test_option_U_upper)
        assertMakeDir("test5", 0755);
        assertChdir("test5");
        assertMakeDir("realDir", 0755);
-       assertMakeSymlink("d1", "realDir");
+       assertMakeSymlink("d1", "realDir", 1);
        assertEqualInt(0,
            systemf("%s -xPf ../archive.tar d1/file1 >test.out 2>test.err", testprog));
        assertIsSymlink("d1", "realDir");
@@ -118,7 +118,7 @@ DEFINE_TEST(test_option_U_upper)
        assertMakeDir("test6", 0755);
        assertChdir("test6");
        assertMakeDir("realDir", 0755);
-       assertMakeSymlink("d1", "realDir");
+       assertMakeSymlink("d1", "realDir", 1);
        assertEqualInt(0,
            systemf("%s -xPUf ../archive.tar d1/file1 >test.out 2>test.err", testprog));
        assertIsSymlink("d1", "realDir");
@@ -132,7 +132,7 @@ DEFINE_TEST(test_option_U_upper)
        assertChdir("test7");
        assertMakeDir("d1", 0755);
        assertMakeFile("d1/realfile1", 0644, "realfile1");
-       assertMakeSymlink("d1/file1", "d1/realfile1");
+       assertMakeSymlink("d1/file1", "d1/realfile1", 0);
        assertEqualInt(0,
            systemf("%s -xf ../archive.tar d1/file1 >test.out 2>test.err", testprog));
        assertIsReg("d1/file1", umasked(0644));
@@ -147,7 +147,7 @@ DEFINE_TEST(test_option_U_upper)
        assertChdir("test8");
        assertMakeDir("d1", 0755);
        assertMakeFile("d1/realfile1", 0644, "realfile1");
-       assertMakeSymlink("d1/file1", "d1/realfile1");
+       assertMakeSymlink("d1/file1", "d1/realfile1", 0);
        assertEqualInt(0,
            systemf("%s -xPUf ../archive.tar d1/file1 >test.out 2>test.err", testprog));
        assertIsReg("d1/file1", umasked(0644));
index ee8332f34fd5873fc7ed0ef541d3dd90a3b5ea92..a08966068b803422728b7a472dfdd0580b2f8eb8 100644 (file)
@@ -36,7 +36,7 @@ DEFINE_TEST(test_option_s)
        assertMakeFile("in/d1/bar", 0644, "bar");
        if (canSymlink()) {
                assertMakeFile("in/d1/realfile", 0644, "realfile");
-               assertMakeSymlink("in/d1/symlink", "realfile");
+               assertMakeSymlink("in/d1/symlink", "realfile", 0);
        }
        assertMakeFile("in/d1/hardlink1", 0644, "hardlinkedfile");
        assertMakeHardlink("in/d1/hardlink2", "in/d1/hardlink1");
index d195af1b3526f4f40a246fa50601e7392f82676e..e1d71ddc525fe9b0651798f6300869b452dc4a3a 100644 (file)
@@ -36,8 +36,8 @@ DEFINE_TEST(test_strip_components)
        assertMakeHardlink("l1", "d1/d2/f1");
        assertMakeHardlink("d1/l2", "d1/d2/f1");
        if (canSymlink()) {
-               assertMakeSymlink("s1", "d1/d2/f1");
-               assertMakeSymlink("d1/s2", "d2/f1");
+               assertMakeSymlink("s1", "d1/d2/f1", 0);
+               assertMakeSymlink("d1/s2", "d2/f1", 0);
        }
        assertChdir("..");
 
index 852e00b37c64c6d1f403dd712b491e5fc99f4460..485ab32f847162d944358da1a9fe8ad327c88c8f 100644 (file)
@@ -66,22 +66,22 @@ DEFINE_TEST(test_symlink_dir)
        /* "dir" is a symlink to an existing "dest1/real_dir" */
        assertMakeDir("dest1/real_dir", 0755);
        if (canSymlink()) {
-               assertMakeSymlink("dest1/dir", "real_dir");
+               assertMakeSymlink("dest1/dir", "real_dir", 1);
                /* "dir2" is a symlink to a non-existing "real_dir2" */
-               assertMakeSymlink("dest1/dir2", "real_dir2");
+               assertMakeSymlink("dest1/dir2", "real_dir2", 1);
        } else {
                skipping("Symlinks are not supported on this platform");
        }
        /* "dir3" is a symlink to an existing "non_dir3" */
        assertMakeFile("dest1/non_dir3", 0755, "abcdef");
        if (canSymlink())
-               assertMakeSymlink("dest1/dir3", "non_dir3");
+               assertMakeSymlink("dest1/dir3", "non_dir3", 1);
        /* "file" is a symlink to existing "real_file" */
        assertMakeFile("dest1/real_file", 0755, "abcdefg");
        if (canSymlink()) {
-               assertMakeSymlink("dest1/file", "real_file");
+               assertMakeSymlink("dest1/file", "real_file", 0);
                /* "file2" is a symlink to non-existing "real_file2" */
-               assertMakeSymlink("dest1/file2", "real_file2");
+               assertMakeSymlink("dest1/file2", "real_file2", 0);
        }
        assertEqualInt(0, systemf("%s -xf test.tar -C dest1", testprog));
 
@@ -108,26 +108,26 @@ DEFINE_TEST(test_symlink_dir)
        /* "dir" is a symlink to existing "real_dir" */
        assertMakeDir("dest2/real_dir", 0755);
        if (canSymlink())
-               assertMakeSymlink("dest2/dir", "real_dir");
+               assertMakeSymlink("dest2/dir", "real_dir", 1);
        /* "dir2" is a symlink to a non-existing "real_dir2" */
        if (canSymlink())
-               assertMakeSymlink("dest2/dir2", "real_dir2");
+               assertMakeSymlink("dest2/dir2", "real_dir2", 1);
        /* "dir3" is a symlink to an existing "non_dir3" */
        assertMakeFile("dest2/non_dir3", 0755, "abcdefgh");
        if (canSymlink())
-               assertMakeSymlink("dest2/dir3", "non_dir3");
+               assertMakeSymlink("dest2/dir3", "non_dir3", 1);
        /* "file" is a symlink to existing "real_file" */
        assertMakeFile("dest2/real_file", 0755, "abcdefghi");
        if (canSymlink())
-               assertMakeSymlink("dest2/file", "real_file");
+               assertMakeSymlink("dest2/file", "real_file", 0);
        /* "file2" is a symlink to non-existing "real_file2" */
        if (canSymlink())
-               assertMakeSymlink("dest2/file2", "real_file2");
+               assertMakeSymlink("dest2/file2", "real_file2", 0);
        assertEqualInt(0, systemf("%s -xPf test.tar -C dest2", testprog));
 
        /* "dir4" is a symlink to existing "real_dir" */
        if (canSymlink())
-               assertMakeSymlink("dest2/dir4", "real_dir");
+               assertMakeSymlink("dest2/dir4", "real_dir", 1);
        assertEqualInt(0, systemf("%s -xPf test2.tar -C dest2", testprog));
 
        /* dest2/dir and dest2/dir4 symlinks should be followed */
index 4ebfdb0cd16754f5c76c7c37ebfcaec197d08b96..154a6964d1bbd3f060315c37af4dc92cf5eab50a 100644 (file)
   assertion_make_file(__FILE__, __LINE__, path, mode, csize, contents)
 #define assertMakeHardlink(newfile, oldfile)   \
   assertion_make_hardlink(__FILE__, __LINE__, newfile, oldfile)
-#define assertMakeSymlink(newfile, linkto)     \
-  assertion_make_symlink(__FILE__, __LINE__, newfile, linkto)
+#define assertMakeSymlink(newfile, linkto, targetIsDir)        \
+  assertion_make_symlink(__FILE__, __LINE__, newfile, linkto, targetIsDir)
 #define assertSetNodump(path)  \
   assertion_set_nodump(__FILE__, __LINE__, path)
 #define assertUmask(mask)      \
@@ -293,7 +293,7 @@ int assertion_is_symlink(const char *, int, const char *, const char *);
 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 *);
-int assertion_make_symlink(const char *, int, const char *newpath, const char *);
+int assertion_make_symlink(const char *, int, const char *newpath, const char *, int);
 int assertion_non_empty_file(const char *, int, const char *);
 int assertion_set_nodump(const char *, int, const char *);
 int assertion_text_file_contents(const char *, int, const char *buff, const char *f);
index 6f4dd3a1a376706a240f4846aa77e6cbd5466fe0..91effdf583e0ae50ba906921b844b1984228e4b9 100644 (file)
@@ -211,13 +211,15 @@ GetFunctionKernel32(const char *name)
 }
 
 static int
-my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags)
+my_CreateSymbolicLinkA(const char *linkname, const char *target,
+    int targetIsDir)
 {
        static BOOLEAN (WINAPI *f)(LPCSTR, LPCSTR, DWORD);
        DWORD attrs;
        static int set;
        int ret, tmpflags;
-       char *tgt, *p;
+       int flags = 0;
+       char *src, *tgt, *p;
        if (!set) {
                set = 1;
                f = GetFunctionKernel32("CreateSymbolicLinkA");
@@ -228,10 +230,26 @@ my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags)
        tgt = malloc(strlen(target) + 1);
        if (tgt == NULL)
                return (0);
+       src = malloc(strlen(linkname) + 1);
+       if (src == NULL) {
+               free(tgt);
+               return (0);
+       }
 
        /*
         * Translate slashes to backslashes
         */
+       p = src;
+       while(*linkname != '\0') {
+               if (*linkname == '/')
+                       *p = '\\';
+               else
+                       *p = *linkname;
+               linkname++;
+               p++;
+       }
+       *p = '\0';
+
        p = tgt;
        while(*target != '\0') {
                if (*target == '/')
@@ -247,19 +265,12 @@ my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags)
         * If the target equals ".", ".." or ends with a slash, it always
         * points to a directory. In this case we can set the directory flag.
         */
-       if (strcmp(tgt, ".") == 0 || strcmp(tgt, "..") == 0 ||
-               *(p - 1) == '\\') {
+       if (targetIsDir) {
 #if defined(SYMBOLIC_LINK_FLAG_DIRECTORY)
                flags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
 #else
                flags |= 0x1;
 #endif
-               /* Now we remove trailing backslashes */
-               p--;
-               while(*p == '\\') {
-                       *p = '\0';
-                       p--;
-               }
        }
 
 #if defined(SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)
@@ -278,14 +289,15 @@ my_CreateSymbolicLinkA(const char *linkname, const char *target, int flags)
                        DeleteFileA(linkname);
        }
 
-       ret = (*f)(linkname, tgt, tmpflags);
+       ret = (*f)(src, tgt, tmpflags);
        /*
         * Prior to Windows 10 the SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
         * is not undestood
         */
        if (!ret)
-               ret = (*f)(linkname, tgt, flags);
+               ret = (*f)(src, tgt, flags);
 
+       free(src);
        free(tgt);
        return (ret);
 }
@@ -1928,20 +1940,26 @@ assertion_make_hardlink(const char *file, int line,
        return(0);
 }
 
-/* Create a symlink and report any failures. */
+/*
+ * Create a symlink and report any failures.
+ *
+ * Windows symlinks need to know if the target is a directory.
+ */
 int
 assertion_make_symlink(const char *file, int line,
-    const char *newpath, const char *linkto)
+    const char *newpath, const char *linkto, int targetIsDir)
 {
 #if defined(_WIN32) && !defined(__CYGWIN__)
-       int targetIsDir = 0;  /* TODO: Fix this */
        assertion_count(file, line);
        if (my_CreateSymbolicLinkA(newpath, linkto, targetIsDir))
                return (1);
 #elif HAVE_SYMLINK
+       (void)targetIsDir; /* UNUSED */
        assertion_count(file, line);
        if (0 == symlink(linkto, newpath))
                return (1);
+#else
+       (void)targetIsDir; /* UNUSED */
 #endif
        failure_start(file, line, "Could not create symlink");
        logprintf("   New link: %s\n", newpath);