/* * Sparse test with directory traversals. */ static void verify_sparse_file(struct archive *a, const char *path, const struct sparse *sparse, int blocks, int expected_data_blocks) { struct archive_entry *ae; const void *buff; size_t bytes_read; int64_t offset; int64_t total; int data_blocks, hole; create_sparse_file(path, sparse); assert((ae = archive_entry_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, path)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); /* Verify the number of holes only, not its offset nor its * length because those alignments are deeply dependence on * its filesystem. */ assertEqualInt(blocks, archive_entry_sparse_count(ae)); total = 0; data_blocks = 0; hole = 0; while (ARCHIVE_OK == archive_read_data_block(a, &buff, &bytes_read, &offset)) { if (offset > total || offset == 0) { if (offset > total) hole = 1; data_blocks++; } total = offset + bytes_read; } assertEqualInt(expected_data_blocks, data_blocks); if (blocks > 1) assert(hole); /* There must be a hole if > 1 blocks were encoded */ assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); archive_entry_free(ae); }
/* * Sparse test with directory traversals. */ static void verify_sparse_file(struct archive *a, const char *path, const struct sparse *sparse, int expected_holes) { struct archive_entry *ae; const void *buff; size_t bytes_read; int64_t offset, expected_offset, last_offset; int holes_seen = 0; create_sparse_file(path, sparse); assert((ae = archive_entry_new()) != NULL); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, path)); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header2(a, ae)); expected_offset = 0; last_offset = 0; while (ARCHIVE_OK == archive_read_data_block(a, &buff, &bytes_read, &offset)) { const char *start = buff; #if DEBUG fprintf(stderr, "%s: bytes_read=%d offset=%d\n", path, (int)bytes_read, (int)offset); #endif if (offset > last_offset) { ++holes_seen; } /* Blocks entirely before the data we just read. */ while (expected_offset + (int64_t)sparse->size < offset) { #if DEBUG fprintf(stderr, " skipping expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); #endif /* Must be holes. */ assert(sparse->type == HOLE); expected_offset += sparse->size; ++sparse; } /* Block that overlaps beginning of data */ if (expected_offset < offset && expected_offset + (int64_t)sparse->size <= offset + (int64_t)bytes_read) { const char *end = (const char *)buff + (expected_offset - offset) + (size_t)sparse->size; #if DEBUG fprintf(stderr, " overlapping hole expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); #endif /* Must be a hole, overlap must be filled with '\0' */ if (assert(sparse->type == HOLE)) { assertMemoryFilledWith(start, end - start, '\0'); } start = end; expected_offset += sparse->size; ++sparse; } /* Blocks completely contained in data we just read. */ while (expected_offset + (int64_t)sparse->size <= offset + (int64_t)bytes_read) { const char *end = (const char *)buff + (expected_offset - offset) + (size_t)sparse->size; if (sparse->type == HOLE) { #if DEBUG fprintf(stderr, " contained hole expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); #endif /* verify data corresponding to hole is '\0' */ if (end > (const char *)buff + bytes_read) { end = (const char *)buff + bytes_read; } assertMemoryFilledWith(start, end - start, '\0'); start = end; expected_offset += sparse->size; ++sparse; } else if (sparse->type == DATA) { #if DEBUG fprintf(stderr, " contained data expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); #endif /* verify data corresponding to hole is ' ' */ if (assert(expected_offset + sparse->size <= offset + bytes_read)) { assert(start == (const char *)buff + (size_t)(expected_offset - offset)); assertMemoryFilledWith(start, end - start, ' '); } start = end; expected_offset += sparse->size; ++sparse; } else { break; } } /* Block that overlaps end of data */ if (expected_offset < offset + (int64_t)bytes_read) { const char *end = (const char *)buff + bytes_read; #if DEBUG fprintf(stderr, " trailing overlap expected_offset=%d, size=%d\n", (int)expected_offset, (int)sparse->size); #endif /* Must be a hole, overlap must be filled with '\0' */ if (assert(sparse->type == HOLE)) { assertMemoryFilledWith(start, end - start, '\0'); } } last_offset = offset + bytes_read; } /* Count a hole at EOF? */ if (last_offset < archive_entry_size(ae)) { ++holes_seen; } /* Verify blocks after last read */ while (sparse->type == HOLE) { expected_offset += sparse->size; ++sparse; } assert(sparse->type == END); assertEqualInt(expected_offset, archive_entry_size(ae)); assertEqualInt(holes_seen, expected_holes); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); archive_entry_free(ae); }
/* * Add the file or dir hierarchy named by 'path' to the archive */ static void write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) { struct archive *disk = bsdtar->diskreader; struct archive_entry *entry = NULL, *spare_entry = NULL; int r; r = archive_read_disk_open(disk, path); if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(disk), "%s", archive_error_string(disk)); bsdtar->return_value = 1; return; } bsdtar->first_fs = -1; for (;;) { archive_entry_free(entry); entry = archive_entry_new(); r = archive_read_next_header2(disk, entry); if (r == ARCHIVE_EOF) break; else if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(disk), "%s", archive_error_string(disk)); if (r == ARCHIVE_FATAL) { bsdtar->return_value = 1; return; } else if (r < ARCHIVE_WARN) continue; } if (bsdtar->uid >= 0) { archive_entry_set_uid(entry, bsdtar->uid); if (!bsdtar->uname) archive_entry_set_uname(entry, archive_read_disk_uname(bsdtar->diskreader, bsdtar->uid)); } if (bsdtar->gid >= 0) { archive_entry_set_gid(entry, bsdtar->gid); if (!bsdtar->gname) archive_entry_set_gname(entry, archive_read_disk_gname(bsdtar->diskreader, bsdtar->gid)); } if (bsdtar->uname) archive_entry_set_uname(entry, bsdtar->uname); if (bsdtar->gname) archive_entry_set_gname(entry, bsdtar->gname); /* * Rewrite the pathname to be archived. If rewrite * fails, skip the entry. */ if (edit_pathname(bsdtar, entry)) continue; /* Display entry as we process it. * This format is required by SUSv2. */ if (bsdtar->verbose) safe_fprintf(stderr, "a %s", archive_entry_pathname(entry)); /* Non-regular files get archived with zero size. */ if (archive_entry_filetype(entry) != AE_IFREG) archive_entry_set_size(entry, 0); archive_entry_linkify(bsdtar->resolver, &entry, &spare_entry); while (entry != NULL) { write_file(bsdtar, a, entry); archive_entry_free(entry); entry = spare_entry; spare_entry = NULL; } if (bsdtar->verbose) fprintf(stderr, "\n"); } archive_entry_free(entry); archive_read_close(disk); }
/* * Write user-specified files/dirs to opened archive. */ static void write_archive(struct archive *a, struct bsdtar *bsdtar) { const char *arg; struct archive_entry *entry, *sparse_entry; /* Choose a suitable copy buffer size */ bsdtar->buff_size = 64 * 1024; while (bsdtar->buff_size < (size_t)bsdtar->bytes_per_block) bsdtar->buff_size *= 2; /* Try to compensate for space we'll lose to alignment. */ bsdtar->buff_size += 16 * 1024; /* Allocate a buffer for file data. */ if ((bsdtar->buff = malloc(bsdtar->buff_size)) == NULL) lafe_errc(1, 0, "cannot allocate memory"); if ((bsdtar->resolver = archive_entry_linkresolver_new()) == NULL) lafe_errc(1, 0, "cannot create link resolver"); archive_entry_linkresolver_set_strategy(bsdtar->resolver, archive_format(a)); /* Create a read_disk object. */ if ((bsdtar->diskreader = archive_read_disk_new()) == NULL) lafe_errc(1, 0, "Cannot create read_disk object"); /* Tell the read_disk how handle symlink. */ switch (bsdtar->symlink_mode) { case 'H': archive_read_disk_set_symlink_hybrid(bsdtar->diskreader); break; case 'L': archive_read_disk_set_symlink_logical(bsdtar->diskreader); break; default: archive_read_disk_set_symlink_physical(bsdtar->diskreader); break; } /* Register entry filters. */ archive_read_disk_set_matching(bsdtar->diskreader, bsdtar->matching, excluded_callback, bsdtar); archive_read_disk_set_metadata_filter_callback( bsdtar->diskreader, metadata_filter, bsdtar); /* Set the behavior of archive_read_disk. */ archive_read_disk_set_behavior(bsdtar->diskreader, bsdtar->readdisk_flags); archive_read_disk_set_standard_lookup(bsdtar->diskreader); if (bsdtar->names_from_file != NULL) archive_names_from_file(bsdtar, a); while (*bsdtar->argv) { arg = *bsdtar->argv; if (arg[0] == '-' && arg[1] == 'C') { arg += 2; if (*arg == '\0') { bsdtar->argv++; arg = *bsdtar->argv; if (arg == NULL) { lafe_warnc(0, "%s", "Missing argument for -C"); bsdtar->return_value = 1; goto cleanup; } if (*arg == '\0') { lafe_warnc(0, "Meaningless argument for -C: ''"); bsdtar->return_value = 1; goto cleanup; } } set_chdir(bsdtar, arg); } else { if (*arg != '/' && (arg[0] != '@' || arg[1] != '/')) do_chdir(bsdtar); /* Handle a deferred -C */ if (*arg == '@') { if (append_archive_filename(bsdtar, a, arg + 1) != 0) break; } else write_hierarchy(bsdtar, a, arg); } bsdtar->argv++; } archive_read_disk_set_matching(bsdtar->diskreader, NULL, NULL, NULL); archive_read_disk_set_metadata_filter_callback( bsdtar->diskreader, NULL, NULL); entry = NULL; archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry); while (entry != NULL) { int r; struct archive_entry *entry2; struct archive *disk = bsdtar->diskreader; /* * This tricky code here is to correctly read the cotents * of the entry because the disk reader bsdtar->diskreader * is pointing at does not have any information about the * entry by this time and using archive_read_data_block() * with the disk reader consequently must fail. And we * have to re-open the entry to read the contents. */ /* TODO: Work with -C option as well. */ r = archive_read_disk_open(disk, archive_entry_sourcepath(entry)); if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(disk), "%s", archive_error_string(disk)); bsdtar->return_value = 1; archive_entry_free(entry); continue; } /* * Invoke archive_read_next_header2() to work * archive_read_data_block(), which is called via write_file(), * without failure. */ entry2 = archive_entry_new(); r = archive_read_next_header2(disk, entry2); archive_entry_free(entry2); if (r != ARCHIVE_OK) { lafe_warnc(archive_errno(disk), "%s", archive_error_string(disk)); if (r == ARCHIVE_FATAL) bsdtar->return_value = 1; else archive_read_close(disk); archive_entry_free(entry); continue; } write_file(bsdtar, a, entry); archive_entry_free(entry); archive_read_close(disk); entry = NULL; archive_entry_linkify(bsdtar->resolver, &entry, &sparse_entry); } if (archive_write_close(a)) { lafe_warnc(0, "%s", archive_error_string(a)); bsdtar->return_value = 1; } cleanup: /* Free file data buffer. */ free(bsdtar->buff); archive_entry_linkresolver_free(bsdtar->resolver); bsdtar->resolver = NULL; archive_read_free(bsdtar->diskreader); bsdtar->diskreader = NULL; if (bsdtar->option_totals) { fprintf(stderr, "Total bytes written: %s\n", tar_i64toa(archive_filter_bytes(a, -1))); } archive_write_free(a); }
/*! * \brief Create pax archive with all metadata * * \param filename Target archive path * \param base_dir Base directory for \a paths * \param paths List of paths to add to the archive * * \return Whether the archive creation was successful */ bool libarchive_tar_create(const std::string &filename, const std::string &base_dir, const std::vector<std::string> &paths) { if (base_dir.empty() && paths.empty()) { LOGE("%s: No base directory or paths specified", filename.c_str()); return false; } autoclose::archive in(archive_read_disk_new(), archive_read_free); if (!in) { LOGE("%s: Out of memory when creating disk reader", __FUNCTION__); return false; } autoclose::archive out(archive_write_new(), archive_write_free); if (!out) { LOGE("%s: Out of memory when creating archive writer", __FUNCTION__); return false; } autoclose::archive_entry_linkresolver resolver(archive_entry_linkresolver_new(), archive_entry_linkresolver_free); if (!resolver) { LOGE("%s: Out of memory when creating link resolver", __FUNCTION__); return false; } // Set up disk reader parameters archive_read_disk_set_symlink_physical(in.get()); archive_read_disk_set_metadata_filter_callback( in.get(), metadata_filter, nullptr); archive_read_disk_set_behavior(in.get(), LIBARCHIVE_DISK_READER_FLAGS); // We don't want to look up usernames and group names on Android //archive_read_disk_set_standard_lookup(in.get()); // Set up archive writer parameters // NOTE: We are creating POSIX pax archives instead of GNU tar archives // because libarchive's GNU tar writer is very limited. In particular, // it does not support storing sparse file information, xattrs, or // ACLs. Since this information is stored as extended attributes in // the pax archive, the GNU tar tool will not be able to extract any // of this additional metadata. In other words, extracting and // repacking a backup on a Linux machine with GNU tar will render the // backup useless. //archive_write_set_format_gnutar(out.get()); archive_write_set_format_pax_restricted(out.get()); //archive_write_set_compression_bzip2(out.get()); //archive_write_set_compression_gzip(out.get()); //archive_write_set_compression_xz(out.get()); archive_write_set_bytes_per_block(out.get(), 10240); // Set up link resolver parameters archive_entry_linkresolver_set_strategy(resolver.get(), archive_format(out.get())); // Open output file if (archive_write_open_filename(out.get(), filename.c_str()) != ARCHIVE_OK) { LOGE("%s: Failed to open file: %s", filename.c_str(), archive_error_string(out.get())); return false; } archive_entry *entry = nullptr; archive_entry *sparse_entry = nullptr; int ret; std::string full_path; // Add hierarchies for (const std::string &path : paths) { if (path.empty()) { LOGE("%s: Cannot add empty path to the archive", filename.c_str()); return false; } // If the path is absolute, don't append it to the base directory if (path[0] == '/') { full_path = path; } else { full_path = base_dir; if (!full_path.empty() && full_path.back() != '/' && path[0] != '/') { full_path += '/'; } full_path += path; } ret = archive_read_disk_open(in.get(), full_path.c_str()); if (ret != ARCHIVE_OK) { LOGE("%s: %s", full_path.c_str(), archive_error_string(in.get())); return false; } while (true) { archive_entry_free(entry); entry = archive_entry_new(); ret = archive_read_next_header2(in.get(), entry); if (ret == ARCHIVE_EOF) { break; } else if (ret != ARCHIVE_OK) { LOGE("%s: Failed to read next header: %s", full_path.c_str(), archive_error_string(in.get())); archive_entry_free(entry); return false; } if (archive_entry_filetype(entry) != AE_IFREG) { archive_entry_set_size(entry, 0); } // If our current directory tree path is not an absolute path, set // the archive path to the relative path starting at base_dir const char *curpath = archive_entry_pathname(entry); if (curpath && path[0] != '/' && !base_dir.empty()) { std::string relpath; if (!util::relative_path(curpath, base_dir, &relpath)) { LOGE("Failed to compute relative path of %s starting at %s: %s", curpath, base_dir.c_str(), strerror(errno)); archive_entry_free(entry); return false; } if (relpath.empty()) { // If the relative path is empty, then the current path is // the root of the directory tree. We don't need that, so // skip it. continue; } archive_entry_set_pathname(entry, relpath.c_str()); } switch (archive_entry_filetype(entry)) { case AE_IFSOCK: LOGW("%s: Skipping socket", archive_entry_pathname(entry)); continue; default: LOGV("%s", archive_entry_pathname(entry)); break; } archive_entry_linkify(resolver.get(), &entry, &sparse_entry); if (entry) { if (!write_file(in.get(), out.get(), entry)) { archive_entry_free(entry); return false; } archive_entry_free(entry); entry = nullptr; } if (sparse_entry) { if (!write_file(in.get(), out.get(), sparse_entry)) { archive_entry_free(sparse_entry); return false; } archive_entry_free(sparse_entry); sparse_entry = nullptr; } } archive_entry_free(entry); entry = nullptr; archive_read_close(in.get()); } archive_read_disk_set_metadata_filter_callback(in.get(), nullptr, nullptr); entry = nullptr; archive_entry_linkify(resolver.get(), &entry, &sparse_entry); while (entry) { // This tricky code here is to correctly read the contents of the entry // because the disk reader 'in' is pointing at does not have any // information about the entry by this time and using // archive_read_data_block() with the disk reader consequently must // fail. And we hae to re-open the entry to read the contents. ret = archive_read_disk_open(in.get(), archive_entry_sourcepath(entry)); if (ret != ARCHIVE_OK) { LOGE("%s: %s", archive_entry_sourcepath(entry), archive_error_string(in.get())); return false; } // Invoke archive_read_next_header2() to work archive_read_data_block(), // which is called via write_file() without failure. archive_entry *entry2 = archive_entry_new(); ret = archive_read_next_header2(in.get(), entry2); archive_entry_free(entry2); if (ret != ARCHIVE_OK) { LOGE("%s: %s", archive_entry_sourcepath(entry), archive_error_string(in.get())); archive_entry_free(entry); return false; } if (!write_file(in.get(), out.get(), entry)) { archive_entry_free(entry); return false; } archive_entry_free(entry); archive_read_close(in.get()); entry = nullptr; archive_entry_linkify(resolver.get(), &entry, &sparse_entry); } if (archive_write_close(out.get()) != ARCHIVE_OK) { LOGE("%s: %s", filename.c_str(), archive_error_string(out.get())); return false; } return true; }
compare_acls(acl_t acl, struct archive_test_acl_t *myacls, int n) #endif { int *marker; int matched; int i; #if HAVE_SUN_ACL int e; aclent_t *acl_entry; #else int entry_id = ACL_FIRST_ENTRY; acl_entry_t acl_entry; #endif /* Count ACL entries in myacls array and allocate an indirect array. */ marker = malloc(sizeof(marker[0]) * n); if (marker == NULL) return; for (i = 0; i < n; i++) marker[i] = i; /* * Iterate over acls in system acl object, try to match each * one with an item in the myacls array. */ #if HAVE_SUN_ACL for(e = 0; e < acl->acl_cnt; e++) { acl_entry = &((aclent_t *)acl->acl_aclp)[e]; #else while (1 == acl_get_entry(acl, entry_id, &acl_entry)) { /* After the first time... */ entry_id = ACL_NEXT_ENTRY; #endif /* Search for a matching entry (tag and qualifier) */ for (i = 0, matched = 0; i < n && !matched; i++) { if (acl_match(acl_entry, &myacls[marker[i]])) { /* We found a match; remove it. */ marker[i] = marker[n - 1]; n--; matched = 1; } } /* TODO: Print out more details in this case. */ failure("ACL entry on file that shouldn't be there"); assert(matched == 1); } /* Dump entries in the myacls array that weren't in the system acl. */ for (i = 0; i < n; ++i) { failure(" ACL entry missing from file: " "type=%#010x,permset=%#010x,tag=%d,qual=%d,name=``%s''\n", myacls[marker[i]].type, myacls[marker[i]].permset, myacls[marker[i]].tag, myacls[marker[i]].qual, myacls[marker[i]].name); assert(0); /* Record this as a failure. */ } free(marker); } #endif /* * Verify ACL restore-to-disk. This test is Platform-specific. */ DEFINE_TEST(test_acl_platform_posix1e_restore) { #if !HAVE_SUN_ACL && !HAVE_POSIX_ACL skipping("POSIX.1e ACLs are not supported on this platform"); #else /* HAVE_SUN_ACL || HAVE_POSIX_ACL */ struct stat st; struct archive *a; struct archive_entry *ae; int n, fd; char *func; #if HAVE_SUN_ACL acl_t *acl, *acl2; #else acl_t acl; #endif /* * First, do a quick manual set/read of ACL data to * verify that the local filesystem does support ACLs. * If it doesn't, we'll simply skip the remaining tests. */ #if HAVE_SUN_ACL n = acl_fromtext("user::rwx,user:1:rw-,group::rwx,group:15:r-x,other:rwx,mask:rwx", &acl); failure("acl_fromtext(): errno = %d (%s)", errno, strerror(errno)); assertEqualInt(0, n); #else acl = acl_from_text("u::rwx,u:1:rw,g::rwx,g:15:rx,o::rwx,m::rwx"); failure("acl_from_text(): errno = %d (%s)", errno, strerror(errno)); assert((void *)acl != NULL); #endif /* Create a test file and try ACL on it. */ fd = open("pretest", O_WRONLY | O_CREAT | O_EXCL, 0777); failure("Could not create test file?!"); if (!assert(fd >= 0)) { acl_free(acl); return; } #if HAVE_SUN_ACL n = facl_get(fd, 0, &acl2); if (n != 0) { close(fd); acl_free(acl); } if (errno == ENOSYS) { skipping("POSIX.1e ACLs are not supported on this filesystem"); return; } failure("facl_get(): errno = %d (%s)", errno, strerror(errno)); assertEqualInt(0, n); if (acl2->acl_type != ACLENT_T) { acl_free(acl2); skipping("POSIX.1e ACLs are not supported on this filesystem"); return; } acl_free(acl2); func = "facl_set()"; n = facl_set(fd, acl); #else func = "acl_set_fd()"; n = acl_set_fd(fd, acl); #endif acl_free(acl); if (n != 0) { #if HAVE_SUN_ACL if (errno == ENOSYS) #else if (errno == EOPNOTSUPP || errno == EINVAL) #endif { close(fd); skipping("POSIX.1e ACLs are not supported on this filesystem"); return; } } failure("%s: errno = %d (%s)", func, errno, strerror(errno)); assertEqualInt(0, n); #if HAVE_SUN_ACL #endif close(fd); /* Create a write-to-disk object. */ assert(NULL != (a = archive_write_disk_new())); archive_write_disk_set_options(a, ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_ACL); /* Populate an archive entry with some metadata, including ACL info */ ae = archive_entry_new(); assert(ae != NULL); archive_entry_set_pathname(ae, "test0"); archive_entry_set_mtime(ae, 123456, 7890); archive_entry_set_size(ae, 0); assertEntrySetAcls(ae, acls2, sizeof(acls2)/sizeof(acls2[0])); assertEqualIntA(a, ARCHIVE_OK, archive_write_header(a, ae)); archive_entry_free(ae); /* Close the archive. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); /* Verify the data on disk. */ assertEqualInt(0, stat("test0", &st)); assertEqualInt(st.st_mtime, 123456); #if HAVE_SUN_ACL n = acl_get("test0", 0, &acl); failure("acl_get(): errno = %d (%s)", errno, strerror(errno)); assertEqualInt(0, n); #else acl = acl_get_file("test0", ACL_TYPE_ACCESS); failure("acl_get_file(): errno = %d (%s)", errno, strerror(errno)); assert(acl != (acl_t)NULL); #endif compare_acls(acl, acls2, sizeof(acls2)/sizeof(acls2[0])); acl_free(acl); #endif /* HAVE_SUN_ACL || HAVE_POSIX_ACL */ } /* * Verify ACL read-from-disk. This test is Platform-specific. */ DEFINE_TEST(test_acl_platform_posix1e_read) { #if !HAVE_SUN_ACL && !HAVE_POSIX_ACL skipping("POSIX.1e ACLs are not supported on this platform"); #else struct archive *a; struct archive_entry *ae; int n, fd, flags, dflags; char *func, *acl_text; const char *acl1_text, *acl2_text, *acl3_text; #if HAVE_SUN_ACL acl_t *acl, *acl1, *acl2, *acl3; #else acl_t acl1, acl2, acl3; #endif /* * Manually construct a directory and two files with * different ACLs. This also serves to verify that ACLs * are supported on the local filesystem. */ /* Create a test file f1 with acl1 */ #if HAVE_SUN_ACL acl1_text = "user::rwx," "group::rwx," "other:rwx," "user:1:rw-," "group:15:r-x," "mask:rwx"; n = acl_fromtext(acl1_text, &acl1); failure("acl_fromtext(): errno = %d (%s)", errno, strerror(errno)); assertEqualInt(0, n); #else acl1_text = "user::rwx\n" "group::rwx\n" "other::rwx\n" "user:1:rw-\n" "group:15:r-x\n" "mask::rwx"; acl1 = acl_from_text(acl1_text); failure("acl_from_text(): errno = %d (%s)", errno, strerror(errno)); assert((void *)acl1 != NULL); #endif fd = open("f1", O_WRONLY | O_CREAT | O_EXCL, 0777); failure("Could not create test file?!"); if (!assert(fd >= 0)) { acl_free(acl1); return; } #if HAVE_SUN_ACL /* Check if Solaris filesystem supports POSIX.1e ACLs */ n = facl_get(fd, 0, &acl); if (n != 0) close(fd); if (n != 0 && errno == ENOSYS) { acl_free(acl1); skipping("POSIX.1e ACLs are not supported on this filesystem"); return; } failure("facl_get(): errno = %d (%s)", errno, strerror(errno)); assertEqualInt(0, n); if (acl->acl_type != ACLENT_T) { acl_free(acl); acl_free(acl1); close(fd); skipping("POSIX.1e ACLs are not supported on this filesystem"); return; } func = "facl_set()"; n = facl_set(fd, acl1); #else func = "acl_set_fd()"; n = acl_set_fd(fd, acl1); #endif acl_free(acl1); if (n != 0) { #if HAVE_SUN_ACL if (errno == ENOSYS) #else if (errno == EOPNOTSUPP || errno == EINVAL) #endif { close(fd); skipping("POSIX.1e ACLs are not supported on this filesystem"); return; } } failure("%s: errno = %d (%s)", func, errno, strerror(errno)); assertEqualInt(0, n); close(fd); assertMakeDir("d", 0700); /* * Create file d/f1 with acl2 * * This differs from acl1 in the u:1: and g:15: permissions. * * This file deliberately has the same name but a different ACL. * Github Issue #777 explains how libarchive's directory traversal * did not always correctly enter directories before attempting * to read ACLs, resulting in reading the ACL from a like-named * file in the wrong directory. */ #if HAVE_SUN_ACL acl2_text = "user::rwx," "group::rwx," "other:---," "user:1:r--," "group:15:r--," "mask:rwx"; n = acl_fromtext(acl2_text, &acl2); failure("acl_fromtext(): errno = %d (%s)", errno, strerror(errno)); assertEqualInt(0, n); #else acl2_text = "user::rwx\n" "group::rwx\n" "other::---\n" "user:1:r--\n" "group:15:r--\n" "mask::rwx"; acl2 = acl_from_text(acl2_text); failure("acl_from_text(): errno = %d (%s)", errno, strerror(errno)); assert((void *)acl2 != NULL); #endif fd = open("d/f1", O_WRONLY | O_CREAT | O_EXCL, 0777); failure("Could not create test file?!"); if (!assert(fd >= 0)) { acl_free(acl2); return; } #if HAVE_SUN_ACL func = "facl_set()"; n = facl_set(fd, acl2); #else func = "acl_set_fd()"; n = acl_set_fd(fd, acl2); #endif acl_free(acl2); if (n != 0) close(fd); failure("%s: errno = %d (%s)", func, errno, strerror(errno)); assertEqualInt(0, n); close(fd); /* Create nested directory d2 with default ACLs */ assertMakeDir("d/d2", 0755); #if HAVE_SUN_ACL acl3_text = "user::rwx," "group::r-x," "other:r-x," "user:2:r--," "group:16:-w-," "mask:rwx," "default:user::rwx," "default:user:1:r--," "default:group::r-x," "default:group:15:r--," "default:mask:rwx," "default:other:r-x"; n = acl_fromtext(acl3_text, &acl3); failure("acl_fromtext(): errno = %d (%s)", errno, strerror(errno)); assertEqualInt(0, n); #else acl3_text = "user::rwx\n" "user:1:r--\n" "group::r-x\n" "group:15:r--\n" "mask::rwx\n" "other::r-x"; acl3 = acl_from_text(acl3_text); failure("acl_from_text(): errno = %d (%s)", errno, strerror(errno)); assert((void *)acl3 != NULL); #endif #if HAVE_SUN_ACL func = "acl_set()"; n = acl_set("d/d2", acl3); #else func = "acl_set_file()"; n = acl_set_file("d/d2", ACL_TYPE_DEFAULT, acl3); #endif acl_free(acl3); failure("%s: errno = %d (%s)", func, errno, strerror(errno)); assertEqualInt(0, n); /* Create a read-from-disk object. */ assert(NULL != (a = archive_read_disk_new())); assertEqualIntA(a, ARCHIVE_OK, archive_read_disk_open(a, ".")); assert(NULL != (ae = archive_entry_new())); #if HAVE_SUN_ACL flags = ARCHIVE_ENTRY_ACL_TYPE_POSIX1E | ARCHIVE_ENTRY_ACL_STYLE_SEPARATOR_COMMA | ARCHIVE_ENTRY_ACL_STYLE_SOLARIS; dflags = flags; #else flags = ARCHIVE_ENTRY_ACL_TYPE_ACCESS; dflags = ARCHIVE_ENTRY_ACL_TYPE_DEFAULT; #endif /* Walk the dir until we see both of the files */ while (ARCHIVE_OK == archive_read_next_header2(a, ae)) { archive_read_disk_descend(a); if (strcmp(archive_entry_pathname(ae), "./f1") == 0) { acl_text = archive_entry_acl_to_text(ae, NULL, flags); assertEqualString(acl_text, acl1_text); free(acl_text); } else if (strcmp(archive_entry_pathname(ae), "./d/f1") == 0) { acl_text = archive_entry_acl_to_text(ae, NULL, flags); assertEqualString(acl_text, acl2_text); free(acl_text); } else if (strcmp(archive_entry_pathname(ae), "./d/d2") == 0) { acl_text = archive_entry_acl_to_text(ae, NULL, dflags); assertEqualString(acl_text, acl3_text); free(acl_text); } } archive_entry_free(ae); assertEqualInt(ARCHIVE_OK, archive_free(a)); #endif }
static void create(const char *filename, int compress, const char **argv) { struct archive *a; struct archive *disk; struct archive_entry *entry; ssize_t len; int fd; a = archive_write_new(); switch (compress) { #ifndef NO_BZIP2_CREATE case 'j': case 'y': archive_write_add_filter_bzip2(a); break; #endif #ifndef NO_COMPRESS_CREATE case 'Z': archive_write_add_filter_compress(a); break; #endif #ifndef NO_GZIP_CREATE case 'z': archive_write_add_filter_gzip(a); break; #endif default: archive_write_add_filter_none(a); break; } archive_write_set_format_ustar(a); if (filename != NULL && strcmp(filename, "-") == 0) filename = NULL; archive_write_open_filename(a, filename); disk = archive_read_disk_new(); #ifndef NO_LOOKUP archive_read_disk_set_standard_lookup(disk); #endif while (*argv != NULL) { struct archive *disk = archive_read_disk_new(); int r; r = archive_read_disk_open(disk, *argv); if (r != ARCHIVE_OK) { errmsg(archive_error_string(disk)); errmsg("\n"); exit(1); } for (;;) { int needcr = 0; entry = archive_entry_new(); r = archive_read_next_header2(disk, entry); if (r == ARCHIVE_EOF) break; if (r != ARCHIVE_OK) { errmsg(archive_error_string(disk)); errmsg("\n"); exit(1); } archive_read_disk_descend(disk); if (verbose) { msg("a "); msg(archive_entry_pathname(entry)); needcr = 1; } r = archive_write_header(a, entry); if (r < ARCHIVE_OK) { errmsg(": "); errmsg(archive_error_string(a)); needcr = 1; } if (r == ARCHIVE_FATAL) exit(1); if (r > ARCHIVE_FAILED) { #if 0 /* Ideally, we would be able to use * the same code to copy a body from * an archive_read_disk to an * archive_write that we use for * copying data from an archive_read * to an archive_write_disk. * Unfortunately, this doesn't quite * work yet. */ copy_data(disk, a); #else /* For now, we use a simpler loop to copy data * into the target archive. */ fd = open(archive_entry_sourcepath(entry), O_RDONLY); len = read(fd, buff, sizeof(buff)); while (len > 0) { archive_write_data(a, buff, len); len = read(fd, buff, sizeof(buff)); } close(fd); #endif } archive_entry_free(entry); if (needcr) msg("\n"); } archive_read_close(disk); archive_read_free(disk); argv++; } archive_write_close(a); archive_write_free(a); }
int archive_create2( char *arch_file, int compress, int format, char **files ) { int fd, len, ret; struct archive *arch_w = NULL, *arch_r; struct archive_entry *arch_entry = NULL; char buf[8192]; if( !arch_file || !files ) return -1; arch_w = archive_write_new(); switch( compress ) { case 'j': //bz2 //archive_write_add_filter_bzip2( arch_w ); archive_write_set_compression_bzip2( arch_w ); break; case 'J': //xz //archive_write_add_filter_xz( arch_w ); archive_write_set_compression_xz( arch_w ); break; case 'z': //gzip //archive_write_add_filter_gzip( arch_w ); archive_write_set_compression_gzip( arch_w ); break; } switch( format ) { case 'c': //cpio archive_write_set_format_cpio( arch_w ); break; case 't': //tar archive_write_set_format_ustar( arch_w ); break; default: //tar archive_write_set_format_ustar( arch_w ); break; } ret = archive_write_open_filename( arch_w, arch_file ); if( ret != ARCHIVE_OK ) { archive_write_finish( arch_w ); return -1; } while( *files ) { arch_r = archive_read_disk_new(); ret = archive_read_disk_open( arch_r, *files ); if( ret != ARCHIVE_OK ) { #ifdef DEBUG printf( "%s\n", archive_error_string( arch_r ) ); #endif archive_write_finish( arch_w ); return -1; } for( ; ; ) { arch_entry = archive_entry_new(); ret = archive_read_next_header2( arch_r, arch_entry ); if( ret == ARCHIVE_EOF ) break; if( ret != ARCHIVE_OK ) { #ifdef DEBUG printf( "%s\n", archive_error_string( arch_r ) ); #endif archive_entry_free( arch_entry ); archive_write_finish( arch_r ); archive_write_finish( arch_w ); return -1; } archive_read_disk_descend( arch_r ); ret = archive_write_header( arch_w, arch_entry ); if( ret < ARCHIVE_OK ) { #ifdef DEBUG printf( "%s\n", archive_error_string( arch_w ) ); #endif archive_entry_free( arch_entry ); archive_write_finish( arch_r ); archive_write_finish( arch_w ); return -1; } if( ( fd = open( archive_entry_sourcepath( arch_entry ), O_RDONLY ) ) != -1 ) { len = read( fd, buf, 8192 ); while( len > 0 ) { archive_write_data( arch_w, buf, len ); len = read( fd, buf, 8192 ); } close( fd ); } archive_entry_free( arch_entry ); } /* libarchive 3 archive_read_close( arch_r ); archive_read_free( arch_r ); */ archive_read_finish( arch_r ); } if( arch_w ) archive_write_finish( arch_w ); return 0; }
int archive_create( char *arch_file, int compress, int format, char *src_dir, char **exclude ) { int fd, len, ret, skip; char *pwd, **files; DIR *dir; struct dirent *dir_entry; struct archive *arch_w = NULL, *arch_r = NULL; struct archive_entry *arch_entry = NULL; char buf[8192]; if( !arch_file || !src_dir ) return -1; dir = opendir( src_dir ); if( !dir ) return -1; arch_w = archive_write_new(); switch( compress ) { case 'j': //bz2 //archive_write_add_filter_bzip2( arch_w ); //libarchive 3 archive_write_set_compression_bzip2( arch_w ); break; case 'J': //xz //archive_write_add_filter_xz( arch_w ); archive_write_set_compression_xz( arch_w ); break; case 'z': //gzip //archive_write_add_filter_gzip( arch_w ); archive_write_set_compression_gzip( arch_w ); break; } switch( format ) { case 'c': //cpio archive_write_set_format_cpio( arch_w ); break; case 't': //tar archive_write_set_format_ustar( arch_w ); break; default: //tar archive_write_set_format_ustar( arch_w ); break; } ret = archive_write_open_filename( arch_w, arch_file ); if( ret != ARCHIVE_OK ) { archive_write_finish( arch_w ); return -1; } pwd = getcwd( NULL, 0 ); chdir( src_dir ); while( (dir_entry = readdir( dir )) ) { if( !strcmp( dir_entry->d_name, "." ) || !strcmp( dir_entry->d_name, ".." ) ) { continue; } if( exclude ) { files = exclude; skip = 0; while( *files ) { if( !strcmp( *files, dir_entry->d_name ) ) { skip = 1; break; } files++; } if( skip ) continue; } arch_r = archive_read_disk_new(); ret = archive_read_disk_open( arch_r, dir_entry->d_name ); if( ret != ARCHIVE_OK ) { #ifdef DEBUG printf( "%s\n", archive_error_string( arch_r ) ); #endif archive_write_finish( arch_w ); return -1; } for( ; ; ) { arch_entry = archive_entry_new(); ret = archive_read_next_header2( arch_r, arch_entry ); if( ret == ARCHIVE_EOF ) break; if( ret != ARCHIVE_OK ) { #ifdef DEBUG printf( "%s\n", archive_error_string( arch_r ) ); #endif archive_entry_free( arch_entry ); archive_write_finish( arch_r ); archive_write_finish( arch_w ); return -1; } #ifdef DEBUG printf( "%s\n", archive_entry_pathname( arch_entry ) ); #endif archive_read_disk_descend( arch_r ); ret = archive_write_header( arch_w, arch_entry ); if( ret < ARCHIVE_OK ) { #ifdef DEBUG printf( "%s\n", archive_error_string( arch_w ) ); #endif archive_entry_free( arch_entry ); archive_write_finish( arch_r ); archive_write_finish( arch_w ); return -1; } if( ( fd = open( archive_entry_sourcepath( arch_entry ), O_RDONLY ) ) != -1 ) { len = read( fd, buf, 8192 ); while( len > 0 ) { archive_write_data( arch_w, buf, len ); len = read( fd, buf, 8192 ); } close( fd ); } archive_entry_free( arch_entry ); } /* libarchive 3 archive_read_close( arch_r ); archive_read_free( arch_r ); */ archive_read_finish( arch_r ); } if( arch_w ) { /* libarchive 3 archive_write_close( arch_r ); archive_write_free( arch_r ); */ archive_write_finish( arch_w ); } if( pwd ) { chdir( pwd ); free( pwd ); } return 0; }