[PATCHv2] tar: remove leading / and ../ on reading and writing

Alexander Shishkin virtuoso at slind.org
Mon Feb 28 18:07:19 UTC 2011


Currently, tar will add members with names starting with
the prefixes to an archive unmodified, and will then refuse
to extract from such archive. However, GNU tar will strip
these prefixes upon creating the archive and reading from
it.

function                                             old     new   delta
strip_leading_junk                                     -     114    +114
static.pfx                                             -      32     +32
static.warned                                          1       -      -1
run_applet_and_exit                                  811     810      -1
.rodata                                           147175  147142     -33
writeFileToTarball                                   535     500     -35
get_header_tar                                      1642    1598     -44
------------------------------------------------------------------------------
(add/remove: 2/1 grow/shrink: 0/4 up/down: 146/-114)           Total: 32 bytes

Signed-off-by: Alexander Shishkin <virtuoso at slind.org>
---
 archival/libarchive/get_header_tar.c |   48 +++++++++++++++++++++++++++------
 archival/tar.c                       |   11 +-------
 include/archive.h                    |    2 +
 testsuite/tar/tar-strips-junk        |    9 ++++++
 4 files changed, 51 insertions(+), 19 deletions(-)
 create mode 100644 testsuite/tar/tar-strips-junk

diff --git a/archival/libarchive/get_header_tar.c b/archival/libarchive/get_header_tar.c
index 2e03327..af67843 100644
--- a/archival/libarchive/get_header_tar.c
+++ b/archival/libarchive/get_header_tar.c
@@ -118,11 +118,44 @@ static char *get_selinux_sctx_from_pax_hdr(archive_handle_t *archive_handle, uns
 }
 #endif
 
+FAST_FUNC char *strip_leading_junk(char *file_name)
+{
+	static struct {
+		const char *prefix;
+		int len;
+		bool warned;
+	} pfx[] = {
+		{"/", 1},
+		{"../", 3}
+	};
+	char *new_name = file_name;
+
+	for (new_name = file_name; *new_name; new_name = file_name) {
+		int i;
+
+		for (i = 0; i < ARRAY_SIZE(pfx); i++)
+			if (!strncmp(file_name, pfx[i].prefix, pfx[i].len)) {
+				file_name += pfx[i].len;
+
+				if (!pfx[i].warned) {
+					bb_error_msg("removing leading '%s' from member names",
+						     pfx[i].prefix);
+					pfx[i].warned = true;
+				}
+			}
+
+		if (new_name == file_name)
+			break;
+	}
+
+	return new_name;
+}
+
 char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
 {
 	file_header_t *file_header = archive_handle->file_header;
 	struct tar_header_t tar;
-	char *cp;
+	char *cp, *real_name;
 	int i, sum_u, sum;
 #if ENABLE_FEATURE_TAR_OLDSUN_COMPATIBILITY
 	int sum_s;
@@ -422,12 +455,9 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
 		p_linkname = NULL;
 	}
 #endif
-	if (strncmp(file_header->name, "/../"+1, 3) == 0
-	 || strstr(file_header->name, "/../")
-	) {
-		bb_error_msg_and_die("name with '..' encountered: '%s'",
-				file_header->name);
-	}
+
+	real_name = file_header->name;
+	file_header->name = strip_leading_junk(real_name);
 
 	/* Strip trailing '/' in directories */
 	/* Must be done after mode is set as '/' is used to check if it's a directory */
@@ -443,10 +473,10 @@ char FAST_FUNC get_header_tar(archive_handle_t *archive_handle)
 		if (archive_handle->accept || archive_handle->reject)
 			llist_add_to(&archive_handle->passed, file_header->name);
 		else /* Caller isn't interested in list of unpacked files */
-			free(file_header->name);
+			free(real_name);
 	} else {
 		data_skip(archive_handle);
-		free(file_header->name);
+		free(real_name);
 	}
 	archive_handle->offset += file_header->size;
 
diff --git a/archival/tar.c b/archival/tar.c
index 1e3cecf..78b6602 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -398,16 +398,7 @@ static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statb
 	DBG("writeFileToTarball('%s')", fileName);
 
 	/* Strip leading '/' (must be before memorizing hardlink's name) */
-	header_name = fileName;
-	while (header_name[0] == '/') {
-		static smallint warned;
-
-		if (!warned) {
-			bb_error_msg("removing leading '/' from member names");
-			warned = 1;
-		}
-		header_name++;
-	}
+	header_name = strip_leading_junk((char *)fileName);
 
 	if (header_name[0] == '\0')
 		return TRUE;
diff --git a/include/archive.h b/include/archive.h
index 49c4787..a9f34ed 100644
--- a/include/archive.h
+++ b/include/archive.h
@@ -224,6 +224,8 @@ int bbunpack(char **argv,
 	    const char *expected_ext
 ) FAST_FUNC;
 
+char *strip_leading_junk(char *file_name) FAST_FUNC;
+
 #if BB_MMU
 void open_transformer(int fd,
 	IF_DESKTOP(long long) int FAST_FUNC (*transformer)(int src_fd, int dst_fd)) FAST_FUNC;
diff --git a/testsuite/tar/tar-strips-junk b/testsuite/tar/tar-strips-junk
new file mode 100644
index 0000000..cb2c16d
--- /dev/null
+++ b/testsuite/tar/tar-strips-junk
@@ -0,0 +1,9 @@
+# FEATURE: CONFIG_FEATURE_TAR_CREATE
+
+touch foo bar
+mkdir tar-tmp
+cd tar-tmp
+busybox tar cf foo.tar ../foo ../bar
+rm ../foo ../bar
+busybox tar xf foo.tar
+test -f foo -a -f bar
-- 
1.7.2.1.45.gb66c2



More information about the busybox mailing list