--- /dev/null
+/*-
+ * Copyright (c) 2003-2014 Tim Kientzle
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "test.h"
+__FBSDID("$FreeBSD$");
+
+DEFINE_TEST(test_leading_slash)
+{
+ const char *reffile = "test_leading_slash.tar";
+
+ extract_reference_file(reffile);
+ assertEqualInt(0, systemf("%s -xf %s >test.out 2>test.err", testprog, reffile));
+ assertFileExists("foo/file");
+ assertTextFileContents("foo\x0a", "foo/file");
+ assertTextFileContents("foo\x0a", "foo/hardlink");
+ assertIsHardlink("foo/file", "foo/hardlink");
+ assertEmptyFile("test.out");
+ assertTextFileContents("bsdtar: Removing leading '/' from member names\x0a", "test.err");
+}
+
--- /dev/null
+begin 644 test_leading_slash.tar
+M+V9O;R]F:6QE````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````#`P,#8T-"``,#`P,#`P(``P,#`P,#`@`#`P,#`P,#`P,#`T
+M(#$R-#$V,S4U-34V(#`Q,C8V-P`@,```````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````````````````````````!U<W1A<@`P,')O;W0`
+M````````````````````````````````````=VAE96P`````````````````
+M```````````````````P,#`P,#`@`#`P,#`P,"``````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M``````````````````````!F;V\*````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`````````````````````````````````````````````"]F;V\O:&%R9&QI
+M;FL`````````````````````````````````````````````````````````
+M```````````````````````````````````````````````````````````P
+M,#`V-#0@`#`P,#`P,"``,#`P,#`P(``P,#`P,#`P,#`P,"`Q,C0Q-C,U-34U
+M-B`P,34R-#,`(#$O9F]O+V9I;&4`````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````=7-T87(`,#!R;V]T````````````````
+M`````````````````````'=H965L````````````````````````````````
+M````,#`P,#`P(``P,#`P,#`@````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+H````````````````````````````````````````````````````````
+`
+end
}
}
+static void
+warn_strip_leading_char(struct bsdtar *bsdtar, const char *c)
+{
+ if (!bsdtar->warned_lead_slash) {
+ lafe_warnc(0,
+ "Removing leading '%c' from member names",
+ c[0]);
+ bsdtar->warned_lead_slash = 1;
+ }
+}
+
+static void
+warn_strip_drive_letter(struct bsdtar *bsdtar)
+{
+ if (!bsdtar->warned_lead_slash) {
+ lafe_warnc(0,
+ "Removing leading drive letter from "
+ "member names");
+ bsdtar->warned_lead_slash = 1;
+ }
+}
+
+/*
+ * Convert absolute path to non-absolute path by skipping leading
+ * absolute path prefixes.
+ */
+static const char*
+strip_absolute_path(struct bsdtar *bsdtar, const char *p)
+{
+ const char *rp;
+
+ /* Remove leading "//./" or "//?/" or "//?/UNC/"
+ * (absolute path prefixes used by Windows API) */
+ if ((p[0] == '/' || p[0] == '\\') &&
+ (p[1] == '/' || p[1] == '\\') &&
+ (p[2] == '.' || p[2] == '?') &&
+ (p[3] == '/' || p[3] == '\\'))
+ {
+ if (p[2] == '?' &&
+ (p[4] == 'U' || p[4] == 'u') &&
+ (p[5] == 'N' || p[5] == 'n') &&
+ (p[6] == 'C' || p[6] == 'c') &&
+ (p[7] == '/' || p[7] == '\\'))
+ p += 8;
+ else
+ p += 4;
+ warn_strip_drive_letter(bsdtar);
+ }
+
+ /* Remove multiple leading slashes and Windows drive letters. */
+ do {
+ rp = p;
+ if (((p[0] >= 'a' && p[0] <= 'z') ||
+ (p[0] >= 'A' && p[0] <= 'Z')) &&
+ p[1] == ':') {
+ p += 2;
+ warn_strip_drive_letter(bsdtar);
+ }
+
+ /* Remove leading "/../", "/./", "//", etc. */
+ while (p[0] == '/' || p[0] == '\\') {
+ if (p[1] == '.' &&
+ p[2] == '.' &&
+ (p[3] == '/' || p[3] == '\\')) {
+ p += 3; /* Remove "/..", leave "/" for next pass. */
+ } else if (p[1] == '.' &&
+ (p[2] == '/' || p[2] == '\\')) {
+ p += 2; /* Remove "/.", leave "/" for next pass. */
+ } else
+ p += 1; /* Remove "/". */
+ warn_strip_leading_char(bsdtar, rp);
+ }
+ } while (rp != p);
+
+ return (p);
+}
+
/*
* Handle --strip-components and any future path-rewriting options.
* Returns non-zero if the pathname should not be extracted.
*
+ * Note: The rewrites are applied uniformly to pathnames and hardlink
+ * names but not to symlink bodies. This is deliberate: Symlink
+ * bodies are not necessarily filenames. Even when they are, they
+ * need to be interpreted relative to the directory containing them,
+ * so simple rewrites like this are rarely appropriate.
+ *
* TODO: Support pax-style regex path rewrites.
*/
int
edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry)
{
const char *name = archive_entry_pathname(entry);
+ const char *original_name = name;
+ const char *hardlinkname = archive_entry_hardlink(entry);
+ const char *original_hardlinkname = hardlinkname;
#if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H)
char *subst_name;
int r;
+ /* Apply user-specified substitution to pathname. */
r = apply_substitution(bsdtar, name, &subst_name, 0, 0);
if (r == -1) {
lafe_warnc(0, "Invalid substitution, skipping entry");
} else
free(subst_name);
name = archive_entry_pathname(entry);
+ original_name = name;
}
- if (archive_entry_hardlink(entry)) {
- r = apply_substitution(bsdtar, archive_entry_hardlink(entry), &subst_name, 0, 1);
+ /* Apply user-specified substitution to hardlink target. */
+ if (hardlinkname != NULL) {
+ r = apply_substitution(bsdtar, hardlinkname, &subst_name, 0, 1);
if (r == -1) {
lafe_warnc(0, "Invalid substitution, skipping entry");
return 1;
archive_entry_copy_hardlink(entry, subst_name);
free(subst_name);
}
+ hardlinkname = archive_entry_hardlink(entry);
+ original_hardlinkname = hardlinkname;
}
+
+ /* Apply user-specified substitution to symlink body. */
if (archive_entry_symlink(entry) != NULL) {
r = apply_substitution(bsdtar, archive_entry_symlink(entry), &subst_name, 1, 0);
if (r == -1) {
/* Strip leading dir names as per --strip-components option. */
if (bsdtar->strip_components > 0) {
- const char *linkname = archive_entry_hardlink(entry);
-
name = strip_components(name, bsdtar->strip_components);
if (name == NULL)
return (1);
- if (linkname != NULL) {
- linkname = strip_components(linkname,
+ if (hardlinkname != NULL) {
+ hardlinkname = strip_components(hardlinkname,
bsdtar->strip_components);
- if (linkname == NULL)
+ if (hardlinkname == NULL)
return (1);
- archive_entry_copy_hardlink(entry, linkname);
}
}
- /* By default, don't write or restore absolute pathnames. */
if (!bsdtar->option_absolute_paths) {
- const char *rp, *p = name;
- int slashonly = 1;
-
- /* Remove leading "//./" or "//?/" or "//?/UNC/"
- * (absolute path prefixes used by Windows API) */
- if ((p[0] == '/' || p[0] == '\\') &&
- (p[1] == '/' || p[1] == '\\') &&
- (p[2] == '.' || p[2] == '?') &&
- (p[3] == '/' || p[3] == '\\'))
- {
- if (p[2] == '?' &&
- (p[4] == 'U' || p[4] == 'u') &&
- (p[5] == 'N' || p[5] == 'n') &&
- (p[6] == 'C' || p[6] == 'c') &&
- (p[7] == '/' || p[7] == '\\'))
- p += 8;
- else
- p += 4;
- slashonly = 0;
- }
- do {
- rp = p;
- /* Remove leading drive letter from archives created
- * on Windows. */
- if (((p[0] >= 'a' && p[0] <= 'z') ||
- (p[0] >= 'A' && p[0] <= 'Z')) &&
- p[1] == ':') {
- p += 2;
- slashonly = 0;
- }
- /* Remove leading "/../", "//", etc. */
- while (p[0] == '/' || p[0] == '\\') {
- if (p[1] == '.' && p[2] == '.' &&
- (p[3] == '/' || p[3] == '\\')) {
- p += 3; /* Remove "/..", leave "/"
- * for next pass. */
- slashonly = 0;
- } else
- p += 1; /* Remove "/". */
- }
- } while (rp != p);
-
- if (p != name && !bsdtar->warned_lead_slash) {
- /* Generate a warning the first time this happens. */
- if (slashonly)
- lafe_warnc(0,
- "Removing leading '%c' from member names",
- name[0]);
- else
- lafe_warnc(0,
- "Removing leading drive letter from "
- "member names");
- bsdtar->warned_lead_slash = 1;
- }
-
- /* Special case: Stripping everything yields ".". */
- if (*p == '\0')
+ /* By default, don't write or restore absolute pathnames. */
+ name = strip_absolute_path(bsdtar, name);
+ if (*name == '\0')
name = ".";
- else
- name = p;
+
+ if (hardlinkname != NULL) {
+ hardlinkname = strip_absolute_path(bsdtar, hardlinkname);
+ if (*hardlinkname == '\0')
+ return (1);
+ }
} else {
/* Strip redundant leading '/' characters. */
while (name[0] == '/' && name[1] == '/')
name++;
}
- /* Safely replace name in archive_entry. */
- if (name != archive_entry_pathname(entry)) {
- char *q = strdup(name);
- archive_entry_copy_pathname(entry, q);
- free(q);
+ /* Replace name in archive_entry. */
+ if (name != original_name) {
+ archive_entry_copy_pathname(entry, name);
+ }
+ if (hardlinkname != original_hardlinkname) {
+ archive_entry_copy_hardlink(entry, hardlinkname);
}
return (0);
}