From 02365a6ef73defb8689d3ed5228125d72993dec9 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 9 Apr 2010 10:52:52 +0200 Subject: [PATCH] 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 --- 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 cc4894229..de2367ac2 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 cf0b9ab02..adb4c157b 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 71095cb20..dd8f11062 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