summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2010-04-09 08:52:52 (GMT)
committer Denys Vlasenko <vda.linux@googlemail.com>2010-04-09 08:52:52 (GMT)
commit02365a6ef73defb8689d3ed5228125d72993dec9 (patch)
tree26358e718c5232ee44ad750f1b386f0f4138512a
parent823b636cd14d337ebb8766c5c181737fb3860b42 (diff)
downloadbusybox-02365a6ef73defb8689d3ed5228125d72993dec9.tar.gz
busybox-02365a6ef73defb8689d3ed5228125d72993dec9.tar.bz2
tar: fix mishandling of repeated hardlink in tarball; expand tests
function old new delta data_extract_all 727 767 +40 get_header_tar 1576 1572 -4 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--archival/libunarchive/data_extract_all.c32
-rw-r--r--archival/libunarchive/get_header_tar.c5
-rwxr-xr-xtestsuite/tar.tests52
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