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>
This commit is contained in:
Denys Vlasenko 2010-04-09 10:52:52 +02:00
parent 823b636cd1
commit 02365a6ef7
3 changed files with 73 additions and 16 deletions

View File

@ -34,12 +34,30 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle)
if (archive_handle->ah_flags & ARCHIVE_UNLINK_OLD) { if (archive_handle->ah_flags & ARCHIVE_UNLINK_OLD) {
/* Remove the entry if it exists */ /* Remove the entry if it exists */
if ((!S_ISDIR(file_header->mode)) if (!S_ISDIR(file_header->mode)) {
&& (unlink(file_header->name) == -1) /* Is it hardlink?
&& (errno != ENOENT) * We encode hard links as regular files of size 0 with a symlink */
) { if (S_ISREG(file_header->mode)
bb_perror_msg_and_die("can't remove old file %s", && file_header->link_target
file_header->name); && 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) { 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 /* 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) if (S_ISREG(file_header->mode)
&& file_header->link_target && file_header->link_target
&& file_header->size == 0 && file_header->size == 0

View File

@ -507,8 +507,9 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
archive_handle->action_header(/*archive_handle->*/ file_header); archive_handle->action_header(/*archive_handle->*/ file_header);
/* Note that we kill the '/' only after action_header() */ /* Note that we kill the '/' only after action_header() */
/* (like GNU tar 1.15.1: verbose mode outputs "dir/dir/") */ /* (like GNU tar 1.15.1: verbose mode outputs "dir/dir/") */
if (cp) *cp = '\0'; if (cp)
archive_handle->ah_flags |= ARCHIVE_EXTRACT_QUIET; *cp = '\0';
//archive_handle->ah_flags |= ARCHIVE_EXTRACT_QUIET; // why??
archive_handle->action_data(archive_handle); archive_handle->action_data(archive_handle);
llist_add_to(&(archive_handle->passed), file_header->name); llist_add_to(&(archive_handle->passed), file_header->name);
} else { } else {

View File

@ -4,20 +4,25 @@
. ./testing.sh . ./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 "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 rm -rf input_* test.tar 2>/dev/null
>input_hard1 >input_hard1
ln input_hard1 input_hard2 ln input_hard1 input_hard2
mkdir input_dir mkdir input_dir
>input_dir/file >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 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 tvf test.tar | sed "s/.*[0-9] input/input/"
tar xf test.tar 2>&1 && echo Ok tar xf test.tar 2>&1
" "\ echo Ok: $?
ls -l . input_dir/* | grep input_ | sed "s/\\(^[^ ]*\\) .* input/\\1 input/"
' "\
input input
input_dir/ input_dir/
input_dir/file input_dir/file
@ -27,7 +32,40 @@ input_hard1 -> input_hard1
input_dir/ input_dir/
input_dir/file input_dir/file
input 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" "" "Ok\n" ""
SKIP= SKIP=
cd .. && rm -rf tempdir || exit 1 cd .. && rm -rf tar.tempdir || exit 1
exit $FAILCOUNT exit $FAILCOUNT