| From ece28103885a079a129a23c5001252a1648517af Mon Sep 17 00:00:00 2001 |
| From: Martin Matuska <martin@matuska.org> |
| 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 <amarnath.valluri@intel.com> |
| --- |
| 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 |
| |