]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Fix extracting hardlinks over symlinks
authorMartin Matuska <martin@matuska.org>
Tue, 29 Nov 2016 15:55:41 +0000 (16:55 +0100)
committerMartin Matuska <martin@matuska.org>
Tue, 29 Nov 2016 21:34:07 +0000 (22:34 +0100)
Closes #821

libarchive/archive_write_disk_posix.c
tar/test/test_symlink_dir.c

index b717ae0dcc3292787cb8dadd0f40adc00b5f7ce0..afcadbbe2571780d5c0519de493bb606d2e29580 100644 (file)
@@ -2590,6 +2590,49 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr,
                                        break;
                                }
                                tail[0] = c;
+                       } else if ((flags &
+                           ARCHIVE_EXTRACT_SECURE_SYMLINKS) == 0) {
+                               /*
+                                * We are not the last element and we want to
+                                * follow symlinks if they are a directory.
+                                * 
+                                * This is needed to extract hardlinks over
+                                * symlinks.
+                                */
+                               r = stat(head, &st);
+                               if (r != 0) {
+                                       tail[0] = c;
+                                       if (errno == ENOENT) {
+                                               break;
+                                       } else {
+                                               fsobj_error(a_eno, a_estr,
+                                                   errno,
+                                                   "Could not stat %s", path);
+                                               res = (ARCHIVE_FAILED);
+                                               break;
+                                       }
+                               } else if (S_ISDIR(st.st_mode)) {
+                                       if (chdir(head) != 0) {
+                                               tail[0] = c;
+                                               fsobj_error(a_eno, a_estr,
+                                                   errno,
+                                                   "Could not chdir %s", path);
+                                               res = (ARCHIVE_FATAL);
+                                               break;
+                                       }
+                                       /*
+                                        * Our view is now from inside
+                                        * this dir:
+                                        */
+                                       head = tail + 1;
+                               } else {
+                                       tail[0] = c;
+                                       fsobj_error(a_eno, a_estr, 0,
+                                           "Cannot extract through "
+                                           "symlink %s", path);
+                                       res = ARCHIVE_FAILED;
+                                       break;
+                               }
                        } else {
                                tail[0] = c;
                                fsobj_error(a_eno, a_estr, 0,
index 25bd8b162a161097ee68bd76815fbbc1d60e4394..852e00b37c64c6d1f403dd712b491e5fc99f4460 100644 (file)
@@ -47,11 +47,18 @@ DEFINE_TEST(test_symlink_dir)
        assertMakeDir("source/dir3", 0755);
        assertMakeDir("source/dir3/d3", 0755);
        assertMakeFile("source/dir3/f3", 0755, "abcde");
+       assertMakeDir("source/dir4", 0755);
+       assertMakeFile("source/dir4/file3", 0755, "abcdef");
+       assertMakeHardlink("source/dir4/file4", "source/dir4/file3");
 
        assertEqualInt(0,
            systemf("%s -cf test.tar -C source dir dir2 dir3 file file2",
                testprog));
 
+       /* Second archive with hardlinks */
+       assertEqualInt(0,
+           systemf("%s -cf test2.tar -C source dir4", testprog));
+
        /*
         * Extract with -x and without -P.
         */
@@ -118,9 +125,15 @@ DEFINE_TEST(test_symlink_dir)
                assertMakeSymlink("dest2/file2", "real_file2");
        assertEqualInt(0, systemf("%s -xPf test.tar -C dest2", testprog));
 
-       /* dest2/dir symlink should be followed */
+       /* "dir4" is a symlink to existing "real_dir" */
+       if (canSymlink())
+               assertMakeSymlink("dest2/dir4", "real_dir");
+       assertEqualInt(0, systemf("%s -xPf test2.tar -C dest2", testprog));
+
+       /* dest2/dir and dest2/dir4 symlinks should be followed */
        if (canSymlink()) {
                assertIsSymlink("dest2/dir", "real_dir");
+               assertIsSymlink("dest2/dir4", "real_dir");
                assertIsDir("dest2/real_dir", -1);
        }
 
@@ -141,4 +154,7 @@ DEFINE_TEST(test_symlink_dir)
        /* dest2/file2 symlink should be removed */
        failure("Symlink to non-existing file should be removed");
        assertIsReg("dest2/file2", -1);
+
+       /* dest2/dir4/file3 and dest2/dir4/file4 should be hard links */
+       assertIsHardlink("dest2/dir4/file3", "dest2/dir4/file4");
 }