[git commit master] tar: fix "hardlinks to symlinks chown" bug 1519.

Denys Vlasenko vda.linux at googlemail.com
Fri Apr 9 12:11:45 UTC 2010


commit: http://git.busybox.net/busybox/commit/?id=e69ad87802139b7b62fc06ff5d5d09cc4245d5fc
branch: http://git.busybox.net/busybox/commit/?id=refs/heads/master

Signed-off-by: Denys Vlasenko <vda.linux at googlemail.com>
---
 archival/libunarchive/data_extract_all.c    |  102 ++++++++++++++-------------
 archival/libunarchive/header_verbose_list.c |    1 +
 testsuite/tar.tests                         |   29 ++++++++
 3 files changed, 82 insertions(+), 50 deletions(-)

diff --git a/archival/libunarchive/data_extract_all.c b/archival/libunarchive/data_extract_all.c
index de2367a..8152610 100644
--- a/archival/libunarchive/data_extract_all.c
+++ b/archival/libunarchive/data_extract_all.c
@@ -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)) {
diff --git a/archival/libunarchive/header_verbose_list.c b/archival/libunarchive/header_verbose_list.c
index f6f04cf..3319e63 100644
--- a/archival/libunarchive/header_verbose_list.c
+++ b/archival/libunarchive/header_verbose_list.c
@@ -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);
 	}
diff --git a/testsuite/tar.tests b/testsuite/tar.tests
index dd8f110..a963829 100755
--- a/testsuite/tar.tests
+++ b/testsuite/tar.tests
@@ -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
-- 
1.6.3.3



More information about the busybox-cvs mailing list