tar: fix "hardlinks to symlinks chown" bug 1519.
No binary (build failed)
—
(log yok)
| @@ -96,58 +96,60 @@ void FAST_FUNC data_extract_all(archive_handle_t *archive_handle) | |
| file_header->name, | |
| file_header->link_target); | |
| } | |
| - | } else { |
| - | /* Create the filesystem entry */ |
| - | switch (file_header->mode & S_IFMT) { |
| - | case S_IFREG: { |
| - | /* Regular file */ |
| - | int flags = O_WRONLY | O_CREAT | O_EXCL; |
| - | if (archive_handle->ah_flags & ARCHIVE_O_TRUNC) |
| - | flags = O_WRONLY | O_CREAT | O_TRUNC; |
| - | dst_fd = xopen3(file_header->name, |
| - | flags, |
| - | file_header->mode |
| - | ); |
| - | bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size); |
| - | close(dst_fd); |
| - | break; |
| + | /* Hardlinks have no separate mode/ownership, skip chown/chmod */ |
| + | goto ret; |
| + | } |
| + | |
| + | /* Create the filesystem entry */ |
| + | switch (file_header->mode & S_IFMT) { |
| + | case S_IFREG: { |
| + | /* Regular file */ |
| + | int flags = O_WRONLY | O_CREAT | O_EXCL; |
| + | if (archive_handle->ah_flags & ARCHIVE_O_TRUNC) |
| + | flags = O_WRONLY | O_CREAT | O_TRUNC; |
| + | dst_fd = xopen3(file_header->name, |
| + | flags, |
| + | file_header->mode |
| + | ); |
| + | bb_copyfd_exact_size(archive_handle->src_fd, dst_fd, file_header->size); |
| + | close(dst_fd); |
| + | break; |
| + | } |
| + | case S_IFDIR: |
| + | res = mkdir(file_header->name, file_header->mode); |
| + | if ((res == -1) |
| + | && (errno != EISDIR) /* btw, Linux doesn't return this */ |
| + | && (errno != EEXIST) |
| + | && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) |
| + | ) { |
| + | bb_perror_msg("can't make dir %s", file_header->name); |
| } | |
| - | case S_IFDIR: |
| - | res = mkdir(file_header->name, file_header->mode); |
| - | if ((res == -1) |
| - | && (errno != EISDIR) /* btw, Linux doesn't return this */ |
| - | && (errno != EEXIST) |
| - | && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) |
| - | ) { |
| - | bb_perror_msg("can't make dir %s", file_header->name); |
| - | } |
| - | break; |
| - | case S_IFLNK: |
| - | /* Symlink */ |
| - | res = symlink(file_header->link_target, file_header->name); |
| - | if ((res == -1) |
| - | && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) |
| - | ) { |
| - | bb_perror_msg("can't create %slink " |
| - | "from %s to %s", "sym", |
| - | file_header->name, |
| - | file_header->link_target); |
| - | } |
| - | break; |
| - | case S_IFSOCK: |
| - | case S_IFBLK: |
| - | case S_IFCHR: |
| - | case S_IFIFO: |
| - | res = mknod(file_header->name, file_header->mode, file_header->device); |
| - | if ((res == -1) |
| - | && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) |
| - | ) { |
| - | bb_perror_msg("can't create node %s", file_header->name); |
| - | } |
| - | break; |
| - | default: |
| - | bb_error_msg_and_die("unrecognized file type"); |
| + | break; |
| + | case S_IFLNK: |
| + | /* Symlink */ |
| + | res = symlink(file_header->link_target, file_header->name); |
| + | if ((res == -1) |
| + | && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) |
| + | ) { |
| + | bb_perror_msg("can't create %slink " |
| + | "from %s to %s", "sym", |
| + | file_header->name, |
| + | file_header->link_target); |
| + | } |
| + | break; |
| + | case S_IFSOCK: |
| + | case S_IFBLK: |
| + | case S_IFCHR: |
| + | case S_IFIFO: |
| + | res = mknod(file_header->name, file_header->mode, file_header->device); |
| + | if ((res == -1) |
| + | && !(archive_handle->ah_flags & ARCHIVE_EXTRACT_QUIET) |
| + | ) { |
| + | bb_perror_msg("can't create node %s", file_header->name); |
| } | |
| + | break; |
| + | default: |
| + | bb_error_msg_and_die("unrecognized file type"); |
| } | |
| if (!(archive_handle->ah_flags & ARCHIVE_DONT_RESTORE_OWNER)) { | |
| @@ -61,6 +61,7 @@ void FAST_FUNC header_verbose_list(const file_header_t *file_header) | |
| #endif /* FEATURE_TAR_UNAME_GNAME */ | |
| + | /* NB: GNU tar shows "->" for symlinks and "link to" for hardlinks */ |
| if (file_header->link_target) { | |
| printf(" -> %s", file_header->link_target); | |
| } | |
| @@ -69,6 +69,35 @@ dr-xr-x--- input_dir | |
| " \ | |
| "" "" | |
| + | testing "tar symlinks mode" '\ |
| + | rm -rf input_* test.tar 2>/dev/null |
| + | >input_file |
| + | chmod 741 input_file |
| + | ln -s input_file input_soft |
| + | mkdir input_dir |
| + | chmod 550 input_dir |
| + | ln input_file input_dir |
| + | ln input_soft 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_file |
| + | input_dir/input_soft -> input_file |
| + | input_file -> input_dir/input_file |
| + | input_soft -> input_dir/input_soft |
| + | Ok: 0 |
| + | -rwxr----x input_dir/input_file |
| + | lrwxrwxrwx input_file |
| + | dr-xr-x--- input_dir |
| + | -rwxr----x input_file |
| + | lrwxrwxrwx input_file |
| + | " \ |
| + | "" "" |
| + | |
| optional FEATURE_TAR_LONG_OPTIONS | |
| testing "tar --overwrite" "\ | |
| rm -rf input_* test.tar 2>/dev/null | |