[git commit master] tar: fix mishandling of repeated hardlink in tarball; expand tests

Denys Vlasenko vda.linux at googlemail.com
Fri Apr 9 08:52:52 UTC 2010


commit: http://git.busybox.net/busybox/commit/?id=02365a6ef73defb8689d3ed5228125d72993dec9
branch: http://git.busybox.net/busybox/commit/?id=refs/heads/master

function                                             old     new   delta
data_extract_all                                     727     767     +40
get_header_tar                                      1576    1572      -4

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 archival/libunarchive/data_extract_all.c |   32 ++++++++++++++----
 archival/libunarchive/get_header_tar.c   |    5 ++-
 testsuite/tar.tests                      |   52 ++++++++++++++++++++++++++----
 3 files changed, 73 insertions(+), 16 deletions(-)

diff --git a/archival/libunarchive/data_extract_all.c b/archival/libunarchive/data_extract_all.c
index cc48942..de2367a 100644
--- a/archival/libunarchive/data_extract_all.c
+++ b/archival/libunarchive/data_extract_all.c
@@ -34,12 +34,30 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
 
 	if (archive_handle->ah_flags & ARCHIVE_UNLINK_OLD) {
 		/* Remove the entry if it exists */
-		if ((!S_ISDIR(file_header->mode))
-		 && (unlink(file_header->name) == -1)
-		 && (errno != ENOENT)
-		) {
-			bb_perror_msg_and_die("can't remove old file %s",
-					file_header->name);
+		if (!S_ISDIR(file_header->mode)) {
+			/* Is it hardlink?
+			 * We encode hard links as regular files of size 0 with a symlink */
+			if (S_ISREG(file_header->mode)
+			 && file_header->link_target
+			 && file_header->size == 0
+			) {
+				/* Ugly special case:
+				 * tar cf t.tar hardlink1 hardlink2 hardlink1
+				 * results in this tarball structure:
+				 * hardlink1
+				 * hardlink2 -> hardlink1
+				 * hardlink1 -> hardlink1 <== !!!
+				 */
+				if (strcmp(file_header->link_target, file_header->name) == 0)
+					goto ret;
+			}
+			/* Proceed with deleting */
+			if (unlink(file_header->name) == -1
+			 && errno != ENOENT
+			) {
+				bb_perror_msg_and_die("can't remove old file %s",
+						file_header->name);
+			}
 		}
 	}
 	else if (archive_handle->ah_flags & ARCHIVE_EXTRACT_NEWER) {
@@ -65,7 +83,7 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
 	}
 
 	/* Handle hard links separately
-	 * We identified hard links as regular files of size 0 with a symlink */
+	 * We encode hard links as regular files of size 0 with a symlink */
 	if (S_ISREG(file_header->mode)
 	 && file_header->link_target
 	 && file_header->size == 0
diff --git a/archival/libunarchive/get_header_tar.c b/archival/libunarchive/get_header_tar.c
index cf0b9ab..adb4c15 100644
--- a/archival/libunarchive/get_header_tar.c
+++ b/archival/libunarchive/get_header_tar.c
@@ -507,8 +507,9 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
 		archive_handle->action_header(/*archive_handle->*/ file_header);
 		/* Note that we kill the '/' only after action_header() */
 		/* (like GNU tar 1.15.1: verbose mode outputs "dir/dir/") */
-		if (cp) *cp = '\0';
-		archive_handle->ah_flags |= ARCHIVE_EXTRACT_QUIET;
+		if (cp)
+			*cp = '\0';
+		//archive_handle->ah_flags |= ARCHIVE_EXTRACT_QUIET; // why??
 		archive_handle->action_data(archive_handle);
 		llist_add_to(&(archive_handle->passed), file_header->name);
 	} else {
diff --git a/testsuite/tar.tests b/testsuite/tar.tests
index 71095cb..dd8f110 100755
--- a/testsuite/tar.tests
+++ b/testsuite/tar.tests
@@ -4,20 +4,25 @@
 
 . ./testing.sh
 
-mkdir tempdir && cd tempdir || exit 1
+rm -rf tar.tempdir 2>/dev/null
+mkdir tar.tempdir && cd tar.tempdir || exit 1
 
 # testing "test name" "script" "expected result" "file input" "stdin"
 
-testing "tar hardlinks and repeated files" "\
+testing "tar hardlinks and repeated files" '\
 rm -rf input_* test.tar 2>/dev/null
 >input_hard1
 ln input_hard1 input_hard2
 mkdir input_dir
 >input_dir/file
+chmod -R 644 *
+chmod    755 input_dir
 tar cf test.tar input input_dir/ input_hard1 input_hard2 input_hard1 input_dir/ input
-tar tvf test.tar | sed 's/.*[0-9] input/input/'
-tar xf test.tar 2>&1 && echo Ok
-" "\
+tar tvf test.tar | sed "s/.*[0-9] input/input/"
+tar xf test.tar 2>&1
+echo Ok: $?
+ls -l . input_dir/* | grep input_ | sed "s/\\(^[^ ]*\\) .* input/\\1 input/"
+' "\
 input
 input_dir/
 input_dir/file
@@ -27,7 +32,40 @@ input_hard1 -> input_hard1
 input_dir/
 input_dir/file
 input
-Ok
+Ok: 0
+-rw-r--r-- input_dir/file
+drwxr-xr-x input_dir
+-rw-r--r-- input_hard1
+-rw-r--r-- input_hard2
+" \
+"" ""
+
+testing "tar hardlinks mode" '\
+rm -rf input_* test.tar 2>/dev/null
+>input_hard1
+chmod 741 input_hard1
+ln input_hard1 input_hard2
+mkdir input_dir
+chmod 550 input_dir
+ln input_hard1 input_dir
+ln input_hard2 input_dir
+tar cf test.tar input_*
+tar tvf test.tar | sed "s/.*[0-9] input/input/"
+tar xf test.tar 2>&1
+echo Ok: $?
+ls -l . input_dir/* | grep input_ | sed "s/\\(^[^ ]*\\) .* input/\\1 input/"
+' "\
+input_dir/
+input_dir/input_hard1
+input_dir/input_hard2 -> input_dir/input_hard1
+input_hard1 -> input_dir/input_hard1
+input_hard2 -> input_dir/input_hard1
+Ok: 0
+-rwxr----x input_dir/input_hard1
+-rwxr----x input_dir/input_hard2
+dr-xr-x--- input_dir
+-rwxr----x input_hard1
+-rwxr----x input_hard2
 " \
 "" ""
 
@@ -46,6 +84,6 @@ Ok
 "Ok\n" ""
 SKIP=
 
-cd .. && rm -rf tempdir || exit 1
+cd .. && rm -rf tar.tempdir || exit 1
 
 exit $FAILCOUNT
-- 
1.6.3.3



More information about the busybox-cvs mailing list