]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Fix handling of leading slashes for hardlinks
authorTim Kientzle <kientzle@acm.org>
Mon, 3 Nov 2014 01:11:20 +0000 (17:11 -0800)
committerTim Kientzle <kientzle@acm.org>
Mon, 3 Nov 2014 01:11:36 +0000 (17:11 -0800)
Makefile.am
tar/test/CMakeLists.txt
tar/test/test_leading_slash.c [new file with mode: 0644]
tar/test/test_leading_slash.tar.uu [new file with mode: 0644]
tar/util.c

index 464d1b27cccfa61c870b72d7620d6b09be9c8097..9b9990f4bb9ad0c8b87d944e7db9b88e5cc1ab24 100644 (file)
@@ -889,6 +889,7 @@ bsdtar_test_SOURCES= \
        tar/test/test_extract_tar_xz.c \
        tar/test/test_format_newc.c \
        tar/test/test_help.c \
+       tar/test/test_leading_slash.c \
        tar/test/test_option_C_upper.c \
        tar/test/test_option_H_upper.c \
        tar/test/test_option_L_upper.c \
@@ -958,6 +959,7 @@ bsdtar_test_EXTRA_DIST= \
        tar/test/test_extract.tar.lzma.uu \
        tar/test/test_extract.tar.lzo.uu \
        tar/test/test_extract.tar.xz.uu \
+       tar/test/test_leading_slash.tar.uu \
        tar/test/test_option_keep_newer_files.tar.Z.uu \
        tar/test/test_option_passphrase.zip.uu \
        tar/test/test_option_s.tar.Z.uu \
index ade72ea7c602783b160ea1808a90d788199b7687..9648e4892a3c0809c96d8cecd2babda9ef61216d 100644 (file)
@@ -24,6 +24,7 @@ IF(ENABLE_TAR AND ENABLE_TEST)
     test_extract_tar_xz.c
     test_format_newc.c
     test_help.c
+    test_leading_slash.c
     test_option_C_upper.c
     test_option_H_upper.c
     test_option_L_upper.c
diff --git a/tar/test/test_leading_slash.c b/tar/test/test_leading_slash.c
new file mode 100644 (file)
index 0000000..350aa4a
--- /dev/null
@@ -0,0 +1,41 @@
+/*-
+ * 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");
+}
+
diff --git a/tar/test/test_leading_slash.tar.uu b/tar/test/test_leading_slash.tar.uu
new file mode 100644 (file)
index 0000000..eeb27c3
--- /dev/null
@@ -0,0 +1,60 @@
+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
index 45a912a89616d166f70064e551ed7b6d16e0c907..203aa6f9daed76721b4214150fc952d87bf06135 100644 (file)
@@ -373,20 +373,107 @@ strip_components(const char *p, int elements)
        }
 }
 
+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");
@@ -400,10 +487,12 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *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;
@@ -412,7 +501,11 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry)
                        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) {
@@ -428,94 +521,41 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry)
 
        /* 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);
 }