From ece28103885a079a129a23c5001252a1648517af Mon Sep 17 00:00:00 2001 From: Martin Matuska Date: Tue, 29 Nov 2016 16:55:41 +0100 Subject: [PATCH 2/2] Fix extracting hardlinks over symlinks Closes #821 Upstream-Status: Backported Signed-off-by: Amarnath Valluri --- libarchive/archive_write_disk_posix.c | 43 +++++++++++++++++++++++++++++++++++ tar/test/test_symlink_dir.c | 18 ++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c index d786bc2..80b03cd 100644 --- a/libarchive/archive_write_disk_posix.c +++ b/libarchive/archive_write_disk_posix.c @@ -2563,6 +2563,49 @@ check_symlinks_fsobj(char *path, int *a_eno, struct archive_string *a_estr, int 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, diff --git a/tar/test/test_symlink_dir.c b/tar/test/test_symlink_dir.c index 25bd8b1..852e00b 100644 --- a/tar/test/test_symlink_dir.c +++ b/tar/test/test_symlink_dir.c @@ -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"); } -- 2.7.4