]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
mtree: Make reading additional information from the fs optional 65/head
authorFlorian Pritz <bluewind@xinu.at>
Sat, 1 Mar 2014 16:21:47 +0000 (17:21 +0100)
committerFlorian Pritz <bluewind@xinu.at>
Sun, 2 Mar 2014 11:22:14 +0000 (12:22 +0100)
This feature is not needed if users just want to read in the content of
an mtree file and do validation against the file system themselves.

It is needed for `bsdtar cvf out.tar @input.mtree` which is why the
option is enabled in bsdtar.

Since the mtree tests rely on this feature, this patch also enables it
there.

Signed-off-by: Florian Pritz <bluewind@xinu.at>
libarchive/archive_read_support_format_mtree.c
libarchive/test/test_read_format_mtree.c
tar/write.c

index 44799dfc95d7b624b603f560afcd48ee9d3c1492..d82d4c157353e9cb6429a0e241986b00d0cc12e6 100644 (file)
@@ -104,6 +104,7 @@ struct mtree {
        struct archive_entry_linkresolver *resolver;
 
        int64_t                  cur_size;
+       char checkfs;
 };
 
 static int     bid_keycmp(const char *, const char *, ssize_t);
@@ -174,6 +175,29 @@ get_time_t_min(void)
 #endif
 }
 
+static int
+archive_read_format_mtree_options(struct archive_read *a,
+    const char *key, const char *val)
+{
+       struct mtree *mtree;
+
+       mtree = (struct mtree *)(a->format->data);
+       if (strcmp(key, "checkfs")  == 0) {
+               /* Allows to read information missing from the mtree from the file system */
+               if (val == NULL || val[0] == 0) {
+                       mtree->checkfs = 0;
+               } else {
+                       mtree->checkfs = 1;
+               }
+               return (ARCHIVE_OK);
+       }
+
+       /* Note: The "warn" return is just to inform the options
+        * supervisor that we didn't handle it.  It will generate
+        * a suitable error if no one used this option. */
+       return (ARCHIVE_WARN);
+}
+
 static void
 free_options(struct mtree_option *head)
 {
@@ -206,7 +230,7 @@ archive_read_support_format_mtree(struct archive *_a)
        mtree->fd = -1;
 
        r = __archive_read_register_format(a, mtree, "mtree",
-           mtree_bid, NULL, read_header, read_data, skip, NULL, cleanup, NULL, NULL);
+           mtree_bid, archive_read_format_mtree_options, read_header, read_data, skip, NULL, cleanup, NULL, NULL);
 
        if (r != ARCHIVE_OK)
                free(mtree);
@@ -1104,162 +1128,164 @@ parse_file(struct archive_read *a, struct archive_entry *entry,
                        mtree->current_dir.length = n;
        }
 
