tar: fix interaction of delayed symlink and hardlink creation
No binary (build failed)
—
(log yok)
| @@ -508,7 +508,7 @@ int cpio_main(int argc UNUSED_PARAM, char **argv) | |
| while (get_header_cpio(archive_handle) == EXIT_SUCCESS) | |
| continue; | |
| - | create_symlinks_from_list(archive_handle->symlink_placeholders); |
| + | create_links_from_list(archive_handle->link_placeholders); |
| if (archive_handle->cpio__blocks != (off_t)-1 | |
| && !(opt & OPT_QUIET) | |
| @@ -122,13 +122,10 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | |
| /* Handle hard links separately */ | |
| if (hard_link) { | |
| - | res = link(hard_link, dst_name); |
| - | if (res != 0) { |
| - | /* shared message */ |
| - | bb_perror_msg("can't create %slink '%s' to '%s'", |
| - | "hard", dst_name, hard_link |
| - | ); |
| - | } |
| + | create_or_remember_link(&archive_handle->link_placeholders, |
| + | hard_link, |
| + | dst_name, |
| + | 1); |
| /* Hardlinks have no separate mode/ownership, skip chown/chmod */ | |
| goto ret; | |
| } | |
| @@ -195,9 +192,10 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | |
| * | |
| * Untarring bug.tar would otherwise place evil.py in '/tmp'. | |
| */ | |
| - | create_or_remember_symlink(&archive_handle->symlink_placeholders, |
| + | create_or_remember_link(&archive_handle->link_placeholders, |
| file_header->link_target, | |
| - | dst_name); |
| + | dst_name, |
| + | 0); |
| break; | |
| case S_IFSOCK: | |
| case S_IFBLK: | |
| @@ -126,7 +126,7 @@ char FAST_FUNC get_header_ar(archive_handle_t *archive_handle) | |
| struct archive_handle_t *sa = archive_handle->dpkg__sub_archive; | |
| while (archive_handle->dpkg__action_data_subarchive(sa) == EXIT_SUCCESS) | |
| continue; | |
| - | create_symlinks_from_list(sa->symlink_placeholders); |
| + | create_links_from_list(sa->link_placeholders); |
| } else | |
| #endif | |
| archive_handle->action_data(archive_handle); | |
| @@ -5,13 +5,14 @@ | |
| #include "libbb.h" | |
| #include "bb_archive.h" | |
| - | void FAST_FUNC create_or_remember_symlink(llist_t **symlink_placeholders, |
| + | void FAST_FUNC create_or_remember_link(llist_t **link_placeholders, |
| const char *target, | |
| - | const char *linkname) |
| + | const char *linkname, |
| + | int hard_link) |
| { | |
| - | if (target[0] == '/' || strstr(target, "..")) { |
| - | llist_add_to(symlink_placeholders, |
| - | xasprintf("%s%c%s", linkname, '\0', target) |
| + | if (hard_link || target[0] == '/' || strstr(target, "..")) { |
| + | llist_add_to_end(link_placeholders, |
| + | xasprintf("%c%s%c%s", hard_link, linkname, '\0', target) |
| ); | |
| return; | |
| } | |
| @@ -23,17 +24,17 @@ void FAST_FUNC create_or_remember_symlink(llist_t **symlink_placeholders, | |
| } | |
| } | |
| - | void FAST_FUNC create_symlinks_from_list(llist_t *list) |
| + | void FAST_FUNC create_links_from_list(llist_t *list) |
| { | |
| while (list) { | |
| char *target; | |
| - | target = list->data + strlen(list->data) + 1; |
| - | if (symlink(target, list->data)) { |
| + | target = list->data + 1 + strlen(list->data + 1) + 1; |
| + | if ((*list->data ? link : symlink) (target, list->data + 1)) { |
| /* shared message */ | |
| bb_error_msg_and_die("can't create %slink '%s' to '%s'", | |
| - | "sym", |
| - | list->data, target |
| + | *list->data ? "hard" : "sym", |
| + | list->data + 1, target |
| ); | |
| } | |
| list = list->link; | |
| @@ -1244,7 +1244,7 @@ int tar_main(int argc UNUSED_PARAM, char **argv) | |
| while (get_header_tar(tar_handle) == EXIT_SUCCESS) | |
| bb_got_signal = EXIT_SUCCESS; /* saw at least one header, good */ | |
| - | create_symlinks_from_list(tar_handle->symlink_placeholders); |
| + | create_links_from_list(tar_handle->link_placeholders); |
| /* Check that every file that should have been extracted was */ | |
| while (tar_handle->accept) { | |
| @@ -372,9 +372,10 @@ static void unzip_extract_symlink(llist_t **symlink_placeholders, | |
| target[xstate.mem_output_size] = '\0'; | |
| #endif | |
| } | |
| - | create_or_remember_symlink(symlink_placeholders, |
| + | create_or_remember_link(symlink_placeholders, |
| target, | |
| - | dst_fn); |
| + | dst_fn, |
| + | 0); |
| free(target); | |
| } | |
| #endif | |
| @@ -990,7 +991,7 @@ int unzip_main(int argc, char **argv) | |
| } | |
| #if ENABLE_FEATURE_UNZIP_CDF | |
| - | create_symlinks_from_list(symlink_placeholders); |
| + | create_links_from_list(symlink_placeholders); |
| #endif | |
| if ((opts & OPT_l) && quiet <= 1) { | |
| @@ -64,8 +64,8 @@ typedef struct archive_handle_t { | |
| /* Currently processed file's header */ | |
| file_header_t *file_header; | |
| - | /* List of symlink placeholders */ |
| - | llist_t *symlink_placeholders; |
| + | /* List of link placeholders */ |
| + | llist_t *link_placeholders; |
| /* Process the header component, e.g. tar -t */ | |
| void FAST_FUNC (*action_header)(const file_header_t *); | |
| @@ -203,10 +203,11 @@ void seek_by_jump(int fd, off_t amount) FAST_FUNC; | |
| void seek_by_read(int fd, off_t amount) FAST_FUNC; | |
| const char *strip_unsafe_prefix(const char *str) FAST_FUNC; | |
| - | void create_or_remember_symlink(llist_t **symlink_placeholders, |
| + | void create_or_remember_link(llist_t **link_placeholders, |
| const char *target, | |
| - | const char *linkname) FAST_FUNC; |
| - | void create_symlinks_from_list(llist_t *list) FAST_FUNC; |
| + | const char *linkname, |
| + | int hard_link) FAST_FUNC; |
| + | void create_links_from_list(llist_t *list) FAST_FUNC; |
| void data_align(archive_handle_t *archive_handle, unsigned boundary) FAST_FUNC; | |
| const llist_t *find_list_entry(const llist_t *list, const char *filename) FAST_FUNC; | |
| @@ -365,4 +365,20 @@ n8fYaKlioCTzL2oXYczyUUIP4u5IpwoSEwWdtoA= | |
| SKIP= | |
| cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null | |
| + | mkdir tar.tempdir && cd tar.tempdir || exit 1 |
| + | testing "Symlinks and hardlinks coexist" '\ |
| + | mkdir dir |
| + | >dir/a |
| + | ln -s ../dir/a dir/b |
| + | ln dir/b dir/c |
| + | mkdir new |
| + | tar cf - dir/* | tar -C new -xvf - 2>&1 |
| + | ' "\ |
| + | dir/a |
| + | dir/b |
| + | dir/c |
| + | " \ |
| + | "" "" |
| + | cd .. || exit 1; rm -rf tar.tempdir 2>/dev/null |
| + | |
| exit $FAILCOUNT | |