tar: fix "hardlinks to symlinks chown" bug 1519.
authorDenys Vlasenko <vda.linux@googlemail.com>
Fri, 9 Apr 2010 12:11:45 +0000 (14:11 +0200)
committerDenys Vlasenko <vda.linux@googlemail.com>
Fri, 9 Apr 2010 12:11:45 +0000 (14:11 +0200)
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
archival/libunarchive/data_extract_all.c
archival/libunarchive/header_verbose_list.c
testsuite/tar.tests

index de2367ac260054ab37ff0e9972819b03e6ab3473..8152610367928f5d4a69ac51c1f687716e212fdc 100644 (file)
@@ -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)) {
index f6f04cfd59e5f92cc6377fbfe0c4d47ee6913264..3319e63a9660ff33bfa13230ab6bfe6560588af0 100644 (file)
@@ -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);
        }
index dd8f110620c717ac821c48e0e41bba81676d6d9e..a96382932952ed2d8d9286a8546f00dfaa63f007 100755 (executable)
@@ -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