]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Add support for a mtree form that NetBSD's mtree -D generates, which places
authorMichihiro NAKAJIMA <ggcueroad@gmail.com>
Thu, 20 Sep 2012 02:34:30 +0000 (11:34 +0900)
committerMichihiro NAKAJIMA <ggcueroad@gmail.com>
Thu, 20 Sep 2012 02:34:30 +0000 (11:34 +0900)
the path as last parameter.

Makefile.am
libarchive/archive_read_support_format_mtree.c
libarchive/test/test_read_format_mtree.c
libarchive/test/test_read_format_mtree_nomagic2.mtree.uu [new file with mode: 0644]
libarchive/test/test_read_format_mtree_nomagic3.mtree.uu [new file with mode: 0644]

index 93413b2e7f3c706667ee07ba6bea6f8bf58a97ab..83cf780bf4a69e3c89795d98930df2d69ed00831 100644 (file)
@@ -558,6 +558,8 @@ libarchive_test_EXTRA_DIST=\
        libarchive/test/test_read_format_lha_withjunk.lzh.uu            \
        libarchive/test/test_read_format_mtree.mtree.uu                 \
        libarchive/test/test_read_format_mtree_nomagic.mtree.uu         \
+       libarchive/test/test_read_format_mtree_nomagic2.mtree.uu        \
+       libarchive/test/test_read_format_mtree_nomagic3.mtree.uu        \
        libarchive/test/test_read_format_rar.rar.uu                     \
        libarchive/test/test_read_format_rar_binary_data.rar.uu         \
        libarchive/test/test_read_format_rar_compress_best.rar.uu       \
index 6fb57629197f1ad4da6562bb2f9c954127a26e53..4f0ffbd9a8e81c3b4f425176f9fca3e04324eaa1 100644 (file)
@@ -1,7 +1,7 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
  * Copyright (c) 2008 Joerg Sonnenberger