-       /*
-        * Try to open and stat the file to get the real size
-        * and other file info.  It would be nice to avoid
-        * this here so that getting a listing of an mtree
-        * wouldn't require opening every referenced contents
-        * file.  But then we wouldn't know the actual
-        * contents size, so I don't see a really viable way
-        * around this.  (Also, we may want to someday pull
-        * other unspecified info from the contents file on
-        * disk.)
-        */
-       mtree->fd = -1;
-       if (archive_strlen(&mtree->contents_name) > 0)
-               path = mtree->contents_name.s;
-       else
-               path = archive_entry_pathname(entry);
-
-       if (archive_entry_filetype(entry) == AE_IFREG ||
-           archive_entry_filetype(entry) == AE_IFDIR) {
-               mtree->fd = open(path, O_RDONLY | O_BINARY | O_CLOEXEC);
-               __archive_ensure_cloexec_flag(mtree->fd);
-               if (mtree->fd == -1 &&
-                   (errno != ENOENT ||
-                    archive_strlen(&mtree->contents_name) > 0)) {
-                       archive_set_error(&a->archive, errno,
-                           "Can't open %s", path);
-                       r = ARCHIVE_WARN;
+       if (mtree->checkfs) {
+               /*
+                * Try to open and stat the file to get the real size
+                * and other file info.  It would be nice to avoid
+                * this here so that getting a listing of an mtree
+                * wouldn't require opening every referenced contents
+                * file.  But then we wouldn't know the actual
+                * contents size, so I don't see a really viable way
+                * around this.  (Also, we may want to someday pull
+                * other unspecified info from the contents file on
+                * disk.)
+                */
+               mtree->fd = -1;
+               if (archive_strlen(&mtree->contents_name) > 0)
+                       path = mtree->contents_name.s;
+               else
+                       path = archive_entry_pathname(entry);
+
+               if (archive_entry_filetype(entry) == AE_IFREG ||
+                               archive_entry_filetype(entry) == AE_IFDIR) {
+                       mtree->fd = open(path, O_RDONLY | O_BINARY | O_CLOEXEC);
+                       __archive_ensure_cloexec_flag(mtree->fd);
+                       if (mtree->fd == -1 &&
+                                       (errno != ENOENT ||
+                                        archive_strlen(&mtree->contents_name) > 0)) {
+                               archive_set_error(&a->archive, errno,
+                                               "Can't open %s", path);
+                               r = ARCHIVE_WARN;
+                       }
                }
-       }
 
-       st = &st_storage;
-       if (mtree->fd >= 0) {
-               if (fstat(mtree->fd, st) == -1) {
-                       archive_set_error(&a->archive, errno,
-                           "Could not fstat %s", path);
-                       r = ARCHIVE_WARN;
-                       /* If we can't stat it, don't keep it open. */
-                       close(mtree->fd);
-                       mtree->fd = -1;
+               st = &st_storage;
+               if (mtree->fd >= 0) {
+                       if (fstat(mtree->fd, st) == -1) {
+                               archive_set_error(&a->archive, errno,
+                                               "Could not fstat %s", path);
+                               r = ARCHIVE_WARN;
+                               /* If we can't stat it, don't keep it open. */
+                               close(mtree->fd);
+                               mtree->fd = -1;
+                               st = NULL;
+                       }
+               } else if (lstat(path, st) == -1) {
                        st = NULL;
                }
-       } else if (lstat(path, st) == -1) {
-               st = NULL;
-       }
 
-       /*
-        * Check for a mismatch between the type in the specification and
-        * the type of the contents object on disk.
-        */
-       if (st != NULL) {
-               if (
-                   ((st->st_mode & S_IFMT) == S_IFREG &&
-                    archive_entry_filetype(entry) == AE_IFREG)
+               /*
+                * Check for a mismatch between the type in the specification and
+                * the type of the contents object on disk.
+                */
+               if (st != NULL) {
+                       if (
+                                       ((st->st_mode & S_IFMT) == S_IFREG &&
+                                        archive_entry_filetype(entry) == AE_IFREG)
 #ifdef S_IFLNK
-                   || ((st->st_mode & S_IFMT) == S_IFLNK &&
-                       archive_entry_filetype(entry) == AE_IFLNK)
+                                       || ((st->st_mode & S_IFMT) == S_IFLNK &&
+                                               archive_entry_filetype(entry) == AE_IFLNK)
 #endif
 #ifdef S_IFSOCK
-                   || ((st->st_mode & S_IFSOCK) == S_IFSOCK &&
-                       archive_entry_filetype(entry) == AE_IFSOCK)
+                                       || ((st->st_mode & S_IFSOCK) == S_IFSOCK &&
+                                               archive_entry_filetype(entry) == AE_IFSOCK)
 #endif
 #ifdef S_IFCHR
-                   || ((st->st_mode & S_IFMT) == S_IFCHR &&
-                       archive_entry_filetype(entry) == AE_IFCHR)
+                                       || ((st->st_mode & S_IFMT) == S_IFCHR &&
+                                               archive_entry_filetype(entry) == AE_IFCHR)
 #endif
 #ifdef S_IFBLK
-                   || ((st->st_mode & S_IFMT) == S_IFBLK &&
-                       archive_entry_filetype(entry) == AE_IFBLK)
+                                       || ((st->st_mode & S_IFMT) == S_IFBLK &&
+                                               archive_entry_filetype(entry) == AE_IFBLK)
 #endif
-                   || ((st->st_mode & S_IFMT) == S_IFDIR &&
-                       archive_entry_filetype(entry) == AE_IFDIR)
+                                       || ((st->st_mode & S_IFMT) == S_IFDIR &&
+                                               archive_entry_filetype(entry) == AE_IFDIR)
 #ifdef S_IFIFO
-                   || ((st->st_mode & S_IFMT) == S_IFIFO &&
-                       archive_entry_filetype(entry) == AE_IFIFO)
+                                       || ((st->st_mode & S_IFMT) == S_IFIFO &&
+                                                       archive_entry_filetype(entry) == AE_IFIFO)
 #endif
-                   ) {
-                       /* Types match. */
-               } else {
-                       /* Types don't match; bail out gracefully. */
-                       if (mtree->fd >= 0)
-                               close(mtree->fd);
-                       mtree->fd = -1;
-                       if (parsed_kws & MTREE_HAS_OPTIONAL) {
-                               /* It's not an error for an optional entry
-                                  to not match disk. */
-                               *use_next = 1;
-                       } else if (r == ARCHIVE_OK) {
-                               archive_set_error(&a->archive,
-                                   ARCHIVE_ERRNO_MISC,
-                                   "mtree specification has different type for %s",
-                                   archive_entry_pathname(entry));
-                               r = ARCHIVE_WARN;
-                       }
-                       return r;
+                                       ) {
+                                               /* Types match. */
+                                       } else {
+                                               /* Types don't match; bail out gracefully. */
+                                               if (mtree->fd >= 0)
+                                                       close(mtree->fd);
+                                               mtree->fd = -1;
+                                               if (parsed_kws & MTREE_HAS_OPTIONAL) {
+                                                       /* It's not an error for an optional entry
+                                                          to not match disk. */
+                                                       *use_next = 1;
+                                               } else if (r == ARCHIVE_OK) {
+                                                       archive_set_error(&a->archive,
+                                                                       ARCHIVE_ERRNO_MISC,
+                                                                       "mtree specification has different type for %s",
+                                                                       archive_entry_pathname(entry));
+                                                       r = ARCHIVE_WARN;
+                                               }
+                                               return r;
+                                       }
                }
-       }
 
-       /*
-        * If there is a contents file on disk, pick some of the metadata
-        * from that file.  For most of these, we only set it from the contents
-        * if it wasn't already parsed from the specification.
-        */
-       if (st != NULL) {
-               if (((parsed_kws & MTREE_HAS_DEVICE) == 0 ||
-                    (parsed_kws & MTREE_HAS_NOCHANGE) != 0) &&
-                   (archive_entry_filetype(entry) == AE_IFCHR ||
-                    archive_entry_filetype(entry) == AE_IFBLK))
-                       archive_entry_set_rdev(entry, st->st_rdev);
-               if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME)) == 0 ||
-                   (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
-                       archive_entry_set_gid(entry, st->st_gid);
-               if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME)) == 0 ||
-                   (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
-                       archive_entry_set_uid(entry, st->st_uid);
-               if ((parsed_kws & MTREE_HAS_MTIME) == 0 ||
-                   (parsed_kws & MTREE_HAS_NOCHANGE) != 0) {
+               /*
+                * If there is a contents file on disk, pick some of the metadata
+                * from that file.  For most of these, we only set it from the contents
+                * if it wasn't already parsed from the specification.
+                */
+               if (st != NULL) {
+                       if (((parsed_kws & MTREE_HAS_DEVICE) == 0 ||
+                                               (parsed_kws & MTREE_HAS_NOCHANGE) != 0) &&
+                                       (archive_entry_filetype(entry) == AE_IFCHR ||
+                                        archive_entry_filetype(entry) == AE_IFBLK))
+                               archive_entry_set_rdev(entry, st->st_rdev);
+                       if ((parsed_kws & (MTREE_HAS_GID | MTREE_HAS_GNAME)) == 0 ||
+                                       (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+                               archive_entry_set_gid(entry, st->st_gid);
+                       if ((parsed_kws & (MTREE_HAS_UID | MTREE_HAS_UNAME)) == 0 ||
+                                       (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+                               archive_entry_set_uid(entry, st->st_uid);
+                       if ((parsed_kws & MTREE_HAS_MTIME) == 0 ||
+                                       (parsed_kws & MTREE_HAS_NOCHANGE) != 0) {
 #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
-                       archive_entry_set_mtime(entry, st->st_mtime,
-                           st->st_mtimespec.tv_nsec);
+                               archive_entry_set_mtime(entry, st->st_mtime,
+                                               st->st_mtimespec.tv_nsec);
 #elif HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
-                       archive_entry_set_mtime(entry, st->st_mtime,
-                           st->st_mtim.tv_nsec);
+                               archive_entry_set_mtime(entry, st->st_mtime,
+                                               st->st_mtim.tv_nsec);
 #elif HAVE_STRUCT_STAT_ST_MTIME_N
-                       archive_entry_set_mtime(entry, st->st_mtime,
-                           st->st_mtime_n);
+                               archive_entry_set_mtime(entry, st->st_mtime,
+                                               st->st_mtime_n);
 #elif HAVE_STRUCT_STAT_ST_UMTIME
-                       archive_entry_set_mtime(entry, st->st_mtime,
-                           st->st_umtime*1000);
+                               archive_entry_set_mtime(entry, st->st_mtime,
+                                               st->st_umtime*1000);
 #elif HAVE_STRUCT_STAT_ST_MTIME_USEC
-                       archive_entry_set_mtime(entry, st->st_mtime,
-                           st->st_mtime_usec*1000);
+                               archive_entry_set_mtime(entry, st->st_mtime,
+                                               st->st_mtime_usec*1000);
 #else
-                       archive_entry_set_mtime(entry, st->st_mtime, 0);
+                               archive_entry_set_mtime(entry, st->st_mtime, 0);
 #endif
+                       }
+                       if ((parsed_kws & MTREE_HAS_NLINK) == 0 ||
+                                       (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+                               archive_entry_set_nlink(entry, st->st_nlink);
+                       if ((parsed_kws & MTREE_HAS_PERM) == 0 ||
+                                       (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+                               archive_entry_set_perm(entry, st->st_mode);
+                       if ((parsed_kws & MTREE_HAS_SIZE) == 0 ||
+                                       (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
+                               archive_entry_set_size(entry, st->st_size);
+                       archive_entry_set_ino(entry, st->st_ino);
+                       archive_entry_set_dev(entry, st->st_dev);
+
+                       archive_entry_linkify(mtree->resolver, &entry, &sparse_entry);
+               } else if (parsed_kws & MTREE_HAS_OPTIONAL) {
+                       /*
+                        * Couldn't open the entry, stat it or the on-disk type
+                        * didn't match.  If this entry is optional, just ignore it
+                        * and read the next header entry.
+                        */
+                       *use_next = 1;
+                       return ARCHIVE_OK;
                }
-               if ((parsed_kws & MTREE_HAS_NLINK) == 0 ||
-                   (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
-                       archive_entry_set_nlink(entry, st->st_nlink);
-               if ((parsed_kws & MTREE_HAS_PERM) == 0 ||
-                   (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
-                       archive_entry_set_perm(entry, st->st_mode);
-               if ((parsed_kws & MTREE_HAS_SIZE) == 0 ||
-                   (parsed_kws & MTREE_HAS_NOCHANGE) != 0)
-                       archive_entry_set_size(entry, st->st_size);
-               archive_entry_set_ino(entry, st->st_ino);
-               archive_entry_set_dev(entry, st->st_dev);
-
-               archive_entry_linkify(mtree->resolver, &entry, &sparse_entry);
-       } else if (parsed_kws & MTREE_HAS_OPTIONAL) {
-               /*
-                * Couldn't open the entry, stat it or the on-disk type
-                * didn't match.  If this entry is optional, just ignore it
-                * and read the next header entry.
-                */
-               *use_next = 1;
-               return ARCHIVE_OK;
        }
 
        mtree->cur_size = archive_entry_size(entry);
index 830fa0a9cf40dd906378bc3cf5ef164f1baaf2ad..f96529daf7a9c2292e83a5bf6ab051ccfb7bc1fa 100644 (file)
@@ -57,6 +57,8 @@ test_read_format_mtree1(void)
            archive_read_support_filter_all(a));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_set_options(a, "mtree:checkfs"));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_open_filename(a, reffile, 11));
 
@@ -208,6 +210,8 @@ test_read_format_mtree2(void)
            archive_read_support_filter_all(a));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_set_options(a, "mtree:checkfs"));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_open_memory(a, archive, sizeof(archive)));
        assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
@@ -245,6 +249,8 @@ test_read_format_mtree3(void)
            archive_read_support_filter_all(a));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_set_options(a, "mtree:checkfs"));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_open_memory(a, archive, sizeof(archive)));
        assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
@@ -298,6 +304,8 @@ DEFINE_TEST(test_read_format_mtree_filenames_only)
            archive_read_support_filter_all(a));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_set_options(a, "mtree:checkfs"));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_open_memory(a, archive, sizeof(archive)));
        assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
@@ -364,6 +372,8 @@ DEFINE_TEST(test_read_format_mtree_nochange)
            archive_read_support_filter_all(a));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_set_options(a, "mtree:checkfs"));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_open_memory(a, archive, sizeof(archive)));
        assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
@@ -401,6 +411,8 @@ DEFINE_TEST(test_read_format_mtree_nochange)
            archive_read_support_filter_all(a));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_set_options(a, "mtree:checkfs"));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_open_memory(a, archive2, sizeof(archive2)));
        assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae));
