diff --git a/libarchive/archive_read_support_format_mtree.c b/libarchive/archive_read_support_format_mtree.c index 44799dfc9..d82d4c157 100644 --- a/libarchive/archive_read_support_format_mtree.c +++ b/libarchive/archive_read_support_format_mtree.c @@ -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); diff --git a/libarchive/test/test_read_format_mtree.c b/libarchive/test/test_read_format_mtree.c index 830fa0a9c..f96529daf 100644 --- a/libarchive/test/test_read_format_mtree.c +++ b/libarchive/test/test_read_format_mtree.c @@ -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)); diff --git a/tar/write.c b/tar/write.c index 40d2fb0a9..7e8cb13f8 100644 --- a/tar/write.c +++ b/tar/write.c @@ -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));