Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 21 Dec 2004 18:55:55 -0800
From:      Tim Kientzle <kientzle@freebsd.org>
To:        mcl@wro-com.net
Cc:        freebsd-bugs@freebsd.org
Subject:   Re: bin/74385: libarchive - archive_write_header_pax: 'x' header failed
Message-ID:  <41C8E23B.8090803@freebsd.org>
In-Reply-To: <200411252127.iAPLRQ0h042865@freefall.freebsd.org>
References:  <200411252127.iAPLRQ0h042865@freefall.freebsd.org>

next in thread | previous in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------000805050409040805000909
Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

Michal,

Could you please try the attached patch to
/usr/src/lib/libarchive and let me know
if it fixes your problem?

Thanks,

Tim Kientzle

http://www.freebsd.org/cgi/query-pr.cgi?pr=74385

--------------000805050409040805000909
Content-Type: text/plain;
 name="archive_write_set_format_pax.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="archive_write_set_format_pax.diff"

Index: archive_write_set_format_pax.c
===================================================================
RCS file: /home/ncvs/src/lib/libarchive/archive_write_set_format_pax.c,v
retrieving revision 1.24
diff -u -r1.24 archive_write_set_format_pax.c
--- archive_write_set_format_pax.c	22 Dec 2004 02:35:37 -0000	1.24
+++ archive_write_set_format_pax.c	22 Dec 2004 02:54:54 -0000
@@ -720,99 +720,191 @@
 /*
  * We need a valid name for the regular 'ustar' entry.  This routine
  * tries to hack something more-or-less reasonable.
+ *
+ * The approach here tries to preserve leading dir names.  We do so by
+ * breaking the full path into three sections:
+ *   1) "prefix" directory names,
+ *   2) "suffix" directory names,
+ *   3) filename.
+ *
+ * These three sections must satisfy the following requirements:
+ *   * Parts 1 & 2 together form an initial portion of the dir name.
+ *   * Part 3 forms an initial portion of the base filename.
+ *   * The filename must be <= 90 chars to fit the ustar 'name' field while
+ *     allowing room for the '/PaxHeader' dir element (see below)
+ *   * Parts 2 & 3 together must be <= 90 chars to fit the ustar 'name' field
+ *     while allowing room for the '/PaxHeader' dir element.
+ *   * Part 1 must be <= 155 chars to fit the ustar 'prefix' field.
+ *   * If the original name ends in a '/', the new name must also end in a '/'
+ *   * Trailing '/.' sequences may be stripped.
+ *
+ * Note: Recall that the ustar format does not store the '/' separating
+ * parts 1 & 2, but does store the '/' separating parts 2 & 3.
  */
 static char *
 build_ustar_entry_name(char *dest, const char *src)
 {
-	const char *basename, *break_point, *prefix;
-	int basename_length, dirname_length, prefix_length;
+	const char *prefix, *prefix_end;
+	const char *suffix, *suffix_end;
+	const char *filename, *filename_end;
+	char *p;
+	size_t s;
+	int need_slash = 0; /* Was there a trailing slash? */
+	size_t suffix_length = 90;
+
+	/* Step 0: Initial checks. */
+	s = strlen(src);
+	if (s < 100) {
+		strcpy(dest, src);
+		return (dest);
+	}
 
-	prefix = src;
-	basename = strrchr(src, '/');
-	if (basename == NULL) {
-		basename = src;
-		prefix_length = 0;
-		basename_length = strlen(basename);
-		if (basename_length > 100)
-			basename_length = 100;
-	} else {
-		basename_length = strlen(basename);
-		if (basename_length > 100)
-			basename_length = 100;
-		dirname_length = basename - src;
-
-		break_point =
-		    strchr(src + dirname_length + basename_length - 101, '/');
-		prefix_length = break_point - prefix - 1;
-		while (prefix_length > 155) {
-			prefix = strchr(prefix, '/') + 1; /* Drop 1st dir. */
-			prefix_length = break_point - prefix - 1;
+	/* Step 1: Locate filename and enforce the length restriction. */
+	filename_end = src + s;
+	/* Remove trailing '/' chars and '/.' pairs. */
+	for (;;) {
+		if (filename_end > src && filename_end[-1] == '/') {
+			filename_end --;
+			need_slash = 1; /* Remember to restore trailing '/'. */
+			continue;
+		}
+		if (filename_end > src + 1 && filename_end[-1] == '.'
+		    && filename_end[-2] == '/') {
+			filename_end -= 2;
+			continue;
 		}
+		break;
 	}
+	filename = filename_end - 1;
+	if (need_slash)
+		suffix_length--;
+	while ((filename > src) && (*filename != '/'))
+		filename --;
+	if ((*filename == '/') && (filename < filename_end - 1))
+		filename ++;
+	if (filename_end > filename + suffix_length)
+		filename_end = filename + suffix_length;
+
+	/* Step 2: Locate the "prefix" section of the dirname, including
+	 * trailing '/'. */
+	prefix = src;
+	prefix_end = prefix + 155;
+	if (prefix_end > filename)
+		prefix_end = filename;
+	while (prefix_end > prefix && *prefix_end != '/')
+		prefix_end--;
+	if ((prefix_end < filename) && (*prefix_end == '/'))
+		prefix_end++;
+
+	/* Step 3: Locate the "suffix" section of the dirname,
+	 * including trailing '/'. */
+	suffix = prefix_end;
+	suffix_end = suffix + 89 - (filename_end - filename);
+	if (suffix_end > filename)
+		suffix_end = filename;
+	if (suffix_end < suffix)
+		suffix_end = suffix;
+	while (suffix_end > suffix && *suffix_end != '/')
+		suffix_end--;
+	if ((suffix_end < filename) && (*suffix_end == '/'))
+		suffix_end++;
 
+	/* Step 4: Build the new name. */
 	/* The OpenBSD strlcpy function is safer, but less portable. */
 	/* Rather than maintain two versions, just use the strncpy version. */
-	strncpy(dest, prefix, basename - prefix + basename_length);
-	dest[basename - prefix + basename_length] = '\0';
+	p = dest;
+	if (prefix_end > prefix) {
+		strncpy(p, prefix, prefix_end - prefix);
+		p += prefix_end - prefix;
+	}
+	if (suffix_end > suffix) {
+		strncpy(p, suffix, suffix_end - suffix);
+		p += suffix_end - suffix;
+	}
+	strncpy(p, filename, filename_end - filename);
+	p += filename_end - filename;
+	if (need_slash)
+		*p++ = '/';
+	*p++ = '\0';
 
 	return (dest);
 }
 
 /*
  * The ustar header for the pax extended attributes must have a
- * reasonable name:  SUSv3 suggests 'dirname'/PaxHeaders/'basename'
+ * reasonable name:  SUSv3 suggests 'dirname'/PaxHeader/'filename'
  *
  * Joerg Schiling has argued that this is unnecessary because, in practice,
  * if the pax extended attributes get extracted as regular files, noone is
  * going to bother reading those attributes to manually restore them.
- * This is a tempting argument, but I'm not entirely convinced.
+ * Based on this, 'star' uses /tmp/PaxHeader/'basename' as the ustar header
+ * name.  This is a tempting argument, but I'm not entirely convinced.
+ * I'm also uncomfortable with the fact that "/tmp" is a Unix-ism.
  *
- * Of course, adding "PaxHeaders/" might force the name to be too big.
- * Here, I start from the (possibly already-trimmed) name used in the
- * main ustar header and delete some additional early path elements to
- * fit in the extra "PaxHeader/" part.
+ * The following routine implements the SUSv3 recommendation, and is
+ * much simpler because we do the initial processing with
+ * build_ustar_entry_name() above which results in something that is
+ * already short enough to accomodate the extra '/PaxHeader'
+ * addition.  We just need to separate dir and filename portions and
+ * handle a few pathological cases.
  */
 static char *
-build_pax_attribute_name(const char *abbreviated, /* ustar-compat name */
+build_pax_attribute_name(const char *src, /* ustar-compat name */
     struct archive_string *work)
 {
-	const char *basename, *break_point, *prefix;
-	int prefix_length, suffix_length;
+	const char *filename, *filename_end;
+
+	if (*src == '\0') {
+		archive_strcpy(work, "PaxHeader/blank");
+		return (work->s);
+	}
+	if (*src == '.' && src[1] == '\0') {
+		archive_strcpy(work, "PaxHeader/dot");
+		return (work->s);
+	}
 
-	/*
-	 * This is much simpler because I know that "abbreviated" is
-	 * already small enough; I just need to determine if it needs
-	 * any further trimming to fit the "PaxHeader/" portion.
-	 */
-
-	/* Identify the final prefix and suffix portions. */
-	prefix = abbreviated;	/* First guess: prefix starts at beginning */
-	if (strlen(abbreviated) > 100) {
-		break_point = strchr(prefix + strlen(prefix) - 101, '/');
-		prefix_length = break_point - prefix - 1;
-		suffix_length = strlen(break_point + 1);
-		/*
-		 * The next loop keeps trimming until "/PaxHeader/" can
-		 * be added to either the prefix or the suffix.
-		 */
-		while (prefix_length > 144 && suffix_length > 89) {
-			prefix = strchr(prefix, '/') + 1; /* Drop 1st dir. */
-			prefix_length = break_point - prefix - 1;
+	/* Prune unwanted final path elements. */
+	filename_end = src + strlen(src);
+	for (;;) {
+		if (filename_end > src && filename_end[-1] == '/') {
+			filename_end --;
+			continue;
+		}
+		if (filename_end > src + 1 && filename_end[-1] == '.'
+		    && filename_end[-2] == '/') {
+			filename_end -= 2;
+			continue;
 		}
+		break;
+	}
+	while ((filename_end > src) && (filename_end[-1] == '/'))
+		filename_end --;
+
+	/* Pathological case: Entire 'src' consists of '/' characters. */
+	if (filename_end == src) {
+		archive_strcpy(work, "/PaxHeader/rootdir");
+		return (work->s);
 	}
 
-	archive_string_empty(work);
-	basename = strrchr(prefix, '/');
-	if (basename == NULL) {
-		archive_strcpy(work, "PaxHeader/");
-		archive_strcat(work, prefix);
-	} else {
-		basename++;
-		archive_strncpy(work, prefix, basename - prefix);
-		archive_strcat(work, "PaxHeader/");
-		archive_strcat(work, basename);
+	/* Find the '/' before the filename portion. */
+	filename = filename_end - 1;
+	while ((filename > src) && (*filename != '/'))
+		filename --;
+	if (*filename == '/')
+		filename ++;
+
+	/* Pathological case: filename is '.' */
+	if (filename_end == filename + 2
+	    && filename[0] == '/' && filename[1] == '.') {
+		archive_strncpy(work, src, filename - src);
+		archive_strcat(work, "PaxHeader/dot");
+		return (work->s);
 	}
 
+	/* Build the new name. */
+	archive_strncpy(work, src, filename - src);
+	archive_strcat(work, "PaxHeader/");
+	archive_strncat(work, filename, filename_end - filename);
 	return (work->s);
 }
 

--------------000805050409040805000909--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?41C8E23B.8090803>