@@ -448,6 +460,8 @@ DEFINE_TEST(test_read_format_mtree_nomagic_v1_form)
            archive_read_support_filter_all(a));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_set_options(a, "mtree:checkfs"));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_open_filename(a, reffile, 11));
 
@@ -551,6 +565,8 @@ DEFINE_TEST(test_read_format_mtree_nomagic_v2_form)
            archive_read_support_filter_all(a));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_set_options(a, "mtree:checkfs"));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_open_filename(a, reffile, 11));
 
@@ -616,6 +632,8 @@ DEFINE_TEST(test_read_format_mtree_nomagic_v2_netbsd_form)
            archive_read_support_filter_all(a));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_set_options(a, "mtree:checkfs"));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_open_filename(a, reffile, 11));
 
@@ -679,6 +697,8 @@ DEFINE_TEST(test_read_format_mtree_nonexistent_contents_file)
            archive_read_support_filter_all(a));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_support_format_all(a));
+       assertEqualIntA(a, ARCHIVE_OK,
+           archive_read_set_options(a, "mtree:checkfs"));
        assertEqualIntA(a, ARCHIVE_OK,
            archive_read_open_memory(a, archive, sizeof(archive)));
        assertEqualIntA(a, ARCHIVE_WARN, archive_read_next_header(a, &ae));
index 40d2fb0a9f52cf63d9e0363458799611a7b99a40..7e8cb13f8ce1f8cd404621c1634e81645ceaf5ce 100644 (file)
@@ -648,6 +648,7 @@ append_archive_filename(struct bsdtar *bsdtar, struct archive *a,
        archive_read_support_format_all(ina);
        archive_read_support_filter_all(ina);
        set_reader_options(bsdtar, a);
+       archive_read_set_options(ina, "mtree:checkfs");
        if (archive_read_open_filename(ina, filename,
                                        bsdtar->bytes_per_block)) {
                lafe_warnc(0, "%s", archive_error_string(ina));