- * Copyright (c) 2011 Michihiro NAKAJIMA
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -103,6 +103,7 @@ struct mtree {
 
 static int     bid_keycmp(const char *, const char *, ssize_t);
 static int     cleanup(struct archive_read *);
+static int     detect_form(struct archive_read *, int *);
 static int     mtree_bid(struct archive_read *, int);
 static int     parse_file(struct archive_read *, struct archive_entry *,
                    struct mtree *, struct mtree_entry *, int *);
@@ -419,7 +420,7 @@ bid_keyword(const char *p,  ssize_t len)
  * When "unset" is specified, expects a set of "<space characters>keyword".
  */
 static int
-bid_keyword_list(const char *p,  ssize_t len, int unset)
+bid_keyword_list(const char *p,  ssize_t len, int unset, int last_is_path)
 {
        int l;
        int keycnt = 0;
@@ -437,8 +438,10 @@ bid_keyword_list(const char *p,  ssize_t len, int unset)
                        break;
                if (p[0] == '\\' && (p[1] == '\n' || p[1] == '\r'))
                        break;
-               if (!blank) /* No blank character. */
+               if (!blank && !last_is_path) /* No blank character. */
                        return (-1);
+               if (last_is_path && len == 0)
+                               return (keycnt);
 
                if (unset) {
                        l = bid_keycmp(p, "all", len);
@@ -473,7 +476,7 @@ bid_keyword_list(const char *p,  ssize_t len, int unset)
 }
 
 static int
-bid_entry(const char *p, ssize_t len)
+bid_entry(const char *p, ssize_t len, ssize_t nl, int *last_is_path)
 {
        int f = 0;
        static const unsigned char safe_char[256] = {
@@ -500,22 +503,49 @@ bid_entry(const char *p, ssize_t len)
                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* E0 - EF */
                0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* F0 - FF */
        };
+       ssize_t ll = len;
+       const char *pp = p;
 
+       *last_is_path = 0;
        /*
         * Skip the path-name which is quoted.
         */
-       while (len > 0 && *p != ' ' && *p != '\t') {
-               if (!safe_char[*(const unsigned char *)p])
-                       return (-1);
-               ++p;
-               --len;
+       while (ll > 0 && *pp != ' ' && *pp != '\t') {
+               if (!safe_char[*(const unsigned char *)pp]) {
+                       f = 0;
+                       break;
+               }
+               ++pp;
+               --ll;
                ++f;
        }
-       /* If a path-name was not found, returns error. */
-       if (f == 0)
-               return (-1);
+       /* If a path-name was not found at the first, try to check
+        * a mtree format ``NetBSD's mtree -D'' creates, which
+        * places the path-name at the last. */
+       if (f == 0) {
+               const char *pb = p + len - nl;
+               int name_len = 0;
+
+               /* Do not accept multi lines for form D. */
+               if (pb-2 >= p &&
+                   pb[-1] == '\\' && (pb[-2] == ' ' || pb[-2] == '\t'))
+                       return (-1);
+               if (pb-1 >= p && pb[-1] == '\\')
+                       return (-1);
 
-       return (bid_keyword_list(p, len, 0));
+               while (p <= --pb && *pb != ' ' && *pb != '\t') {
+                       if (!safe_char[*(const unsigned char *)pb])
+                               return (-1);
+                       name_len++;
+               }
+               if (name_len == 0)
+                       return (-1);
+               ll = len - nl - name_len;
+               pp = p;
+               *last_is_path = 1;
+       }
+
+       return (bid_keyword_list(pp, ll, 0, *last_is_path));
 }
 
 #define MAX_BID_ENTRY  3
@@ -525,14 +555,11 @@ mtree_bid(struct archive_read *a, int best_bid)
 {
        const char *signature = "#mtree";
        const char *p;
-       ssize_t avail, ravail;
-       ssize_t len, nl;
-       int detected_bytes = 0, entry_cnt = 0, multiline = 0;
 
        (void)best_bid; /* UNUSED */
 
        /* Now let's look at the actual header and see if it matches. */
-       p = __archive_read_ahead(a, strlen(signature), &avail);
+       p = __archive_read_ahead(a, strlen(signature), NULL);
        if (p == NULL)
                return (-1);
 
@@ -542,6 +569,24 @@ mtree_bid(struct archive_read *a, int best_bid)
        /*
         * There is not a mtree signature. Let's try to detect mtree format.
         */
+       return (detect_form(a, NULL));
+}
+
+static int
+detect_form(struct archive_read *a, int *is_form_d)
+{
+       const char *p;
+       ssize_t avail, ravail;
+       ssize_t len, nl;
+       int detected_bytes = 0, entry_cnt = 0, multiline = 0;
+       int form_D = 0;/* The archive is generated by `NetBSD mtree -D'
+                       * (In this source we call it `form D') . */
+
+       if (is_form_d != NULL)
+               *is_form_d = 0;
+       p = __archive_read_ahead(a, 1, &avail);
+       if (p == NULL)
+               return (-1);
        ravail = avail;
        for (;;) {
                len = next_line(a, &p, &avail, &ravail, &nl);
@@ -566,7 +611,7 @@ mtree_bid(struct archive_read *a, int best_bid)
                } else {
                        /* A continuance line; the terminal
                         * character of previous line was '\' character. */
-                       if (bid_keyword_list(p, len, 0) <= 0)
+                       if (bid_keyword_list(p, len, 0, 0) <= 0)
                                break;
                        if (multiline == 1)
                                detected_bytes += len;
@@ -581,9 +626,25 @@ mtree_bid(struct archive_read *a, int best_bid)
                        continue;
                }
                if (p[0] != '/') {
-                       if (bid_entry(p, len) >= 0) {
+                       int last_is_path, keywords;
+
+                       keywords = bid_entry(p, len, nl, &last_is_path);
+                       if (keywords >= 0) {
                                detected_bytes += len;
-                               if (p[len-nl-1] == '\\')
+                               if (form_D == 0) {
+                                       if (last_is_path)
+                                               form_D = 1;
+                                       else if (keywords > 0)
+                                               /* This line is not `form D'. */
+                                               form_D = -1;
+                               } else if (form_D == 1) {
+                                       if (!last_is_path && keywords > 0)
+                                               /* This this is not `form D'
+                                                * and We cannot accept mixed
+                                                * format. */
+                                               break;
+                               }
+                               if (!last_is_path && p[len-nl-1] == '\\')
                                        /* This line continues. */
                                        multiline = 1;
                                else {
@@ -596,13 +657,13 @@ mtree_bid(struct archive_read *a, int best_bid)
                        } else
                                break;
                } else if (strncmp(p, "/set", 4) == 0) {
-                       if (bid_keyword_list(p+4, len-4, 0) <= 0)
+                       if (bid_keyword_list(p+4, len-4, 0, 0) <= 0)
                                break;
                        /* This line continues. */
                        if (p[len-nl-1] == '\\')
                                multiline = 2;
                } else if (strncmp(p, "/unset", 6) == 0) {
-                       if (bid_keyword_list(p+6, len-6, 1) <= 0)
+                       if (bid_keyword_list(p+6, len-6, 1, 0) <= 0)
                                break;
                        /* This line continues. */
                        if (p[len-nl-1] == '\\')
@@ -614,8 +675,13 @@ mtree_bid(struct archive_read *a, int best_bid)
                p += len;
                avail -= len;
        }
-       if (entry_cnt >= MAX_BID_ENTRY || (entry_cnt > 0 && len == 0))
+       if (entry_cnt >= MAX_BID_ENTRY || (entry_cnt > 0 && len == 0)) {
+               if (is_form_d != NULL) {
+                       if (form_D == 1)
+                               *is_form_d = 1;
+               }
                return (32);
+       }
 
        return (0);
 }
@@ -739,12 +805,12 @@ process_global_unset(struct archive_read *a,
 
 static int
 process_add_entry(struct archive_read *a, struct mtree *mtree,
-    struct mtree_option **global, const char *line,
-    struct mtree_entry **last_entry)
+    struct mtree_option **global, const char *line, ssize_t line_len,
+    struct mtree_entry **last_entry, int is_form_d)
 {
        struct mtree_entry *entry;
        struct mtree_option *iter;
-       const char *next, *eq;
+       const char *next, *eq, *name, *end;
        size_t len;
        int r;
 
@@ -765,17 +831,46 @@ process_add_entry(struct archive_read *a, struct mtree *mtree,
                (*last_entry)->next = entry;
        *last_entry = entry;
 
-       len = strcspn(line, " \t\r\n");
+       if (is_form_d) {
+               /*
+                * This form places the file name as last parameter.
+                */
+               name = line + line_len -1;
+               while (line_len > 0) {
+                       if (*name != '\r' && *name != '\n' &&
+                           *name != '\t' && *name != ' ')
+                               break;
+                       name--;
+                       line_len--;
+               }
+               len = 0;
+               while (line_len > 0) {
+                       if (*name == '\r' || *name == '\n' ||
+                           *name == '\t' || *name == ' ') {
+                               name++;
+                               break;
+                       }
+                       name--;
+                       line_len--;
+                       len++;
+               }
+               end = name;
+       } else {
+               len = strcspn(line, " \t\r\n");
+               name = line;
+               line += len;
+               end = line + line_len;
+       }
+
        if ((entry->name = malloc(len + 1)) == NULL) {
                archive_set_error(&a->archive, errno, "Can't allocate memory");
                return (ARCHIVE_FATAL);
        }
 
-       memcpy(entry->name, line, len);
+       memcpy(entry->name, name, len);
        entry->name[len] = '\0';
        parse_escapes(entry->name, entry);
 
-       line += len;
        for (iter = *global; iter != NULL; iter = iter->next) {
                r = add_option(a, &entry->options, iter->value,
                    strlen(iter->value));
@@ -787,6 +882,8 @@ process_add_entry(struct archive_read *a, struct mtree *mtree,
                next = line + strspn(line, " \t\r\n");
                if (*next == '\0')
                        return (ARCHIVE_OK);
+               if (next >= end)
+                       return (ARCHIVE_OK);
                line = next;
                next = line + strcspn(line, " \t\r\n");
                eq = strchr(line, '=');
@@ -811,7 +908,7 @@ read_mtree(struct archive_read *a, struct mtree *mtree)
        char *p;
        struct mtree_option *global;
        struct mtree_entry *last_entry;
-       int r;
+       int r, is_form_d;
 
        mtree->archive_format = ARCHIVE_FORMAT_MTREE;
        mtree->archive_format_name = "mtree";
@@ -819,6 +916,8 @@ read_mtree(struct archive_read *a, struct mtree *mtree)
        global = NULL;
        last_entry = NULL;
 
+       (void)detect_form(a, &is_form_d);
+
        for (counter = 1; ; ++counter) {
                len = readline(a, mtree, &p, 65536);
                if (len == 0) {
@@ -841,8 +940,8 @@ read_mtree(struct archive_read *a, struct mtree *mtree)
                if (*p == '\r' || *p == '\n' || *p == '\0')
                        continue;
                if (*p != '/') {
-                       r = process_add_entry(a, mtree, &global, p,
-                           &last_entry);
+                       r = process_add_entry(a, mtree, &global, p, len,
+                           &last_entry, is_form_d);
                } else if (strncmp(p, "/set", 4) == 0) {
                        if (p[4] != ' ' && p[4] != '\t')
                                break;
index a5d7febe1763a197d9ecd9dc8d878cd0cbcbe9f8..67466cd6242172f4a0c5ae4467731b63347005cd 100644 (file)
@@ -1,5 +1,6 @@
 /*-
  * Copyright (c) 2003-2007 Tim Kientzle
+ * Copyright (c) 2011-2012 Michihiro NAKAJIMA
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -340,6 +341,146 @@ test_read_format_mtree5(void)
        assertChdir("..");
 }
 
+/*
+ * Test for a format that NetBSD mtree -C generates.
+ */
+static void
+test_read_format_mtree6(void)
+{
+       const char reffile[] = "test_read_format_mtree_nomagic2.mtree";
+       char buff[16];
+       struct archive_entry *ae;
+       struct archive *a;
+       FILE *f;
+
+       assertMakeDir("mtree6", 0777);
+       assertChdir("mtree6");
+
+       extract_reference_file(reffile);
+
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_open_file(a, reffile, 11));
+
+       /*
+        * Read "file", whose data is available on disk.
+        */
+       f = fopen("file", "wb");
+       assert(f != NULL);
+       assertEqualInt(3, fwrite("hi\n", 1, 3, f));
+       fclose(f);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt(archive_format(a), ARCHIVE_FORMAT_MTREE);
+       assertEqualString(archive_entry_pathname(ae), "./file");
+       assertEqualInt(archive_entry_uid(ae), 18);
+       assertEqualInt(AE_IFREG, archive_entry_filetype(ae));
+       assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0123);
+       assertEqualInt(archive_entry_size(ae), 3);
+       assertEqualInt(3, archive_read_data(a, buff, 3));
+       assertEqualMem(buff, "hi\n", 3);
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualString(archive_entry_pathname(ae), "./dir");
+       assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755);
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualString(archive_entry_pathname(ae), "./dir/file with space");
+       assertEqualInt(archive_entry_uid(ae), 18);
+       assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644);
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualString(archive_entry_pathname(ae), "./file with space");
+       assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644);
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualString(archive_entry_pathname(ae), "./dir2");
+       assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755);
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualString(archive_entry_pathname(ae), "./dir2/dir3a");
+       assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755);
+
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+       assertEqualInt(6, archive_file_count(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+       assertChdir("..");
+}
+
+/*
+ * Test for a format that NetBSD mtree -D generates.
+ */
+static void
+test_read_format_mtree7(void)
+{
+       const char reffile[] = "test_read_format_mtree_nomagic3.mtree";
+       char buff[16];
+       struct archive_entry *ae;
+       struct archive *a;
+       FILE *f;
+
+       assertMakeDir("mtree7", 0777);
+       assertChdir("mtree7");
+
+       extract_reference_file(reffile);
+
+       assert((a = archive_read_new()) != NULL);
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_filter_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_open_file(a, reffile, 11));
+
+       /*
+        * Read "file", whose data is available on disk.
+        */
+       f = fopen("file", "wb");
+       assert(f != NULL);
+       assertEqualInt(3, fwrite("hi\n", 1, 3, f));
+       fclose(f);
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualInt(archive_format(a), ARCHIVE_FORMAT_MTREE);
+       assertEqualString(archive_entry_pathname(ae), "./file");
+       assertEqualInt(archive_entry_uid(ae), 18);
+       assertEqualInt(AE_IFREG, archive_entry_filetype(ae));
+       assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0123);
+       assertEqualInt(archive_entry_size(ae), 3);
+       assertEqualInt(3, archive_read_data(a, buff, 3));
+       assertEqualMem(buff, "hi\n", 3);
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualString(archive_entry_pathname(ae), "./dir");
+       assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755);
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualString(archive_entry_pathname(ae), "./dir/file with space");
+       assertEqualInt(archive_entry_uid(ae), 18);
+       assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644);
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualString(archive_entry_pathname(ae), "./file with space");
+       assertEqualInt(archive_entry_mode(ae), AE_IFREG | 0644);
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualString(archive_entry_pathname(ae), "./dir2");
+       assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755);
+
+       assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
+       assertEqualString(archive_entry_pathname(ae), "./dir2/dir3a");
+       assertEqualInt(archive_entry_mode(ae), AE_IFDIR | 0755);
+
+       assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae));
+       assertEqualInt(6, archive_file_count(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_close(a));
+       assertEqualInt(ARCHIVE_OK, archive_read_free(a));
+
+       assertChdir("..");
+}
+
 DEFINE_TEST(test_read_format_mtree)
 {
        test_read_format_mtree1();
@@ -347,4 +488,6 @@ DEFINE_TEST(test_read_format_mtree)
        test_read_format_mtree3();
        test_read_format_mtree4();
        test_read_format_mtree5();
+       test_read_format_mtree6();
+       test_read_format_mtree7();
 }
diff --git a/libarchive/test/test_read_format_mtree_nomagic2.mtree.uu b/libarchive/test/test_read_format_mtree_nomagic2.mtree.uu
new file mode 100644 (file)
index 0000000..f27fb2b
--- /dev/null
@@ -0,0 +1,10 @@
+begin 644 test_read_format_mtree_nomagic2.mtree
+M(PHC(%1H:7,@9F]R;2!I<R!G96YE<F%T960@8GD@3F5T0E-$(&UT<F5E("U#
+M"B,*+B]F:6QE('1Y<&4]9FEL92!U:60],3@@;6]D93TP,3(S('-I>F4],PHN
+M+V1I<B!T>7!E/61I<B!M;V1E/3`W-34*+B]D:7(O9FEL95PP-#!W:71H7#`T
+M,'-P86-E('1Y<&4]9FEL92!U:60],3@@;6]D93TP-C0T"BXO9FEL95PP-#!W
+M:71H7#`T,'-P86-E('1Y<&4]9FEL92!M;V1E/3`V-#0*+B]D:7(R('1Y<&4]
+M9&ER(&UO9&4],#<U-0HN+V1I<C(O9&ER,V$@='EP93UD:7(@;6]D93TP-S4U
+!"@``
+`
+end
diff --git a/libarchive/test/test_read_format_mtree_nomagic3.mtree.uu b/libarchive/test/test_read_format_mtree_nomagic3.mtree.uu
new file mode 100644 (file)
index 0000000..97de341
--- /dev/null
@@ -0,0 +1,10 @@
+begin 644 test_read_format_mtree_nomagic3.mtree
+M(PHC(%1H:7,@9F]R;2!I<R!G96YE<F%T960@8GD@3F5T0E-$(&UT<F5E("U$
+M"B,*='EP93UF:6QE('5I9#TQ."!M;V1E/3`Q,C,@<VEZ93TS("XO9FEL90IT
+M>7!E/61I<B!M;V1E/3`W-34@+B]D:7(*='EP93UF:6QE(&UO9&4],#8T-"!U
+M:60],3@@+B]D:7(O9FEL95PP-#!W:71H7#`T,'-P86-E"G1Y<&4]9FEL92!M
+M;V1E/3`V-#0@+B]F:6QE7#`T,'=I=&A<,#0P<W!A8V4*='EP93UD:7(@;6]D
+M93TP-S4U("XO9&ER,@IT>7!E/61I<B!M;V1E/3`W-34@+B]D:7(R+V1I<C-A
+!"@``
+`
+end