static void test_exclusion_and_inclusion(void) { struct archive_entry *ae; struct archive *m; const char *mp; const wchar_t *wp; if (!assert((m = archive_match_new()) != NULL)) return; if (!assert((ae = archive_entry_new()) != NULL)) { archive_match_free(m); return; } assertEqualIntA(m, 0, archive_match_exclude_pattern(m, "^aaa*")); assertEqualIntA(m, 0, archive_match_include_pattern_w(m, L"^aa*")); assertEqualIntA(m, 0, archive_match_include_pattern(m, "^a1*")); /* Test with 'aa1234', which should not be excluded. */ archive_entry_copy_pathname(ae, "aa1234"); failure("'aa1234' should not be excluded"); assertEqualInt(0, archive_match_path_excluded(m, ae)); assertEqualInt(0, archive_match_excluded(m, ae)); archive_entry_clear(ae); archive_entry_copy_pathname_w(ae, L"aa1234"); failure("'aa1234' should not be excluded"); assertEqualInt(0, archive_match_path_excluded(m, ae)); assertEqualInt(0, archive_match_excluded(m, ae)); /* Test with 'aaa1234', which should be excluded. */ archive_entry_copy_pathname(ae, "aaa1234"); failure("'aaa1234' should be excluded"); assertEqualInt(1, archive_match_path_excluded(m, ae)); assertEqualInt(1, archive_match_excluded(m, ae)); archive_entry_clear(ae); archive_entry_copy_pathname_w(ae, L"aaa1234"); failure("'aaa1234' should be excluded"); assertEqualInt(1, archive_match_path_excluded(m, ae)); assertEqualInt(1, archive_match_excluded(m, ae)); /* Verify unmatched_inclusions. */ assertEqualInt(1, archive_match_path_unmatched_inclusions(m)); /* Verify unmatched inclusion patterns. */ assertEqualIntA(m, ARCHIVE_OK, archive_match_path_unmatched_inclusions_next(m, &mp)); assertEqualString("^a1*", mp); assertEqualIntA(m, ARCHIVE_EOF, archive_match_path_unmatched_inclusions_next(m, &mp)); /* Verify unmatched inclusion patterns again in Wide-Char. */ assertEqualIntA(m, ARCHIVE_OK, archive_match_path_unmatched_inclusions_next_w(m, &wp)); assertEqualWString(L"^a1*", wp); assertEqualIntA(m, ARCHIVE_EOF, archive_match_path_unmatched_inclusions_next_w(m, &wp)); /* Clean up. */ archive_entry_free(ae); archive_match_free(m); }
static void test_inclusion_wcs(void) { struct archive_entry *ae; struct archive *m; const char *mp; if (!assert((m = archive_match_new()) != NULL)) return; if (!assert((ae = archive_entry_new()) != NULL)) { archive_match_free(m); return; } /* Test for pattern "^aa*" */ assertEqualIntA(m, 0, archive_match_include_pattern_w(m, L"^aa*")); /* Test with 'aa1234', which should not be excluded. */ archive_entry_copy_pathname(ae, "aa1234"); failure("'aa1234' should not be excluded"); assertEqualInt(0, archive_match_path_excluded(m, ae)); assertEqualInt(0, archive_match_excluded(m, ae)); archive_entry_clear(ae); archive_entry_copy_pathname_w(ae, L"aa1234"); failure("'aa1234' should not be excluded"); assertEqualInt(0, archive_match_path_excluded(m, ae)); assertEqualInt(0, archive_match_excluded(m, ae)); /* Test with 'a1234', which should be excluded. */ archive_entry_copy_pathname(ae, "a1234"); failure("'a1234' should be excluded"); assertEqualInt(1, archive_match_path_excluded(m, ae)); assertEqualInt(1, archive_match_excluded(m, ae)); archive_entry_clear(ae); archive_entry_copy_pathname_w(ae, L"a1234"); failure("'a1234' should be excluded"); assertEqualInt(1, archive_match_path_excluded(m, ae)); assertEqualInt(1, archive_match_excluded(m, ae)); /* Verify unmatched_inclusions. */ assertEqualInt(0, archive_match_path_unmatched_inclusions(m)); assertEqualIntA(m, ARCHIVE_EOF, archive_match_path_unmatched_inclusions_next(m, &mp)); /* Clean up. */ archive_entry_free(ae); archive_match_free(m); }
static void exclusion_from_file(struct archive *m) { struct archive_entry *ae; if (!assert((ae = archive_entry_new()) != NULL)) { archive_match_free(m); return; } /* Test with 'first', which should not be excluded. */ archive_entry_copy_pathname(ae, "first"); failure("'first' should not be excluded"); assertEqualInt(0, archive_match_path_excluded(m, ae)); assertEqualInt(0, archive_match_excluded(m, ae)); archive_entry_clear(ae); archive_entry_copy_pathname_w(ae, L"first"); failure("'first' should not be excluded"); assertEqualInt(0, archive_match_path_excluded(m, ae)); assertEqualInt(0, archive_match_excluded(m, ae)); /* Test with 'second', which should be excluded. */ archive_entry_copy_pathname(ae, "second"); failure("'second' should be excluded"); assertEqualInt(1, archive_match_path_excluded(m, ae)); assertEqualInt(1, archive_match_excluded(m, ae)); archive_entry_clear(ae); archive_entry_copy_pathname_w(ae, L"second"); failure("'second' should be excluded"); assertEqualInt(1, archive_match_path_excluded(m, ae)); assertEqualInt(1, archive_match_excluded(m, ae)); /* Test with 'third', which should not be excluded. */ archive_entry_copy_pathname(ae, "third"); failure("'third' should not be excluded"); assertEqualInt(0, archive_match_path_excluded(m, ae)); assertEqualInt(0, archive_match_excluded(m, ae)); archive_entry_clear(ae); archive_entry_copy_pathname_w(ae, L"third"); failure("'third' should not be excluded"); assertEqualInt(0, archive_match_path_excluded(m, ae)); assertEqualInt(0, archive_match_excluded(m, ae)); /* Test with 'four', which should be excluded. */ archive_entry_copy_pathname(ae, "four"); failure("'four' should be excluded"); assertEqualInt(1, archive_match_path_excluded(m, ae)); assertEqualInt(1, archive_match_excluded(m, ae)); archive_entry_clear(ae); archive_entry_copy_pathname_w(ae, L"four"); failure("'four' should be excluded"); assertEqualInt(1, archive_match_path_excluded(m, ae)); assertEqualInt(1, archive_match_excluded(m, ae)); /* Clean up. */ archive_entry_free(ae); }
static void test_exclusion_mbs(void) { struct archive_entry *ae; struct archive *m; if (!assert((m = archive_match_new()) != NULL)) return; if (!assert((ae = archive_entry_new()) != NULL)) { archive_match_free(m); return; } /* Test for pattern "^aa*" */ assertEqualIntA(m, 0, archive_match_exclude_pattern(m, "^aa*")); /* Test with 'aa1234', which should be excluded. */ archive_entry_copy_pathname(ae, "aa1234"); failure("'aa1234' should be excluded"); assertEqualInt(1, archive_match_path_excluded(m, ae)); assertEqualInt(1, archive_match_excluded(m, ae)); archive_entry_clear(ae); archive_entry_copy_pathname_w(ae, L"aa1234"); failure("'aa1234' should be excluded"); assertEqualInt(1, archive_match_path_excluded(m, ae)); assertEqualInt(1, archive_match_excluded(m, ae)); /* Test with 'a1234', which should not be excluded. */ archive_entry_copy_pathname(ae, "a1234"); failure("'a1234' should not be excluded"); assertEqualInt(0, archive_match_path_excluded(m, ae)); assertEqualInt(0, archive_match_excluded(m, ae)); archive_entry_clear(ae); archive_entry_copy_pathname_w(ae, L"a1234"); failure("'a1234' should not be excluded"); assertEqualInt(0, archive_match_path_excluded(m, ae)); assertEqualInt(0, archive_match_excluded(m, ae)); /* Clean up. */ archive_entry_free(ae); archive_match_free(m); }
static int append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) { struct archive_entry *in_entry; int e; while (ARCHIVE_OK == (e = archive_read_next_header(ina, &in_entry))) { if (archive_match_excluded(bsdtar->matching, in_entry)) continue; if (bsdtar->option_interactive && !yes("copy '%s'", archive_entry_pathname(in_entry))) continue; if (bsdtar->verbose > 1) { safe_fprintf(stderr, "a "); list_item_verbose(bsdtar, stderr, in_entry); } else if (bsdtar->verbose > 0) safe_fprintf(stderr, "a %s", archive_entry_pathname(in_entry)); if (need_report()) report_write(bsdtar, a, in_entry, 0); e = archive_write_header(a, in_entry); if (e != ARCHIVE_OK) { if (!bsdtar->verbose) lafe_warnc(0, "%s: %s", archive_entry_pathname(in_entry), archive_error_string(a)); else fprintf(stderr, ": %s", archive_error_string(a)); } if (e == ARCHIVE_FATAL) exit(1); if (e >= ARCHIVE_WARN) { if (archive_entry_size(in_entry) == 0) archive_read_data_skip(ina); else if (copy_file_data_block(bsdtar, a, ina, in_entry)) exit(1); } if (bsdtar->verbose) fprintf(stderr, "\n"); } return (e == ARCHIVE_EOF ? ARCHIVE_OK : e); }
bool libarchive_tar_extract(const std::string &filename, const std::string &target, const std::vector<std::string> &patterns) { if (target.empty()) { LOGE("%s: Invalid target path for extraction", target.c_str()); return false; } autoclose::archive matcher(archive_match_new(), archive_match_free); if (!matcher) { LOGE("%s: Out of memory when creating matcher", __FUNCTION__); return false; } autoclose::archive in(archive_read_new(), archive_read_free); if (!in) { LOGE("%s: Out of memory when creating archive reader", __FUNCTION__); return false; } autoclose::archive out(archive_write_disk_new(), archive_write_free); if (!out) { LOGE("%s: Out of memory when creating disk writer", __FUNCTION__); return false; } // Set up matcher parameters for (const std::string &pattern : patterns) { if (archive_match_include_pattern( matcher.get(), pattern.c_str()) != ARCHIVE_OK) { LOGE("Invalid pattern: %s", pattern.c_str()); return false; } } // Set up archive reader parameters //archive_read_support_format_gnutar(in.get()); archive_read_support_format_tar(in.get()); //archive_read_support_filter_bzip2(in.get()); //archive_read_support_filter_gzip(in.get()); //archive_read_support_filter_xz(in.get()); // Set up disk writer parameters archive_write_disk_set_standard_lookup(out.get()); archive_write_disk_set_options(out.get(), LIBARCHIVE_DISK_WRITER_FLAGS); if (archive_read_open_filename( in.get(), filename.c_str(), 10240) != ARCHIVE_OK) { LOGE("%s: Failed to open file: %s", filename.c_str(), archive_error_string(in.get())); return false; } archive_entry *entry; int ret; std::string target_path; while (true) { ret = archive_read_next_header(in.get(), &entry); if (ret == ARCHIVE_EOF) { break; } else if (ret == ARCHIVE_RETRY) { LOGW("%s: Retrying header read", filename.c_str()); continue; } else if (ret != ARCHIVE_OK) { LOGE("%s: Failed to read header: %s", filename.c_str(), archive_error_string(in.get())); return false; } const char *path = archive_entry_pathname(entry); if (!path || !*path) { LOGE("%s: Header has null or empty filename", filename.c_str()); return false; } LOGV("%s", path); // Build path target_path = target; if (target_path.back() != '/' && *path != '/') { target_path += '/'; } target_path += path; archive_entry_set_pathname(entry, target_path.c_str()); // Check pattern matches if (archive_match_excluded(matcher.get(), entry)) { continue; } // Extract file ret = archive_read_extract2(in.get(), entry, out.get()); if (ret != ARCHIVE_OK) { LOGE("%s: %s", archive_entry_pathname(entry), archive_error_string(in.get())); return false; } } if (archive_read_close(in.get()) != ARCHIVE_OK) { LOGE("%s: %s", filename.c_str(), archive_error_string(in.get())); return false; } // Check that all patterns were matched const char *pattern; while ((ret = archive_match_path_unmatched_inclusions_next( matcher.get(), &pattern)) == ARCHIVE_OK) { LOGE("%s: Pattern not matched: %s", filename.c_str(), pattern); } if (ret != ARCHIVE_EOF) { LOGE("%s: %s", filename.c_str(), archive_error_string(matcher.get())); return false; } return archive_match_path_unmatched_inclusions(matcher.get()) == 0; }
/* * Handle 'x' and 't' modes. */ static void read_archive(struct bsdtar *bsdtar, char mode, struct archive *writer) { struct progress_data progress_data; FILE *out; struct archive *a; struct archive_entry *entry; const char *reader_options; int r; while (*bsdtar->argv) { if (archive_match_include_pattern(bsdtar->matching, *bsdtar->argv) != ARCHIVE_OK) lafe_errc(1, 0, "Error inclusion pattern: %s", archive_error_string(bsdtar->matching)); bsdtar->argv++; } if (bsdtar->names_from_file != NULL) if (archive_match_include_pattern_from_file( bsdtar->matching, bsdtar->names_from_file, bsdtar->option_null) != ARCHIVE_OK) lafe_errc(1, 0, "Error inclusion pattern: %s", archive_error_string(bsdtar->matching)); a = archive_read_new(); if (cset_read_support_filter_program(bsdtar->cset, a) == 0) archive_read_support_filter_all(a); archive_read_support_format_all(a); reader_options = getenv(ENV_READER_OPTIONS); if (reader_options != NULL) { char *p; /* Set default read options. */ p = malloc(sizeof(IGNORE_WRONG_MODULE_NAME) + strlen(reader_options) + 1); if (p == NULL) lafe_errc(1, errno, "Out of memory"); /* Prepend magic code to ignore options for * a format or modules which are not added to * the archive read object. */ strncpy(p, IGNORE_WRONG_MODULE_NAME, sizeof(IGNORE_WRONG_MODULE_NAME) -1); strcpy(p + sizeof(IGNORE_WRONG_MODULE_NAME) -1, reader_options); r = archive_read_set_options(a, p); free(p); if (r == ARCHIVE_FATAL) lafe_errc(1, 0, "%s", archive_error_string(a)); else archive_clear_error(a); } if (ARCHIVE_OK != archive_read_set_options(a, bsdtar->option_options)) lafe_errc(1, 0, "%s", archive_error_string(a)); if (archive_read_open_filename(a, bsdtar->filename, bsdtar->bytes_per_block)) lafe_errc(1, 0, "Error opening archive: %s", archive_error_string(a)); do_chdir(bsdtar); if (mode == 'x') { /* Set an extract callback so that we can handle SIGINFO. */ progress_data.bsdtar = bsdtar; progress_data.archive = a; archive_read_extract_set_progress_callback(a, progress_func, &progress_data); } if (mode == 'x' && bsdtar->option_chroot) { #if HAVE_CHROOT if (chroot(".") != 0) lafe_errc(1, errno, "Can't chroot to \".\""); #else lafe_errc(1, 0, "chroot isn't supported on this platform"); #endif } for (;;) { /* Support --fast-read option */ if (bsdtar->option_fast_read && archive_match_path_unmatched_inclusions(bsdtar->matching) == 0) break; r = archive_read_next_header(a, &entry); progress_data.entry = entry; if (r == ARCHIVE_EOF) break; if (r < ARCHIVE_OK) lafe_warnc(0, "%s", archive_error_string(a)); if (r <= ARCHIVE_WARN) bsdtar->return_value = 1; if (r == ARCHIVE_RETRY) { /* Retryable error: try again */ lafe_warnc(0, "Retrying..."); continue; } if (r == ARCHIVE_FATAL) break; if (bsdtar->uid >= 0) { archive_entry_set_uid(entry, bsdtar->uid); archive_entry_set_uname(entry, NULL); } if (bsdtar->gid >= 0) { archive_entry_set_gid(entry, bsdtar->gid); archive_entry_set_gname(entry, NULL); } if (bsdtar->uname) archive_entry_set_uname(entry, bsdtar->uname); if (bsdtar->gname) archive_entry_set_gname(entry, bsdtar->gname); /* * Note that pattern exclusions are checked before * pathname rewrites are handled. This gives more * control over exclusions, since rewrites always lose * information. (For example, consider a rewrite * s/foo[0-9]/foo/. If we check exclusions after the * rewrite, there would be no way to exclude foo1/bar * while allowing foo2/bar.) */ if (archive_match_excluded(bsdtar->matching, entry)) continue; /* Excluded by a pattern test. */ if (mode == 't') { /* Perversely, gtar uses -O to mean "send to stderr" * when used with -t. */ out = bsdtar->option_stdout ? stderr : stdout; /* * TODO: Provide some reasonable way to * preview rewrites. gtar always displays * the unedited path in -t output, which means * you cannot easily preview rewrites. */ if (bsdtar->verbose < 2) safe_fprintf(out, "%s", archive_entry_pathname(entry)); else list_item_verbose(bsdtar, out, entry); fflush(out); r = archive_read_data_skip(a); if (r == ARCHIVE_WARN) { fprintf(out, "\n"); lafe_warnc(0, "%s", archive_error_string(a)); } if (r == ARCHIVE_RETRY) { fprintf(out, "\n"); lafe_warnc(0, "%s", archive_error_string(a)); } if (r == ARCHIVE_FATAL) { fprintf(out, "\n"); lafe_warnc(0, "%s", archive_error_string(a)); bsdtar->return_value = 1; break; } fprintf(out, "\n"); } else { /* Note: some rewrite failures prevent extraction. */ if (edit_pathname(bsdtar, entry)) continue; /* Excluded by a rewrite failure. */ if (bsdtar->option_interactive && !yes("extract '%s'", archive_entry_pathname(entry))) continue; /* * Format here is from SUSv2, including the * deferred '\n'. */ if (bsdtar->verbose) { safe_fprintf(stderr, "x %s", archive_entry_pathname(entry)); fflush(stderr); } /* TODO siginfo_printinfo(bsdtar, 0); */ if (bsdtar->option_stdout) r = archive_read_data_into_fd(a, 1); else r = archive_read_extract2(a, entry, writer); if (r != ARCHIVE_OK) { if (!bsdtar->verbose) safe_fprintf(stderr, "%s", archive_entry_pathname(entry)); safe_fprintf(stderr, ": %s", archive_error_string(a)); if (!bsdtar->verbose) fprintf(stderr, "\n"); bsdtar->return_value = 1; } if (bsdtar->verbose) fprintf(stderr, "\n"); if (r == ARCHIVE_FATAL) break; } } r = archive_read_close(a); if (r != ARCHIVE_OK) lafe_warnc(0, "%s", archive_error_string(a)); if (r <= ARCHIVE_WARN) bsdtar->return_value = 1; if (bsdtar->verbose > 2) fprintf(stdout, "Archive Format: %s, Compression: %s\n", archive_format_name(a), archive_filter_name(a, 0)); archive_read_free(a); }