/* * All of the sample files have the same contents; they're just * compressed in different ways. */ static void compat_bzip2(const char *name) { const char *n[7] = { "f1", "f2", "f3", "d1/f1", "d1/f2", "d1/f3", NULL }; struct archive_entry *ae; struct archive *a; int i; assert((a = archive_read_new()) != NULL); if (ARCHIVE_OK != archive_read_support_compression_bzip2(a)) { skipping("Unsupported bzip2"); return; } assertEqualIntA(a, ARCHIVE_OK, archive_read_support_format_all(a)); extract_reference_file(name); assertEqualIntA(a, ARCHIVE_OK, archive_read_open_filename(a, name, 2)); /* Read entries, match up names with list above. */ for (i = 0; i < 6; ++i) { failure("Could not read file %d (%s) from %s", i, n[i], name); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString(n[i], archive_entry_pathname(ae)); } /* Verify the end-of-archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify that the format detection worked. */ assertEqualInt(archive_compression(a), ARCHIVE_COMPRESSION_BZIP2); assertEqualString(archive_compression_name(a), "bzip2"); assertEqualInt(archive_format(a), ARCHIVE_FORMAT_TAR_USTAR); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_finish(a)); }
/* * Convenience function to perform all exclusion tests. * * Returns 1 if archive entry is excluded. * Returns 0 if archive entry is not excluded. * Returns <0 if something error happened. */ int archive_match_excluded(struct archive *_a, struct archive_entry *entry) { struct archive_match *a; int r; archive_check_magic(_a, ARCHIVE_MATCH_MAGIC, ARCHIVE_STATE_NEW, "archive_match_excluded_ae"); a = (struct archive_match *)_a; if (entry == NULL) { archive_set_error(&(a->archive), EINVAL, "entry is NULL"); return (ARCHIVE_FAILED); } r = 0; if (a->setflag & PATTERN_IS_SET) { #if defined(_WIN32) && !defined(__CYGWIN__) r = path_excluded(a, 0, archive_entry_pathname_w(entry)); #else r = path_excluded(a, 1, archive_entry_pathname(entry)); #endif if (r != 0) return (r); } if (a->setflag & TIME_IS_SET) { r = time_excluded(a, entry); if (r != 0) return (r); } if (a->setflag & ID_IS_SET) r = owner_excluded(a, entry); return (r); }
/* * Extract an encoded file. * The header of the 7z archive files is not encoded. */ static void test_plain_header(const char *refname) { struct archive_entry *ae; struct archive *a; char buff[128]; extract_reference_file(refname); 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_filename(a, refname, 10240)); /* Verify regular file1. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt((AE_IFREG | 0644), archive_entry_mode(ae)); assertEqualString("file1", archive_entry_pathname(ae)); assertEqualInt(1322058763, archive_entry_mtime(ae)); assertEqualInt(2844, archive_entry_size(ae)); assertEqualInt(sizeof(buff), archive_read_data(a, buff, sizeof(buff))); assertEqualMem(buff, "The libarchive distribution ", 28); assertEqualInt(1, archive_file_count(a)); /* End of archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); /* Verify archive format. */ assertEqualIntA(a, ARCHIVE_COMPRESSION_NONE, archive_compression(a)); assertEqualIntA(a, ARCHIVE_FORMAT_7ZIP, archive_format(a)); /* Close the archive. */ assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); }
Installer::ProceedState RomInstaller::on_checked_device() { // /sbin is not going to be populated with anything useful in a normal boot // image. We can almost guarantee that a recovery image is going to be // installed though, so we'll open the recovery partition with libmbp and // extract its /sbin with libarchive into the chroot's /sbin. std::string block_dev(_recovery_block_dev); mbp::BootImage bi; mbp::CpioFile innerCpio; const unsigned char *ramdisk_data; std::size_t ramdisk_size; bool using_boot = false; // Check if the device has a combined boot/recovery partition. If the // FOTAKernel partition is listed, it will be used instead of the combined // ramdisk from the boot image bool combined = mb_device_flags(_device) & FLAG_HAS_COMBINED_BOOT_AND_RECOVERY; if (combined && block_dev.empty()) { block_dev = _boot_block_dev; using_boot = true; } if (block_dev.empty()) { display_msg("Could not determine the recovery block device"); return ProceedState::Fail; } if (!bi.loadFile(block_dev)) { display_msg("Failed to load recovery partition image"); return ProceedState::Fail; } // Load ramdisk bi.ramdiskImageC(&ramdisk_data, &ramdisk_size); if (using_boot) { if (!innerCpio.load(ramdisk_data, ramdisk_size)) { display_msg("Failed to load ramdisk from combined boot image"); return ProceedState::Fail; } if (!innerCpio.contentsC("sbin/ramdisk-recovery.cpio", &ramdisk_data, &ramdisk_size)) { display_msg("Could not find recovery ramdisk in combined boot image"); return ProceedState::Fail; } } autoclose::archive in(archive_read_new(), archive_read_free); autoclose::archive out(archive_write_disk_new(), archive_write_free); if (!in || !out) { LOGE("Out of memory"); return ProceedState::Fail; } archive_entry *entry; // Set up input archive_read_support_filter_gzip(in.get()); archive_read_support_filter_lzop(in.get()); archive_read_support_filter_lz4(in.get()); archive_read_support_filter_lzma(in.get()); archive_read_support_filter_xz(in.get()); archive_read_support_format_cpio(in.get()); int ret = archive_read_open_memory(in.get(), const_cast<unsigned char *>(ramdisk_data), ramdisk_size); if (ret != ARCHIVE_OK) { LOGW("Failed to open recovery ramdisk: %s", archive_error_string(in.get())); return ProceedState::Fail; } // Set up output archive_write_disk_set_options(out.get(), ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_SECURE_NODOTDOT | ARCHIVE_EXTRACT_SECURE_SYMLINKS | ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_UNLINK | ARCHIVE_EXTRACT_XATTR); while ((ret = archive_read_next_header(in.get(), &entry)) == ARCHIVE_OK) { std::string path = archive_entry_pathname(entry); if (path == "default.prop") { path = "default.recovery.prop"; } else if (!util::starts_with(path, "sbin/")) { continue; } LOGE("Copying from recovery: %s", path.c_str()); archive_entry_set_pathname(entry, in_chroot(path).c_str()); if (util::libarchive_copy_header_and_data( in.get(), out.get(), entry) != ARCHIVE_OK) { return ProceedState::Fail; } archive_entry_set_pathname(entry, path.c_str()); } if (ret != ARCHIVE_EOF) { LOGE("Archive extraction ended without reaching EOF: %s", archive_error_string(in.get())); return ProceedState::Fail; } // Create fake /etc/fstab file to please installers that read the file std::string etc_fstab(in_chroot("/etc/fstab")); if (access(etc_fstab.c_str(), R_OK) < 0 && errno == ENOENT) { autoclose::file fp(autoclose::fopen(etc_fstab.c_str(), "w")); if (fp) { auto system_devs = mb_device_system_block_devs(_device); auto cache_devs = mb_device_cache_block_devs(_device); auto data_devs = mb_device_data_block_devs(_device); // Set block device if it's provided and non-empty const char *system_dev = system_devs && system_devs[0] && system_devs[0][0] ? system_devs[0] : "dummy"; const char *cache_dev = cache_devs && cache_devs[0] && cache_devs[0][0] ? cache_devs[0] : "dummy"; const char *data_dev = data_devs && data_devs[0] && data_devs[0][0] ? data_devs[0] : "dummy"; fprintf(fp.get(), "%s /system ext4 rw 0 0\n", system_dev); fprintf(fp.get(), "%s /cache ext4 rw 0 0\n", cache_dev); fprintf(fp.get(), "%s /data ext4 rw 0 0\n", data_dev); } } // Load recovery properties util::file_get_all_properties( in_chroot("/default.recovery.prop"), &_recovery_props); return ProceedState::Continue; }
/** * asb_utils_explode: * @filename: package filename * @dir: directory to decompress into * @glob: (element-type utf8): filename globs, or %NULL * @error: A #GError or %NULL * * Decompresses the package into a given directory. * * Returns: %TRUE for success, %FALSE otherwise * * Since: 0.1.0 **/ gboolean asb_utils_explode (const gchar *filename, const gchar *dir, GPtrArray *glob, GError **error) { const gchar *tmp; gboolean ret = TRUE; gboolean valid; int r; struct archive *arch = NULL; struct archive *arch_preview = NULL; struct archive_entry *entry; g_autoptr(GHashTable) matches = NULL; /* populate a hash with all the files, symlinks and hardlinks that * actually need decompressing */ arch_preview = archive_read_new (); archive_read_support_format_all (arch_preview); archive_read_support_filter_all (arch_preview); r = archive_read_open_filename (arch_preview, filename, 1024 * 32); if (r) { ret = FALSE; g_set_error (error, ASB_PLUGIN_ERROR, ASB_PLUGIN_ERROR_FAILED, "Cannot open: %s", archive_error_string (arch_preview)); goto out; } matches = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); for (;;) { g_autofree gchar *path = NULL; r = archive_read_next_header (arch_preview, &entry); if (r == ARCHIVE_EOF) break; if (r != ARCHIVE_OK) { ret = FALSE; g_set_error (error, ASB_PLUGIN_ERROR, ASB_PLUGIN_ERROR_FAILED, "Cannot read header: %s", archive_error_string (arch_preview)); goto out; } /* get the destination filename */ tmp = archive_entry_pathname (entry); if (tmp == NULL) continue; path = asb_utils_sanitise_path (tmp); if (glob != NULL) { if (asb_glob_value_search (glob, path) == NULL) continue; } g_hash_table_insert (matches, g_strdup (path), GINT_TO_POINTER (1)); /* add hardlink */ tmp = archive_entry_hardlink (entry); if (tmp != NULL) { g_hash_table_insert (matches, asb_utils_sanitise_path (tmp), GINT_TO_POINTER (1)); } /* add symlink */ tmp = archive_entry_symlink (entry); if (tmp != NULL) { if (g_path_is_absolute (tmp)) { g_hash_table_insert (matches, asb_utils_sanitise_path (tmp), GINT_TO_POINTER (1)); } else { g_autofree gchar *parent_dir = g_path_get_dirname (path); g_hash_table_insert (matches, asb_utils_resolve_relative_symlink (parent_dir, tmp), GINT_TO_POINTER (1)); } } } /* decompress anything matching either glob */ arch = archive_read_new (); archive_read_support_format_all (arch); archive_read_support_filter_all (arch); r = archive_read_open_filename (arch, filename, 1024 * 32); if (r) { ret = FALSE; g_set_error (error, ASB_PLUGIN_ERROR, ASB_PLUGIN_ERROR_FAILED, "Cannot open: %s", archive_error_string (arch)); goto out; } for (;;) { g_autofree gchar *path = NULL; r = archive_read_next_header (arch, &entry); if (r == ARCHIVE_EOF) break; if (r != ARCHIVE_OK) { ret = FALSE; g_set_error (error, ASB_PLUGIN_ERROR, ASB_PLUGIN_ERROR_FAILED, "Cannot read header: %s", archive_error_string (arch)); goto out; } /* only extract if valid */ tmp = archive_entry_pathname (entry); path = asb_utils_sanitise_path (tmp); if (g_hash_table_lookup (matches, path) == NULL) continue; valid = asb_utils_explode_file (entry, dir); if (!valid) continue; r = archive_read_extract (arch, entry, 0); if (r != ARCHIVE_OK) { ret = FALSE; g_set_error (error, ASB_PLUGIN_ERROR, ASB_PLUGIN_ERROR_FAILED, "Cannot extract: %s", archive_error_string (arch)); goto out; } } out: if (arch_preview != NULL) { archive_read_close (arch_preview); archive_read_free (arch_preview); } if (arch != NULL) { archive_read_close (arch); archive_read_free (arch); } return ret; }
static int setup_xattrs(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { char *list, *p; const char *path; ssize_t list_size; path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); if (*fd < 0 && a->tree != NULL) { if (a->follow_symlinks || archive_entry_filetype(entry) != AE_IFLNK) *fd = a->open_on_current_dir(a->tree, path, O_RDONLY | O_NONBLOCK); if (*fd < 0) { if (a->tree_enter_working_dir(a->tree) != 0) { archive_set_error(&a->archive, errno, "Couldn't access %s", path); return (ARCHIVE_FAILED); } } } #if HAVE_FLISTXATTR if (*fd >= 0) list_size = flistxattr(*fd, NULL, 0); else if (!a->follow_symlinks) list_size = llistxattr(path, NULL, 0); else list_size = listxattr(path, NULL, 0); #elif HAVE_FLISTEA if (*fd >= 0) list_size = flistea(*fd, NULL, 0); else if (!a->follow_symlinks) list_size = llistea(path, NULL, 0); else list_size = listea(path, NULL, 0); #endif if (list_size == -1) { if (errno == ENOTSUP || errno == ENOSYS) return (ARCHIVE_OK); archive_set_error(&a->archive, errno, "Couldn't list extended attributes"); return (ARCHIVE_WARN); } if (list_size == 0) return (ARCHIVE_OK); if ((list = malloc(list_size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } #if HAVE_FLISTXATTR if (*fd >= 0) list_size = flistxattr(*fd, list, list_size); else if (!a->follow_symlinks) list_size = llistxattr(path, list, list_size); else list_size = listxattr(path, list, list_size); #elif HAVE_FLISTEA if (*fd >= 0) list_size = flistea(*fd, list, list_size); else if (!a->follow_symlinks) list_size = llistea(path, list, list_size); else list_size = listea(path, list, list_size); #endif if (list_size == -1) { archive_set_error(&a->archive, errno, "Couldn't retrieve extended attributes"); free(list); return (ARCHIVE_WARN); } for (p = list; (p - list) < list_size; p += strlen(p) + 1) { if (strncmp(p, "system.", 7) == 0 || strncmp(p, "xfsroot.", 8) == 0) continue; setup_xattr(a, entry, p, *fd); } free(list); return (ARCHIVE_OK); }
static int setup_acls(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { const char *accpath; acl_t acl; #if HAVE_ACL_IS_TRIVIAL_NP int r; #endif accpath = archive_entry_sourcepath(entry); if (accpath == NULL) accpath = archive_entry_pathname(entry); archive_entry_acl_clear(entry); /* Try NFS4 ACL first. */ if (*fd >= 0) acl = acl_get_fd(*fd); #if HAVE_ACL_GET_LINK_NP else if (!a->follow_symlinks) acl = acl_get_link_np(accpath, ACL_TYPE_NFS4); #else else if ((!a->follow_symlinks) && (archive_entry_filetype(entry) == AE_IFLNK)) /* We can't get the ACL of a symlink, so we assume it can't have one. */ acl = NULL; #endif else acl = acl_get_file(accpath, ACL_TYPE_NFS4); #if HAVE_ACL_IS_TRIVIAL_NP /* Ignore "trivial" ACLs that just mirror the file mode. */ acl_is_trivial_np(acl, &r); if (r) { acl_free(acl); acl = NULL; } #endif if (acl != NULL) { translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_NFS4); acl_free(acl); return (ARCHIVE_OK); } /* Retrieve access ACL from file. */ if (*fd >= 0) acl = acl_get_fd(*fd); #if HAVE_ACL_GET_LINK_NP else if (!a->follow_symlinks) acl = acl_get_link_np(accpath, ACL_TYPE_ACCESS); #else else if ((!a->follow_symlinks) && (archive_entry_filetype(entry) == AE_IFLNK)) /* We can't get the ACL of a symlink, so we assume it can't have one. */ acl = NULL; #endif else acl = acl_get_file(accpath, ACL_TYPE_ACCESS); if (acl != NULL) { translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_ACCESS); acl_free(acl); } /* Only directories can have default ACLs. */ if (S_ISDIR(archive_entry_mode(entry))) { acl = acl_get_file(accpath, ACL_TYPE_DEFAULT); if (acl != NULL) { translate_acl(a, entry, acl, ARCHIVE_ENTRY_ACL_TYPE_DEFAULT); acl_free(acl); } } return (ARCHIVE_OK); }
int archive_read_disk_entry_from_file(struct archive *_a, struct archive_entry *entry, int fd, const struct stat *st) { struct archive_read_disk *a = (struct archive_read_disk *)_a; const char *path, *name; struct stat s; int initial_fd = fd; int r, r1; archive_clear_error(_a); path = archive_entry_sourcepath(entry); if (path == NULL) path = archive_entry_pathname(entry); if (a->tree == NULL) { if (st == NULL) { #if HAVE_FSTAT if (fd >= 0) { if (fstat(fd, &s) != 0) { archive_set_error(&a->archive, errno, "Can't fstat"); return (ARCHIVE_FAILED); } } else #endif #if HAVE_LSTAT if (!a->follow_symlinks) { if (lstat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't lstat %s", path); return (ARCHIVE_FAILED); } } else #endif if (stat(path, &s) != 0) { archive_set_error(&a->archive, errno, "Can't stat %s", path); return (ARCHIVE_FAILED); } st = &s; } archive_entry_copy_stat(entry, st); } /* Lookup uname/gname */ name = archive_read_disk_uname(_a, archive_entry_uid(entry)); if (name != NULL) archive_entry_copy_uname(entry, name); name = archive_read_disk_gname(_a, archive_entry_gid(entry)); if (name != NULL) archive_entry_copy_gname(entry, name); #ifdef HAVE_STRUCT_STAT_ST_FLAGS /* On FreeBSD, we get flags for free with the stat. */ /* TODO: Does this belong in copy_stat()? */ if (st->st_flags != 0) archive_entry_set_fflags(entry, st->st_flags, 0); #endif #if defined(EXT2_IOC_GETFLAGS) && defined(HAVE_WORKING_EXT2_IOC_GETFLAGS) /* Linux requires an extra ioctl to pull the flags. Although * this is an extra step, it has a nice side-effect: We get an * open file descriptor which we can use in the subsequent lookups. */ if ((S_ISREG(st->st_mode) || S_ISDIR(st->st_mode))) { if (fd < 0) { if (a->tree != NULL) fd = a->open_on_current_dir(a->tree, path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); else fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC); __archive_ensure_cloexec_flag(fd); } if (fd >= 0) { int stflags; r = ioctl(fd, EXT2_IOC_GETFLAGS, &stflags); if (r == 0 && stflags != 0) archive_entry_set_fflags(entry, stflags, 0); } } #endif #if defined(HAVE_READLINK) || defined(HAVE_READLINKAT) if (S_ISLNK(st->st_mode)) { size_t linkbuffer_len = st->st_size + 1; char *linkbuffer; int lnklen; linkbuffer = malloc(linkbuffer_len); if (linkbuffer == NULL) { archive_set_error(&a->archive, ENOMEM, "Couldn't read link data"); return (ARCHIVE_FAILED); } if (a->tree != NULL) { #ifdef HAVE_READLINKAT lnklen = readlinkat(a->tree_current_dir_fd(a->tree), path, linkbuffer, linkbuffer_len); #else if (a->tree_enter_working_dir(a->tree) != 0) { archive_set_error(&a->archive, errno, "Couldn't read link data"); free(linkbuffer); return (ARCHIVE_FAILED); } lnklen = readlink(path, linkbuffer, linkbuffer_len); #endif /* HAVE_READLINKAT */ } else lnklen = readlink(path, linkbuffer, linkbuffer_len); if (lnklen < 0) { archive_set_error(&a->archive, errno, "Couldn't read link data"); free(linkbuffer); return (ARCHIVE_FAILED); } linkbuffer[lnklen] = 0; archive_entry_set_symlink(entry, linkbuffer); free(linkbuffer); } #endif /* HAVE_READLINK || HAVE_READLINKAT */ r = setup_acls(a, entry, &fd); r1 = setup_xattrs(a, entry, &fd); if (r1 < r) r = r1; if (a->enable_copyfile) { r1 = setup_mac_metadata(a, entry, &fd); if (r1 < r) r = r1; } r1 = setup_sparse(a, entry, &fd); if (r1 < r) r = r1; /* If we opened the file earlier in this function, close it. */ if (initial_fd != fd) close(fd); return (r); }
static void test_filename(const char *prefix, int dlen, int flen) { char buff[8192]; char filename[400]; char dirname[400]; struct archive_entry *ae; struct archive *a; size_t used; char *p; int i; p = filename; if (prefix) { strcpy(filename, prefix); p += strlen(p); } if (dlen > 0) { for (i = 0; i < dlen; i++) *p++ = 'a'; *p++ = '/'; } for (i = 0; i < flen; i++) *p++ = 'b'; *p = '\0'; strcpy(dirname, filename); /* Create a new archive in memory. */ assert((a = archive_write_new()) != NULL); assertA(0 == archive_write_set_format_pax_restricted(a)); assertA(0 == archive_write_set_compression_none(a)); assertA(0 == archive_write_set_bytes_per_block(a,0)); assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used)); /* * Write a file to it. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, filename); archive_entry_set_mode(ae, S_IFREG | 0755); failure("Pathname %d/%d", dlen, flen); assertA(0 == archive_write_header(a, ae)); archive_entry_free(ae); /* * Write a dir to it (without trailing '/'). */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, dirname); archive_entry_set_mode(ae, S_IFDIR | 0755); failure("Dirname %d/%d", dlen, flen); assertA(0 == archive_write_header(a, ae)); archive_entry_free(ae); /* Tar adds a '/' to directory names. */ strcat(dirname, "/"); /* * Write a dir to it (with trailing '/'). */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, dirname); archive_entry_set_mode(ae, S_IFDIR | 0755); failure("Dirname %d/%d", dlen, flen); assertA(0 == archive_write_header(a, ae)); archive_entry_free(ae); /* Close out the archive. */ assertA(0 == archive_write_close(a)); #if ARCHIVE_VERSION_NUMBER < 2000000 archive_write_finish(a); #else assertA(0 == archive_write_finish(a)); #endif /* * Now, read the data back. */ assert((a = archive_read_new()) != NULL); assertA(0 == archive_read_support_format_all(a)); assertA(0 == archive_read_support_compression_all(a)); assertA(0 == archive_read_open_memory(a, buff, used)); /* Read the file and check the filename. */ assertA(0 == archive_read_next_header(a, &ae)); #if ARCHIVE_VERSION_NUMBER < 1009000 skipping("Leading '/' preserved on long filenames"); #else assertEqualString(filename, archive_entry_pathname(ae)); #endif assertEqualInt((S_IFREG | 0755), archive_entry_mode(ae)); /* * Read the two dirs and check the names. * * Both dirs should read back with the same name, since * tar should add a trailing '/' to any dir that doesn't * already have one. We only report the first such failure * here. */ assertA(0 == archive_read_next_header(a, &ae)); #if ARCHIVE_VERSION_NUMBER < 1009000 skipping("Trailing '/' preserved on dirnames"); #else assertEqualString(dirname, archive_entry_pathname(ae)); #endif assert((S_IFDIR | 0755) == archive_entry_mode(ae)); assertA(0 == archive_read_next_header(a, &ae)); #if ARCHIVE_VERSION_NUMBER < 1009000 skipping("Trailing '/' added to dir names"); #else assertEqualString(dirname, archive_entry_pathname(ae)); #endif assert((S_IFDIR | 0755) == archive_entry_mode(ae)); /* Verify the end of the archive. */ assert(1 == archive_read_next_header(a, &ae)); assert(0 == archive_read_close(a)); #if ARCHIVE_VERSION_NUMBER < 2000000 archive_read_finish(a); #else assert(0 == archive_read_finish(a)); #endif }
static void test_unicode_CP932(void) { char buff[30]; const char reffile[] = "test_read_format_rar_unicode.rar"; const char test_txt[] = "kanji"; struct archive_entry *ae; struct archive *a; if (NULL == setlocale(LC_ALL, "Japanese_Japan") && NULL == setlocale(LC_ALL, "ja_JP.SJIS")) { skipping("CP932 locale not available on this system."); return; } extract_reference_file(reffile); assert((a = archive_read_new()) != NULL); assertA(0 == archive_read_support_filter_all(a)); assertA(0 == archive_read_support_format_all(a)); /* Specify the charset of symbolic-link file name. */ if (ARCHIVE_OK != archive_read_set_options(a, "rar:hdrcharset=UTF-8")) { skipping("This system cannot convert character-set" " from UTF-8 to CP932."); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); return; } assertA(0 == archive_read_open_file(a, reffile, 10240)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("\x95\x5c\x82\xbe\x82\xe6/\x90\x56\x82\xb5\x82\xa2" "\x83\x74\x83\x48\x83\x8b\x83\x5f/\x90\x56\x8b\x4b\x83\x65\x83\x4c" "\x83\x58\x83\x67 \x83\x68\x83\x4c\x83\x85\x83\x81\x83\x93\x83\x67.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); /* Second header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("\x95\x5c\x82\xbe\x82\xe6/\x8a\xbf\x8e\x9a" "\x92\xb7\x82\xa2\x83\x74\x83\x40\x83\x43\x83\x8b\x96\xbc\x6c" "\x6f\x6e\x67\x2d\x66\x69\x6c\x65\x6e\x61\x6d\x65\x2d\x69\x6e" "\x2d\x8a\xbf\x8e\x9a.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(5, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertA(5 == archive_read_data(a, buff, 5)); assertEqualMem(buff, test_txt, 5); /* Third header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("\x95\x5c\x82\xbe\x82\xe6/" "\x90\x56\x82\xb5\x82\xa2\x83\x74\x83\x48\x83\x8b\x83\x5f", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); /* Fourth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("\x95\x5c\x82\xbe\x82\xe6", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); /* Fifth header, which has a symbolic-link name in multi-byte characters. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("\x95\x5c\x82\xbe\x82\xe6/" "\x83\x74\x83\x40\x83\x43\x83\x8B", archive_entry_pathname(ae)); assertEqualString("\x8a\xbf\x8e\x9a" "\x92\xb7\x82\xa2\x83\x74\x83\x40\x83\x43\x83\x8b\x96\xbc\x6c" "\x6f\x6e\x67\x2d\x66\x69\x6c\x65\x6e\x61\x6d\x65\x2d\x69\x6e" "\x2d\x8a\xbf\x8e\x9a.txt", archive_entry_symlink(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(41453, archive_entry_mode(ae)); assertEqualIntA(a, 0, archive_read_data(a, buff, sizeof(buff))); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(5, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); }
static void test_unicode_UTF8(void) { char buff[30]; const char reffile[] = "test_read_format_rar_unicode.rar"; const char test_txt[] = "kanji"; struct archive_entry *ae; struct archive *a; if (NULL == setlocale(LC_ALL, "en_US.UTF-8")) { skipping("en_US.UTF-8 locale not available on this system."); return; } extract_reference_file(reffile); assert((a = archive_read_new()) != NULL); assertA(0 == archive_read_support_filter_all(a)); assertA(0 == archive_read_support_format_all(a)); assertA(0 == archive_read_open_file(a, reffile, 10240)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); #if defined(__APPLE__) #define f1name "\xE8\xA1\xA8\xE3\x81\x9F\xE3\x82\x99\xE3\x82\x88/"\ "\xE6\x96\xB0\xE3\x81\x97\xE3\x81\x84\xE3\x83\x95\xE3\x82\xA9"\ "\xE3\x83\xAB\xE3\x82\xBF\xE3\x82\x99/\xE6\x96\xB0\xE8\xA6\x8F"\ "\xE3\x83\x86\xE3\x82\xAD\xE3\x82\xB9\xE3\x83\x88 "\ "\xE3\x83\x88\xE3\x82\x99\xE3\x82\xAD\xE3\x83\xA5\xE3\x83\xA1"\ "\xE3\x83\xB3\xE3\x83\x88.txt" /* NFD */ #else #define f1name "\xE8\xA1\xA8\xE3\x81\xA0\xE3\x82\x88/"\ "\xE6\x96\xB0\xE3\x81\x97\xE3\x81\x84\xE3\x83\x95\xE3\x82\xA9"\ "\xE3\x83\xAB\xE3\x83\x80/\xE6\x96\xB0\xE8\xA6\x8F"\ "\xE3\x83\x86\xE3\x82\xAD\xE3\x82\xB9\xE3\x83\x88 "\ "\xE3\x83\x89\xE3\x82\xAD\xE3\x83\xA5\xE3\x83\xA1"\ "\xE3\x83\xB3\xE3\x83\x88.txt" /* NFC */ #endif assertEqualUTF8String(f1name, archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); /* Second header. */ assertA(0 == archive_read_next_header(a, &ae)); #if defined(__APPLE__) #define f2name "\xE8\xA1\xA8\xE3\x81\x9F\xE3\x82\x99\xE3\x82\x88/"\ "\xE6\xBC\xA2\xE5\xAD\x97\xE9\x95\xB7\xE3\x81\x84\xE3\x83\x95"\ "\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB\xE5\x90\x8Dlong-filename-in-"\ "\xE6\xBC\xA2\xE5\xAD\x97.txt" /* NFD */ #else #define f2name "\xE8\xA1\xA8\xE3\x81\xA0\xE3\x82\x88/"\ "\xE6\xBC\xA2\xE5\xAD\x97\xE9\x95\xB7\xE3\x81\x84\xE3\x83\x95"\ "\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB\xE5\x90\x8Dlong-filename-in-"\ "\xE6\xBC\xA2\xE5\xAD\x97.txt" /* NFC */ #endif assertEqualUTF8String(f2name, archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(5, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertEqualIntA(a, 5, archive_read_data(a, buff, 5)); assertEqualMem(buff, test_txt, 5); /* Third header. */ assertA(0 == archive_read_next_header(a, &ae)); #if defined(__APPLE__) #define f3name "\xE8\xA1\xA8\xE3\x81\x9F\xE3\x82\x99\xE3\x82\x88/"\ "\xE6\x96\xB0\xE3\x81\x97\xE3\x81\x84\xE3\x83\x95\xE3\x82"\ "\xA9\xE3\x83\xAB\xE3\x82\xBF\xE3\x82\x99" /* NFD */ #else #define f3name "\xE8\xA1\xA8\xE3\x81\xA0\xE3\x82\x88/"\ "\xE6\x96\xB0\xE3\x81\x97\xE3\x81\x84\xE3\x83\x95\xE3\x82"\ "\xA9\xE3\x83\xAB\xE3\x83\x80" /* NFC */ #endif assertEqualUTF8String(f3name, archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); /* Fourth header. */ assertA(0 == archive_read_next_header(a, &ae)); #if defined(__APPLE__) #define f4name "\xE8\xA1\xA8\xE3\x81\x9F\xE3\x82\x99\xE3\x82\x88" /* NFD */ #else #define f4name "\xE8\xA1\xA8\xE3\x81\xA0\xE3\x82\x88" /* NFC */ #endif assertEqualUTF8String(f4name, archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); /* Fifth header, which has a symbolic-link name in multi-byte characters. */ assertA(0 == archive_read_next_header(a, &ae)); #if defined(__APPLE__) #define f5name "\xE8\xA1\xA8\xE3\x81\x9F\xE3\x82\x99\xE3\x82\x88/"\ "\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB" /* NFD */ #else #define f5name "\xE8\xA1\xA8\xE3\x81\xA0\xE3\x82\x88/"\ "\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB" /* NFC */ #endif assertEqualUTF8String(f5name, archive_entry_pathname(ae)); assertEqualUTF8String( "\xE6\xBC\xA2\xE5\xAD\x97\xE9\x95\xB7\xE3\x81\x84\xE3\x83\x95" "\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB\xE5\x90\x8Dlong-filename-in-" "\xE6\xBC\xA2\xE5\xAD\x97.txt", archive_entry_symlink(ae)); assertA((int)archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(41453, archive_entry_mode(ae)); assertEqualIntA(a, 0, archive_read_data(a, buff, sizeof(buff))); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(5, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); }
/* * Write a file using archive_write_data call, read the file * back and verify the contents. The data written includes large * blocks of nulls, so it should exercise the sparsification logic * if ARCHIVE_EXTRACT_SPARSE is enabled. */ static void verify_write_data(struct archive *a, int sparse) { static const char data[]="abcdefghijklmnopqrstuvwxyz"; struct stat st; struct archive_entry *ae; size_t buff_size = 64 * 1024; char *buff, *p; const char *msg = sparse ? "sparse" : "non-sparse"; FILE *f; buff = malloc(buff_size); assert(buff != NULL); ae = archive_entry_new(); assert(ae != NULL); archive_entry_set_size(ae, 8 * buff_size); archive_entry_set_pathname(ae, "test_write_data"); archive_entry_set_mode(ae, AE_IFREG | 0755); assertEqualIntA(a, 0, archive_write_header(a, ae)); /* Use archive_write_data() to write three relatively sparse blocks. */ /* First has non-null data at beginning. */ memset(buff, 0, buff_size); memcpy(buff, data, sizeof(data)); failure("%s", msg); assertEqualInt(buff_size, archive_write_data(a, buff, buff_size)); /* Second has non-null data in the middle. */ memset(buff, 0, buff_size); memcpy(buff + buff_size / 2 - 3, data, sizeof(data)); failure("%s", msg); assertEqualInt(buff_size, archive_write_data(a, buff, buff_size)); /* Third has non-null data at the end. */ memset(buff, 0, buff_size); memcpy(buff + buff_size - sizeof(data), data, sizeof(data)); failure("%s", msg); assertEqualInt(buff_size, archive_write_data(a, buff, buff_size)); failure("%s", msg); assertEqualIntA(a, 0, archive_write_finish_entry(a)); /* Test the entry on disk. */ assert(0 == stat(archive_entry_pathname(ae), &st)); assertEqualInt(st.st_size, 8 * buff_size); f = fopen(archive_entry_pathname(ae), "rb"); if (!assert(f != NULL)) return; /* Check first block. */ assertEqualInt(buff_size, fread(buff, 1, buff_size, f)); failure("%s", msg); assertEqualMem(buff, data, sizeof(data)); for (p = buff + sizeof(data); p < buff + buff_size; ++p) { failure("offset: %d, %s", (int)(p - buff), msg); if (!assertEqualInt(0, *p)) break; } /* Check second block. */ assertEqualInt(buff_size, fread(buff, 1, buff_size, f)); for (p = buff; p < buff + buff_size; ++p) { failure("offset: %d, %s", (int)(p - buff), msg); if (p == buff + buff_size / 2 - 3) { assertEqualMem(p, data, sizeof(data)); p += sizeof(data); } else if (!assertEqualInt(0, *p)) break; } /* Check third block. */ assertEqualInt(buff_size, fread(buff, 1, buff_size, f)); for (p = buff; p < buff + buff_size - sizeof(data); ++p) { failure("offset: %d, %s", (int)(p - buff), msg); if (!assertEqualInt(0, *p)) break; } failure("%s", msg); assertEqualMem(buff + buff_size - sizeof(data), data, sizeof(data)); /* XXX more XXX */ assertEqualInt(0, fclose(f)); archive_entry_free(ae); free(buff); }
int pkg_create_repo(char *path, bool force, void (progress)(struct pkg *pkg, void *data), void *data) { FTS *fts = NULL; struct thd_data thd_data; int num_workers; size_t len; pthread_t *tids = NULL; struct pkg_dep *dep = NULL; struct pkg_category *category = NULL; struct pkg_license *license = NULL; struct pkg_option *option = NULL; struct pkg_shlib *shlib = NULL; sqlite3 *sqlite = NULL; int64_t package_id; char *errmsg = NULL; int retcode = EPKG_OK; int ret; char *repopath[2]; char repodb[MAXPATHLEN + 1]; char repopack[MAXPATHLEN + 1]; struct archive *a = NULL; struct archive_entry *ae = NULL; if (!is_dir(path)) { pkg_emit_error("%s is not a directory", path); return (EPKG_FATAL); } repopath[0] = path; repopath[1] = NULL; len = sizeof(num_workers); if (sysctlbyname("hw.ncpu", &num_workers, &len, NULL, 0) == -1) num_workers = 6; if ((fts = fts_open(repopath, FTS_PHYSICAL|FTS_NOCHDIR, NULL)) == NULL) { pkg_emit_errno("fts_open", path); retcode = EPKG_FATAL; goto cleanup; } snprintf(repodb, sizeof(repodb), "%s/repo.sqlite", path); snprintf(repopack, sizeof(repopack), "%s/repo.txz", path); if (access(repopack, F_OK) == 0) { a = archive_read_new(); archive_read_support_compression_all(a); archive_read_support_format_tar(a); ret = archive_read_open_filename(a, repopack, 4096); if (ret != ARCHIVE_OK) { /* if we can't unpack it it won't be useful for us */ unlink(repopack); } else { while (archive_read_next_header(a, &ae) == ARCHIVE_OK) { if (!strcmp(archive_entry_pathname(ae), "repo.sqlite")) { archive_entry_set_pathname(ae, repodb); archive_read_extract(a, ae, EXTRACT_ARCHIVE_FLAGS); break; } } } if (a != NULL) archive_read_finish(a); } if ((retcode = initialize_repo(repodb, force, &sqlite)) != EPKG_OK) goto cleanup; if ((retcode = initialize_prepared_statements(sqlite)) != EPKG_OK) goto cleanup; thd_data.root_path = path; thd_data.max_results = num_workers; thd_data.num_results = 0; thd_data.stop = false; thd_data.fts = fts; pthread_mutex_init(&thd_data.fts_m, NULL); STAILQ_INIT(&thd_data.results); thd_data.thd_finished = 0; pthread_mutex_init(&thd_data.results_m, NULL); pthread_cond_init(&thd_data.has_result, NULL); pthread_cond_init(&thd_data.has_room, NULL); /* Launch workers */ tids = calloc(num_workers, sizeof(pthread_t)); for (int i = 0; i < num_workers; i++) { pthread_create(&tids[i], NULL, (void *)&read_pkg_file, &thd_data); } for (;;) { struct pkg_result *r; const char *name, *version, *origin, *comment, *desc; const char *arch, *maintainer, *www, *prefix; int64_t flatsize; lic_t licenselogic; pthread_mutex_lock(&thd_data.results_m); while ((r = STAILQ_FIRST(&thd_data.results)) == NULL) { if (thd_data.thd_finished == num_workers) { break; } pthread_cond_wait(&thd_data.has_result, &thd_data.results_m); } if (r != NULL) { STAILQ_REMOVE_HEAD(&thd_data.results, next); thd_data.num_results--; pthread_cond_signal(&thd_data.has_room); } pthread_mutex_unlock(&thd_data.results_m); if (r == NULL) { break; } if (r->retcode != EPKG_OK) { continue; } /* do not add if package if already in repodb (possibly at a different pkg_path) */ if (run_prepared_statement(EXISTS, r->cksum) != SQLITE_ROW) { ERROR_SQLITE(sqlite); goto cleanup; } if (sqlite3_column_int(STMT(EXISTS), 0) > 0) { continue; } if (progress != NULL) progress(r->pkg, data); pkg_get(r->pkg, PKG_ORIGIN, &origin, PKG_NAME, &name, PKG_VERSION, &version, PKG_COMMENT, &comment, PKG_DESC, &desc, PKG_ARCH, &arch, PKG_MAINTAINER, &maintainer, PKG_WWW, &www, PKG_PREFIX, &prefix, PKG_FLATSIZE, &flatsize, PKG_LICENSE_LOGIC, &licenselogic); try_again: if ((ret = run_prepared_statement(PKG, origin, name, version, comment, desc, arch, maintainer, www, prefix, r->size, flatsize, (int64_t)licenselogic, r->cksum, r->path)) != SQLITE_DONE) { if (ret == SQLITE_CONSTRAINT) { switch(maybe_delete_conflicting(origin, version, r->path)) { case EPKG_FATAL: /* sqlite error */ ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; break; case EPKG_END: /* repo already has newer */ continue; break; default: /* conflict cleared, try again */ goto try_again; break; } } else { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } } package_id = sqlite3_last_insert_rowid(sqlite); dep = NULL; while (pkg_deps(r->pkg, &dep) == EPKG_OK) { if (run_prepared_statement(DEPS, pkg_dep_origin(dep), pkg_dep_name(dep), pkg_dep_version(dep), package_id) != SQLITE_DONE) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } } category = NULL; while (pkg_categories(r->pkg, &category) == EPKG_OK) { const char *cat_name = pkg_category_name(category); ret = run_prepared_statement(CAT1, cat_name); if (ret == SQLITE_DONE) ret = run_prepared_statement(CAT2, package_id, cat_name); if (ret != SQLITE_DONE) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } } license = NULL; while (pkg_licenses(r->pkg, &license) == EPKG_OK) { const char *lic_name = pkg_license_name(license); ret = run_prepared_statement(LIC1, lic_name); if (ret == SQLITE_DONE) ret = run_prepared_statement(LIC2, package_id, lic_name); if (ret != SQLITE_DONE) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } } option = NULL; while (pkg_options(r->pkg, &option) == EPKG_OK) { if (run_prepared_statement(OPTS, pkg_option_opt(option), pkg_option_value(option), package_id) != SQLITE_DONE) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } } shlib = NULL; while (pkg_shlibs(r->pkg, &shlib) == EPKG_OK) { const char *shlib_name = pkg_shlib_name(shlib); ret = run_prepared_statement(SHLIB1, shlib_name); if (ret == SQLITE_DONE) ret = run_prepared_statement(SHLIB2, package_id, shlib_name); if (ret != SQLITE_DONE) { ERROR_SQLITE(sqlite); retcode = EPKG_FATAL; goto cleanup; } } pkg_free(r->pkg); free(r); } if (pkgdb_transaction_commit(sqlite, NULL) != SQLITE_OK) retcode = EPKG_FATAL; cleanup: if (tids != NULL) { // Cancel running threads if (retcode != EPKG_OK) { pthread_mutex_lock(&thd_data.fts_m); thd_data.stop = true; pthread_mutex_unlock(&thd_data.fts_m); } // Join on threads to release thread IDs for (int i = 0; i < num_workers; i++) { pthread_join(tids[i], NULL); } free(tids); } if (fts != NULL) fts_close(fts); finalize_prepared_statements(); if (sqlite != NULL) sqlite3_close(sqlite); if (errmsg != NULL) sqlite3_free(errmsg); sqlite3_shutdown(); return (retcode); }
/*- * The logic here for -C <dir> attempts to avoid * chdir() as long as possible. For example: * "-C /foo -C /bar file" needs chdir("/bar") but not chdir("/foo") * "-C /foo -C bar file" needs chdir("/foo/bar") * "-C /foo -C bar /file1" does not need chdir() * "-C /foo -C bar /file1 file2" needs chdir("/foo/bar") before file2 * * The only correct way to handle this is to record a "pending" chdir * request and combine multiple requests intelligently until we * need to process a non-absolute file. set_chdir() adds the new dir * to the pending list; do_chdir() actually executes any pending chdir. * * This way, programs that build tar command lines don't have to worry * about -C with non-existent directories; such requests will only * fail if the directory must be accessed. * */ void set_chdir(struct bsdtar *bsdtar, const char *newdir) { #if defined(_WIN32) && !defined(__CYGWIN__) if (newdir[0] == '/' || newdir[0] == '\\' || /* Detect this type, for example, "C:\" or "C:/" */ (((newdir[0] >= 'a' && newdir[0] <= 'z') || (newdir[0] >= 'A' && newdir[0] <= 'Z')) && newdir[1] == ':' && (newdir[2] == '/' || newdir[2] == '\\'))) { #else if (newdir[0] == '/') { #endif /* The -C /foo -C /bar case; dump first one. */ free(bsdtar->pending_chdir); bsdtar->pending_chdir = NULL; } if (bsdtar->pending_chdir == NULL) /* Easy case: no previously-saved dir. */ bsdtar->pending_chdir = strdup(newdir); else { /* The -C /foo -C bar case; concatenate */ char *old_pending = bsdtar->pending_chdir; size_t old_len = strlen(old_pending); bsdtar->pending_chdir = malloc(old_len + strlen(newdir) + 2); if (old_pending[old_len - 1] == '/') old_pending[old_len - 1] = '\0'; if (bsdtar->pending_chdir != NULL) sprintf(bsdtar->pending_chdir, "%s/%s", old_pending, newdir); free(old_pending); } if (bsdtar->pending_chdir == NULL) lafe_errc(1, errno, "No memory"); } void do_chdir(struct bsdtar *bsdtar) { if (bsdtar->pending_chdir == NULL) return; if (chdir(bsdtar->pending_chdir) != 0) { lafe_errc(1, 0, "could not chdir to '%s'\n", bsdtar->pending_chdir); } free(bsdtar->pending_chdir); bsdtar->pending_chdir = NULL; } static const char * strip_components(const char *p, int elements) { /* Skip as many elements as necessary. */ while (elements > 0) { switch (*p++) { case '/': #if defined(_WIN32) && !defined(__CYGWIN__) case '\\': /* Support \ path sep on Windows ONLY. */ #endif elements--; break; case '\0': /* Path is too short, skip it. */ return (NULL); } } /* Skip any / characters. This handles short paths that have * additional / termination. This also handles the case where * the logic above stops in the middle of a duplicate // * sequence (which would otherwise get converted to an * absolute path). */ for (;;) { switch (*p) { case '/': #if defined(_WIN32) && !defined(__CYGWIN__) case '\\': /* Support \ path sep on Windows ONLY. */ #endif ++p; break; case '\0': return (NULL); default: return (p); } } } static void warn_strip_leading_char(struct bsdtar *bsdtar, const char *c) { if (!bsdtar->warned_lead_slash) { lafe_warnc(0, "Removing leading '%c' from member names", c[0]); bsdtar->warned_lead_slash = 1; } } static void warn_strip_drive_letter(struct bsdtar *bsdtar) { if (!bsdtar->warned_lead_slash) { lafe_warnc(0, "Removing leading drive letter from " "member names"); bsdtar->warned_lead_slash = 1; } } /* * Convert absolute path to non-absolute path by skipping leading * absolute path prefixes. */ static const char* strip_absolute_path(struct bsdtar *bsdtar, const char *p) { const char *rp; /* Remove leading "//./" or "//?/" or "//?/UNC/" * (absolute path prefixes used by Windows API) */ if ((p[0] == '/' || p[0] == '\\') && (p[1] == '/' || p[1] == '\\') && (p[2] == '.' || p[2] == '?') && (p[3] == '/' || p[3] == '\\')) { if (p[2] == '?' && (p[4] == 'U' || p[4] == 'u') && (p[5] == 'N' || p[5] == 'n') && (p[6] == 'C' || p[6] == 'c') && (p[7] == '/' || p[7] == '\\')) p += 8; else p += 4; warn_strip_drive_letter(bsdtar); } /* Remove multiple leading slashes and Windows drive letters. */ do { rp = p; if (((p[0] >= 'a' && p[0] <= 'z') || (p[0] >= 'A' && p[0] <= 'Z')) && p[1] == ':') { p += 2; warn_strip_drive_letter(bsdtar); } /* Remove leading "/../", "/./", "//", etc. */ while (p[0] == '/' || p[0] == '\\') { if (p[1] == '.' && p[2] == '.' && (p[3] == '/' || p[3] == '\\')) { p += 3; /* Remove "/..", leave "/" for next pass. */ } else if (p[1] == '.' && (p[2] == '/' || p[2] == '\\')) { p += 2; /* Remove "/.", leave "/" for next pass. */ } else p += 1; /* Remove "/". */ warn_strip_leading_char(bsdtar, rp); } } while (rp != p); return (p); } /* * Handle --strip-components and any future path-rewriting options. * Returns non-zero if the pathname should not be extracted. * * Note: The rewrites are applied uniformly to pathnames and hardlink * names but not to symlink bodies. This is deliberate: Symlink * bodies are not necessarily filenames. Even when they are, they * need to be interpreted relative to the directory containing them, * so simple rewrites like this are rarely appropriate. * * TODO: Support pax-style regex path rewrites. */ int edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) { const char *name = archive_entry_pathname(entry); const char *original_name = name; const char *hardlinkname = archive_entry_hardlink(entry); const char *original_hardlinkname = hardlinkname; #if defined(HAVE_REGEX_H) || defined(HAVE_PCREPOSIX_H) char *subst_name; int r; /* Apply user-specified substitution to pathname. */ r = apply_substitution(bsdtar, name, &subst_name, 0, 0); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_pathname(entry, subst_name); if (*subst_name == '\0') { free(subst_name); return -1; } else free(subst_name); name = archive_entry_pathname(entry); original_name = name; } /* Apply user-specified substitution to hardlink target. */ if (hardlinkname != NULL) { r = apply_substitution(bsdtar, hardlinkname, &subst_name, 0, 1); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_hardlink(entry, subst_name); free(subst_name); } hardlinkname = archive_entry_hardlink(entry); original_hardlinkname = hardlinkname; } /* Apply user-specified substitution to symlink body. */ if (archive_entry_symlink(entry) != NULL) { r = apply_substitution(bsdtar, archive_entry_symlink(entry), &subst_name, 1, 0); if (r == -1) { lafe_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { archive_entry_copy_symlink(entry, subst_name); free(subst_name); } } #endif /* Strip leading dir names as per --strip-components option. */ if (bsdtar->strip_components > 0) { name = strip_components(name, bsdtar->strip_components); if (name == NULL) return (1); if (hardlinkname != NULL) { hardlinkname = strip_components(hardlinkname, bsdtar->strip_components); if (hardlinkname == NULL) return (1); } } if (!bsdtar->option_absolute_paths) { /* By default, don't write or restore absolute pathnames. */ name = strip_absolute_path(bsdtar, name); if (*name == '\0') name = "."; if (hardlinkname != NULL) { hardlinkname = strip_absolute_path(bsdtar, hardlinkname); if (*hardlinkname == '\0') return (1); } } else { /* Strip redundant leading '/' characters. */ while (name[0] == '/' && name[1] == '/') name++; } /* Replace name in archive_entry. */ if (name != original_name) { archive_entry_copy_pathname(entry, name); } if (hardlinkname != original_hardlinkname) { archive_entry_copy_hardlink(entry, hardlinkname); } return (0); } /* * It would be nice to just use printf() for formatting large numbers, * but the compatibility problems are quite a headache. Hence the * following simple utility function. */ const char * tar_i64toa(int64_t n0) { static char buff[24]; uint64_t n = n0 < 0 ? -n0 : n0; char *p = buff + sizeof(buff); *--p = '\0'; do { *--p = '0' + (int)(n % 10); } while (n /= 10); if (n0 < 0) *--p = '-'; return p; } /* * Like strcmp(), but try to be a little more aware of the fact that * we're comparing two paths. Right now, it just handles leading * "./" and trailing '/' specially, so that "a/b/" == "./a/b" * * TODO: Make this better, so that "./a//b/./c/" == "a/b/c" * TODO: After this works, push it down into libarchive. * TODO: Publish the path normalization routines in libarchive so * that bsdtar can normalize paths and use fast strcmp() instead * of this. * * Note: This is currently only used within write.c, so should * not handle \ path separators. */ int pathcmp(const char *a, const char *b) { /* Skip leading './' */ if (a[0] == '.' && a[1] == '/' && a[2] != '\0') a += 2; if (b[0] == '.' && b[1] == '/' && b[2] != '\0') b += 2; /* Find the first difference, or return (0) if none. */ while (*a == *b) { if (*a == '\0') return (0); a++; b++; } /* * If one ends in '/' and the other one doesn't, * they're the same. */ if (a[0] == '/' && a[1] == '\0' && b[0] == '\0') return (0); if (a[0] == '\0' && b[0] == '/' && b[1] == '\0') return (0); /* They're really different, return the correct sign. */ return (*(const unsigned char *)a - *(const unsigned char *)b); } #define PPBUFF_SIZE 1024 const char * passphrase_callback(struct archive *a, void *_client_data) { struct bsdtar *bsdtar = (struct bsdtar *)_client_data; (void)a; /* UNUSED */ if (bsdtar->ppbuff == NULL) { bsdtar->ppbuff = malloc(PPBUFF_SIZE); if (bsdtar->ppbuff == NULL) lafe_errc(1, errno, "Out of memory"); } return lafe_readpassphrase("Enter passphrase:", bsdtar->ppbuff, PPBUFF_SIZE); } void passphrase_free(char *ppbuff) { if (ppbuff != NULL) { memset(ppbuff, 0, PPBUFF_SIZE); free(ppbuff); } } /* * Display information about the current file. * * The format here roughly duplicates the output of 'ls -l'. * This is based on SUSv2, where 'tar tv' is documented as * listing additional information in an "unspecified format," * and 'pax -l' is documented as using the same format as 'ls -l'. */ void list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) { char tmp[100]; size_t w; const char *p; const char *fmt; time_t tim; static time_t now; /* * We avoid collecting the entire list in memory at once by * listing things as we see them. However, that also means we can't * just pre-compute the field widths. Instead, we start with guesses * and just widen them as necessary. These numbers are completely * arbitrary. */ if (!bsdtar->u_width) { bsdtar->u_width = 6; bsdtar->gs_width = 13; } if (!now) time(&now); fprintf(out, "%s %d ", archive_entry_strmode(entry), archive_entry_nlink(entry)); /* Use uname if it's present, else uid. */ p = archive_entry_uname(entry); if ((p == NULL) || (*p == '\0')) { sprintf(tmp, "%lu ", (unsigned long)archive_entry_uid(entry)); p = tmp; } w = strlen(p); if (w > bsdtar->u_width) bsdtar->u_width = w; fprintf(out, "%-*s ", (int)bsdtar->u_width, p); /* Use gname if it's present, else gid. */ p = archive_entry_gname(entry); if (p != NULL && p[0] != '\0') { fprintf(out, "%s", p); w = strlen(p); } else { sprintf(tmp, "%lu", (unsigned long)archive_entry_gid(entry)); w = strlen(tmp); fprintf(out, "%s", tmp); } /* * Print device number or file size, right-aligned so as to make * total width of group and devnum/filesize fields be gs_width. * If gs_width is too small, grow it. */ if (archive_entry_filetype(entry) == AE_IFCHR || archive_entry_filetype(entry) == AE_IFBLK) { sprintf(tmp, "%lu,%lu", (unsigned long)archive_entry_rdevmajor(entry), (unsigned long)archive_entry_rdevminor(entry)); } else { strcpy(tmp, tar_i64toa(archive_entry_size(entry))); } if (w + strlen(tmp) >= bsdtar->gs_width) bsdtar->gs_width = w+strlen(tmp)+1; fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp); /* Format the time using 'ls -l' conventions. */ tim = archive_entry_mtime(entry); #define HALF_YEAR (time_t)365 * 86400 / 2 #if defined(_WIN32) && !defined(__CYGWIN__) #define DAY_FMT "%d" /* Windows' strftime function does not support %e format. */ #else #define DAY_FMT "%e" /* Day number without leading zeros */ #endif if (tim < now - HALF_YEAR || tim > now + HALF_YEAR) fmt = bsdtar->day_first ? DAY_FMT " %b %Y" : "%b " DAY_FMT " %Y"; else fmt = bsdtar->day_first ? DAY_FMT " %b %H:%M" : "%b " DAY_FMT " %H:%M"; strftime(tmp, sizeof(tmp), fmt, localtime(&tim)); fprintf(out, " %s ", tmp); safe_fprintf(out, "%s", archive_entry_pathname(entry)); /* Extra information for links. */ if (archive_entry_hardlink(entry)) /* Hard link */ safe_fprintf(out, " link to %s", archive_entry_hardlink(entry)); else if (archive_entry_symlink(entry)) /* Symbolic link */ safe_fprintf(out, " -> %s", archive_entry_symlink(entry)); }
static void verify_contents(struct archive *a, int expect_details) { char filedata[64]; struct archive_entry *ae; /* * Read and verify first file. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(1, archive_entry_mtime(ae)); /* Zip doesn't store high-resolution mtime. */ assertEqualInt(0, archive_entry_mtime_nsec(ae)); assertEqualInt(0, archive_entry_atime(ae)); assertEqualInt(0, archive_entry_ctime(ae)); assertEqualString("file", archive_entry_pathname(ae)); if (expect_details) { assertEqualInt(AE_IFREG | 0755, archive_entry_mode(ae)); assertEqualInt(8, archive_entry_size(ae)); } else { assertEqualInt(0, archive_entry_size(ae)); } assertEqualIntA(a, 8, archive_read_data(a, filedata, sizeof(filedata))); assertEqualMem(filedata, "12345678", 8); /* * Read the second file back. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(1, archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_mtime_nsec(ae)); assertEqualInt(0, archive_entry_atime(ae)); assertEqualInt(0, archive_entry_ctime(ae)); assertEqualString("file2", archive_entry_pathname(ae)); if (expect_details) { assertEqualInt(AE_IFREG | 0755, archive_entry_mode(ae)); assertEqualInt(4, archive_entry_size(ae)); } else { assertEqualInt(0, archive_entry_size(ae)); } assertEqualIntA(a, 4, archive_read_data(a, filedata, sizeof(filedata))); assertEqualMem(filedata, "1234", 4); /* * Read the third file back. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(1, archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_mtime_nsec(ae)); assertEqualInt(0, archive_entry_atime(ae)); assertEqualInt(0, archive_entry_ctime(ae)); assertEqualString("symlink", archive_entry_pathname(ae)); if (expect_details) { assertEqualInt(AE_IFLNK | 0755, archive_entry_mode(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualString("file1", archive_entry_symlink(ae)); } else { assertEqualInt(AE_IFREG | 0777, archive_entry_mode(ae)); assertEqualInt(0, archive_entry_size(ae)); } /* * Read the dir entry back. */ assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualInt(11, archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_mtime_nsec(ae)); assertEqualInt(0, archive_entry_atime(ae)); assertEqualInt(0, archive_entry_ctime(ae)); assertEqualString("dir/", archive_entry_pathname(ae)); if (expect_details) assertEqualInt(AE_IFDIR | 0755, archive_entry_mode(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualIntA(a, 0, archive_read_data(a, filedata, 10)); /* Verify the end of the archive. */ assertEqualIntA(a, ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualInt(ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); }
static int write_header(struct archive_write *a, struct archive_entry *entry) { int64_t ino; struct cpio *cpio; const char *p, *path; int pathlength, ret, ret_final; char h[c_header_size]; struct archive_string_conv *sconv; struct archive_entry *entry_main; size_t len; int pad; cpio = (struct cpio *)a->format_data; ret_final = ARCHIVE_OK; sconv = get_sconv(a); #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure the path separators in pahtname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ entry_main = __la_win_entry_in_posix_pathseparator(entry); if (entry_main == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate ustar data"); return(ARCHIVE_FATAL); } if (entry != entry_main) entry = entry_main; else entry_main = NULL; #else entry_main = NULL; #endif ret = archive_entry_pathname_l(entry, &path, &len, sconv); if (ret != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); ret_final = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate pathname '%s' to %s", archive_entry_pathname(entry), archive_string_conversion_charset_name(sconv)); ret_final = ARCHIVE_WARN; } pathlength = (int)len + 1; /* Include trailing null. */ memset(h, 0, c_header_size); format_hex(0x070701, h + c_magic_offset, c_magic_size); format_hex(archive_entry_devmajor(entry), h + c_devmajor_offset, c_devmajor_size); format_hex(archive_entry_devminor(entry), h + c_devminor_offset, c_devminor_size); ino = archive_entry_ino64(entry); if (ino > 0xffffffff) { archive_set_error(&a->archive, ERANGE, "large inode number truncated"); ret_final = ARCHIVE_WARN; } /* TODO: Set ret_final to ARCHIVE_WARN if any of these overflow. */ format_hex(ino & 0xffffffff, h + c_ino_offset, c_ino_size); format_hex(archive_entry_mode(entry), h + c_mode_offset, c_mode_size); format_hex(archive_entry_uid(entry), h + c_uid_offset, c_uid_size); format_hex(archive_entry_gid(entry), h + c_gid_offset, c_gid_size); format_hex(archive_entry_nlink(entry), h + c_nlink_offset, c_nlink_size); if (archive_entry_filetype(entry) == AE_IFBLK || archive_entry_filetype(entry) == AE_IFCHR) { format_hex(archive_entry_rdevmajor(entry), h + c_rdevmajor_offset, c_rdevmajor_size); format_hex(archive_entry_rdevminor(entry), h + c_rdevminor_offset, c_rdevminor_size); } else { format_hex(0, h + c_rdevmajor_offset, c_rdevmajor_size); format_hex(0, h + c_rdevminor_offset, c_rdevminor_size); } format_hex(archive_entry_mtime(entry), h + c_mtime_offset, c_mtime_size); format_hex(pathlength, h + c_namesize_offset, c_namesize_size); format_hex(0, h + c_checksum_offset, c_checksum_size); /* Non-regular files don't store bodies. */ if (archive_entry_filetype(entry) != AE_IFREG) archive_entry_set_size(entry, 0); /* Symlinks get the link written as the body of the entry. */ ret = archive_entry_symlink_l(entry, &p, &len, sconv); if (ret != 0) { if (errno == ENOMEM) { archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Likname"); ret_final = ARCHIVE_FATAL; goto exit_write_header; } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate linkname '%s' to %s", archive_entry_symlink(entry), archive_string_conversion_charset_name(sconv)); ret_final = ARCHIVE_WARN; } if (len > 0 && p != NULL && *p != '\0') ret = format_hex(strlen(p), h + c_filesize_offset, c_filesize_size); else ret = format_hex(archive_entry_size(entry), h + c_filesize_offset, c_filesize_size); if (ret) { archive_set_error(&a->archive, ERANGE, "File is too large for this format."); ret_final = ARCHIVE_FAILED; goto exit_write_header; } ret = __archive_write_output(a, h, c_header_size); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } /* Pad pathname to even length. */ ret = __archive_write_output(a, path, pathlength); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } pad = PAD4(pathlength + c_header_size); if (pad) { ret = __archive_write_output(a, "\0\0\0", pad); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } } cpio->entry_bytes_remaining = archive_entry_size(entry); cpio->padding = (int)PAD4(cpio->entry_bytes_remaining); /* Write the symlink now. */ if (p != NULL && *p != '\0') { ret = __archive_write_output(a, p, strlen(p)); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } pad = PAD4(strlen(p)); ret = __archive_write_output(a, "\0\0\0", pad); if (ret != ARCHIVE_OK) { ret_final = ARCHIVE_FATAL; goto exit_write_header; } } exit_write_header: if (entry_main) archive_entry_free(entry_main); return (ret_final); }
Installer::ProceedState RomInstaller::on_checked_device() { // /sbin is not going to be populated with anything useful in a normal boot // image. We can almost guarantee that a recovery image is going to be // installed though, so we'll open the recovery partition with libmbp and // extract its /sbin with libarchive into the chroot's /sbin. mbp::BootImage bi; if (!bi.loadFile(_recovery_block_dev)) { display_msg("Failed to load recovery partition image"); return ProceedState::Fail; } const unsigned char *ramdisk_data; std::size_t ramdisk_size; bi.ramdiskImageC(&ramdisk_data, &ramdisk_size); autoclose::archive in(archive_read_new(), archive_read_free); autoclose::archive out(archive_write_disk_new(), archive_write_free); if (!in | !out) { LOGE("Out of memory"); return ProceedState::Fail; } archive_entry *entry; // Set up input archive_read_support_filter_gzip(in.get()); archive_read_support_filter_lzop(in.get()); archive_read_support_filter_lz4(in.get()); archive_read_support_filter_lzma(in.get()); archive_read_support_format_cpio(in.get()); int ret = archive_read_open_memory(in.get(), const_cast<unsigned char *>(ramdisk_data), ramdisk_size); if (ret != ARCHIVE_OK) { LOGW("Failed to open recovery ramdisk: %s", archive_error_string(in.get())); return ProceedState::Fail; } // Set up output archive_write_disk_set_options(out.get(), ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_SECURE_NODOTDOT | ARCHIVE_EXTRACT_SECURE_SYMLINKS | ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_UNLINK | ARCHIVE_EXTRACT_XATTR); while ((ret = archive_read_next_header(in.get(), &entry)) == ARCHIVE_OK) { std::string path = archive_entry_pathname(entry); if (!util::starts_with(path, "sbin/")) { continue; } LOGE("Copying from recovery: %s", path.c_str()); archive_entry_set_pathname(entry, (_chroot + "/" + path).c_str()); if (util::archive_copy_header_and_data(in.get(), out.get(), entry) != ARCHIVE_OK) { return ProceedState::Fail; } archive_entry_set_pathname(entry, path.c_str()); } if (ret != ARCHIVE_EOF) { LOGE("Archive extraction ended without reaching EOF: %s", archive_error_string(in.get())); return ProceedState::Fail; } return ProceedState::Continue; }
static void test_compress_best(void) { const char reffile[] = "test_read_format_rar_compress_best.rar"; char file1_buff[20111]; int file1_size = sizeof(file1_buff); const char file1_test_txt[] = "<P STYLE=\"margin-bottom: 0in\"><BR>\n" "</P>\n" "</BODY>\n" "</HTML>"; char file2_buff[20]; int file2_size = sizeof(file2_buff); const char file2_test_txt[] = "test text document\r\n"; struct archive_entry *ae; struct archive *a; extract_reference_file(reffile); assert((a = archive_read_new()) != NULL); assertA(0 == archive_read_support_filter_all(a)); assertA(0 == archive_read_support_format_all(a)); assertA(0 == archive_read_open_file(a, reffile, 10240)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("LibarchiveAddingTest.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file1_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertA(file1_size == archive_read_data(a, file1_buff, file1_size)); assertEqualMem(&file1_buff[file1_size - sizeof(file1_test_txt) + 1], file1_test_txt, sizeof(file1_test_txt) - 1); /* Second header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testlink", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(41471, archive_entry_mode(ae)); assertEqualString("LibarchiveAddingTest.html", archive_entry_symlink(ae)); assertEqualIntA(a, 0, archive_read_data(a, file1_buff, 30)); /* Third header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir/test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file2_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertA(file2_size == archive_read_data(a, file2_buff, file2_size)); assertEqualMem(&file2_buff[file2_size + 1 - sizeof(file2_test_txt)], file2_test_txt, sizeof(file2_test_txt) - 1); /* Fourth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir/LibarchiveAddingTest.html", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(file1_size, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertA(file1_size == archive_read_data(a, file1_buff, file1_size)); assertEqualMem(&file1_buff[file1_size - sizeof(file1_test_txt) + 1], file1_test_txt, sizeof(file1_test_txt) - 1); /* Fifth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); /* Sixth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testemptydir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(6, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); }
static int archive_write_ar_header(struct archive_write *a, struct archive_entry *entry) { int ret, append_fn; char buff[60]; char *ss, *se; struct ar_w *ar; const char *pathname; const char *filename; int64_t size; append_fn = 0; ar = (struct ar_w *)a->format_data; ar->is_strtab = 0; filename = NULL; size = archive_entry_size(entry); /* * Reject files with empty name. */ pathname = archive_entry_pathname(entry); if (pathname == NULL || *pathname == '\0') { archive_set_error(&a->archive, EINVAL, "Invalid filename"); return (ARCHIVE_WARN); } /* * If we are now at the beginning of the archive, * we need first write the ar global header. */ if (!ar->wrote_global_header) { __archive_write_output(a, "!<arch>\n", 8); ar->wrote_global_header = 1; } memset(buff, ' ', 60); strncpy(&buff[AR_fmag_offset], "`\n", 2); if (strcmp(pathname, "/") == 0 ) { /* Entry is archive symbol table in GNU format */ buff[AR_name_offset] = '/'; goto stat; } if (strcmp(pathname, "__.SYMDEF") == 0) { /* Entry is archive symbol table in BSD format */ strncpy(buff + AR_name_offset, "__.SYMDEF", 9); goto stat; } if (strcmp(pathname, "//") == 0) { /* * Entry is archive filename table, inform that we should * collect strtab in next _data call. */ ar->is_strtab = 1; buff[AR_name_offset] = buff[AR_name_offset + 1] = '/'; /* * For archive string table, only ar_size field should * be set. */ goto size; } /* * Otherwise, entry is a normal archive member. * Strip leading paths from filenames, if any. */ if ((filename = ar_basename(pathname)) == NULL) { /* Reject filenames with trailing "/" */ archive_set_error(&a->archive, EINVAL, "Invalid filename"); return (ARCHIVE_WARN); } if (a->archive.archive_format == ARCHIVE_FORMAT_AR_GNU) { /* * SVR4/GNU variant use a "/" to mark then end of the filename, * make it possible to have embedded spaces in the filename. * So, the longest filename here (without extension) is * actually 15 bytes. */ if (strlen(filename) <= 15) { strncpy(&buff[AR_name_offset], filename, strlen(filename)); buff[AR_name_offset + strlen(filename)] = '/'; } else { /* * For filename longer than 15 bytes, GNU variant * makes use of a string table and instead stores the * offset of the real filename to in the ar_name field. * The string table should have been written before. */ if (ar->has_strtab <= 0) { archive_set_error(&a->archive, EINVAL, "Can't find string table"); return (ARCHIVE_WARN); } se = (char *)malloc(strlen(filename) + 3); if (se == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate filename buffer"); return (ARCHIVE_FATAL); } strncpy(se, filename, strlen(filename)); strcpy(se + strlen(filename), "/\n"); ss = strstr(ar->strtab, se); free(se); if (ss == NULL) { archive_set_error(&a->archive, EINVAL, "Invalid string table"); return (ARCHIVE_WARN); } /* * GNU variant puts "/" followed by digits into * ar_name field. These digits indicates the real * filename string's offset to the string table. */ buff[AR_name_offset] = '/'; if (format_decimal(ss - ar->strtab, buff + AR_name_offset + 1, AR_name_size - 1)) { archive_set_error(&a->archive, ERANGE, "string table offset too large"); return (ARCHIVE_WARN); } } } else if (a->archive.archive_format == ARCHIVE_FORMAT_AR_BSD) { /* * BSD variant: for any file name which is more than * 16 chars or contains one or more embedded space(s), the * string "#1/" followed by the ASCII length of the name is * put into the ar_name field. The file size (stored in the * ar_size field) is incremented by the length of the name. * The name is then written immediately following the * archive header. */ if (strlen(filename) <= 16 && strchr(filename, ' ') == NULL) { strncpy(&buff[AR_name_offset], filename, strlen(filename)); buff[AR_name_offset + strlen(filename)] = ' '; } else { strncpy(buff + AR_name_offset, "#1/", 3); if (format_decimal(strlen(filename), buff + AR_name_offset + 3, AR_name_size - 3)) { archive_set_error(&a->archive, ERANGE, "File name too long"); return (ARCHIVE_WARN); } append_fn = 1; size += strlen(filename); } } stat: if (format_decimal(archive_entry_mtime(entry), buff + AR_date_offset, AR_date_size)) { archive_set_error(&a->archive, ERANGE, "File modification time too large"); return (ARCHIVE_WARN); } if (format_decimal(archive_entry_uid(entry), buff + AR_uid_offset, AR_uid_size)) { archive_set_error(&a->archive, ERANGE, "Numeric user ID too large"); return (ARCHIVE_WARN); } if (format_decimal(archive_entry_gid(entry), buff + AR_gid_offset, AR_gid_size)) { archive_set_error(&a->archive, ERANGE, "Numeric group ID too large"); return (ARCHIVE_WARN); } if (format_octal(archive_entry_mode(entry), buff + AR_mode_offset, AR_mode_size)) { archive_set_error(&a->archive, ERANGE, "Numeric mode too large"); return (ARCHIVE_WARN); } /* * Sanity Check: A non-pseudo archive member should always be * a regular file. */ if (filename != NULL && archive_entry_filetype(entry) != AE_IFREG) { archive_set_error(&a->archive, EINVAL, "Regular file required for non-pseudo member"); return (ARCHIVE_WARN); } size: if (format_decimal(size, buff + AR_size_offset, AR_size_size)) { archive_set_error(&a->archive, ERANGE, "File size out of range"); return (ARCHIVE_WARN); } ret = __archive_write_output(a, buff, 60); if (ret != ARCHIVE_OK) return (ret); ar->entry_bytes_remaining = size; ar->entry_padding = ar->entry_bytes_remaining % 2; if (append_fn > 0) { ret = __archive_write_output(a, filename, strlen(filename)); if (ret != ARCHIVE_OK) return (ret); ar->entry_bytes_remaining -= strlen(filename); } return (ARCHIVE_OK); }
static void test_sfx(void) { char buff[441]; const char reffile[] = "test_read_format_rar_sfx.exe"; const char test_txt[] = "test text file\r\n"; int size = sizeof(test_txt)-1; struct archive_entry *ae; struct archive *a; extract_reference_file(reffile); assert((a = archive_read_new()) != NULL); assertA(0 == archive_read_support_filter_all(a)); assertA(0 == archive_read_support_format_all(a)); assertA(0 == archive_read_open_file(a, reffile, 10240)); /* First header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(16, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertA(size == archive_read_data(a, buff, size)); assertEqualMem(buff, test_txt, size); /* Second header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testshortcut.lnk", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(sizeof(buff), archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertA(sizeof(buff) == archive_read_data(a, buff, sizeof(buff))); /* Third header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir/test.txt", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(16, archive_entry_size(ae)); assertEqualInt(33188, archive_entry_mode(ae)); assertA(size == archive_read_data(a, buff, size)); assertEqualMem(buff, test_txt, size); /* Fourth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testdir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); /* Fifth header. */ assertA(0 == archive_read_next_header(a, &ae)); assertEqualString("testemptydir", archive_entry_pathname(ae)); assertA((int)archive_entry_mtime(ae)); assertA((int)archive_entry_ctime(ae)); assertA((int)archive_entry_atime(ae)); assertEqualInt(0, archive_entry_size(ae)); assertEqualInt(16877, archive_entry_mode(ae)); /* Test EOF */ assertA(1 == archive_read_next_header(a, &ae)); assertEqualInt(5, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); }
/* * The Mac OS "copyfile()" API copies the extended metadata for a * file into a separate file in AppleDouble format (see RFC 1740). * * Mac OS tar and cpio implementations store this extended * metadata as a separate entry just before the regular entry * with a "._" prefix added to the filename. * * Note that this is currently done unconditionally; the tar program has * an option to discard this information before the archive is written. * * TODO: If there's a failure, report it and return ARCHIVE_WARN. */ static int setup_mac_metadata(struct archive_read_disk *a, struct archive_entry *entry, int *fd) { int tempfd = -1; int copyfile_flags = COPYFILE_NOFOLLOW | COPYFILE_ACL | COPYFILE_XATTR; struct stat copyfile_stat; int ret = ARCHIVE_OK; void *buff = NULL; int have_attrs; const char *name, *tempdir; struct archive_string tempfile; (void)fd; /* UNUSED */ name = archive_entry_sourcepath(entry); if (name == NULL) name = archive_entry_pathname(entry); if (name == NULL) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Can't open file to read extended attributes: No name"); return (ARCHIVE_WARN); } if (a->tree != NULL) { if (a->tree_enter_working_dir(a->tree) != 0) { archive_set_error(&a->archive, errno, "Couldn't change dir"); return (ARCHIVE_FAILED); } } /* Short-circuit if there's nothing to do. */ have_attrs = copyfile(name, NULL, 0, copyfile_flags | COPYFILE_CHECK); if (have_attrs == -1) { archive_set_error(&a->archive, errno, "Could not check extended attributes"); return (ARCHIVE_WARN); } if (have_attrs == 0) return (ARCHIVE_OK); tempdir = NULL; if (issetugid() == 0) tempdir = getenv("TMPDIR"); if (tempdir == NULL) tempdir = _PATH_TMP; archive_string_init(&tempfile); archive_strcpy(&tempfile, tempdir); archive_strcat(&tempfile, "tar.md.XXXXXX"); tempfd = mkstemp(tempfile.s); if (tempfd < 0) { archive_set_error(&a->archive, errno, "Could not open extended attribute file"); ret = ARCHIVE_WARN; goto cleanup; } __archive_ensure_cloexec_flag(tempfd); /* XXX I wish copyfile() could pack directly to a memory * buffer; that would avoid the temp file here. For that * matter, it would be nice if fcopyfile() actually worked, * that would reduce the many open/close races here. */ if (copyfile(name, tempfile.s, 0, copyfile_flags | COPYFILE_PACK)) { archive_set_error(&a->archive, errno, "Could not pack extended attributes"); ret = ARCHIVE_WARN; goto cleanup; } if (fstat(tempfd, ©file_stat)) { archive_set_error(&a->archive, errno, "Could not check size of extended attributes"); ret = ARCHIVE_WARN; goto cleanup; } buff = malloc(copyfile_stat.st_size); if (buff == NULL) { archive_set_error(&a->archive, errno, "Could not allocate memory for extended attributes"); ret = ARCHIVE_WARN; goto cleanup; } if (copyfile_stat.st_size != read(tempfd, buff, copyfile_stat.st_size)) { archive_set_error(&a->archive, errno, "Could not read extended attributes into memory"); ret = ARCHIVE_WARN; goto cleanup; } archive_entry_copy_mac_metadata(entry, buff, copyfile_stat.st_size); cleanup: if (tempfd >= 0) { close(tempfd); unlink(tempfile.s); } archive_string_free(&tempfile); free(buff); return (ret); }
/* * The reference file for this has been manually tweaked so that: * * file2 has length-at-end but file1 does not * * file2 has an invalid CRC */ static void verify_basic(struct archive *a, int seek_checks) { struct archive_entry *ae; char *buff[128]; const void *pv; size_t s; int64_t o; assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 1.0 (uncompressed)", archive_format_name(a)); assertEqualString("dir/", archive_entry_pathname(ae)); assertEqualInt(1179604249, archive_entry_mtime(ae)); assertEqualInt(0, archive_entry_size(ae)); if (seek_checks) assertEqualInt(AE_IFDIR | 0755, archive_entry_mode(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); assertEqualIntA(a, ARCHIVE_EOF, archive_read_data_block(a, &pv, &s, &o)); assertEqualInt((int)s, 0); assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 2.0 (deflation)", archive_format_name(a)); assertEqualString("file1", archive_entry_pathname(ae)); assertEqualInt(1179604289, archive_entry_mtime(ae)); if (seek_checks) assertEqualInt(AE_IFREG | 0755, archive_entry_mode(ae)); assertEqualInt(18, archive_entry_size(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); failure("archive_read_data() returns number of bytes read"); if (archive_zlib_version() != NULL) { assertEqualInt(18, archive_read_data(a, buff, 19)); assertEqualMem(buff, "hello\nhello\nhello\n", 18); } else { assertEqualInt(ARCHIVE_FAILED, archive_read_data(a, buff, 19)); assertEqualString(archive_error_string(a), "Unsupported ZIP compression method (deflation)"); assert(archive_errno(a) != 0); } assertEqualIntA(a, ARCHIVE_OK, archive_read_next_header(a, &ae)); assertEqualString("ZIP 2.0 (deflation)", archive_format_name(a)); assertEqualString("file2", archive_entry_pathname(ae)); assertEqualInt(1179605932, archive_entry_mtime(ae)); assertEqualInt(archive_entry_is_encrypted(ae), 0); assertEqualIntA(a, archive_read_has_encrypted_entries(a), 0); if (seek_checks) { assertEqualInt(AE_IFREG | 0755, archive_entry_mode(ae)); } assert(archive_entry_size_is_set(ae)); assertEqualInt(18, archive_entry_size(ae)); if (archive_zlib_version() != NULL) { failure("file2 has a bad CRC, so read should fail and not change buff"); memset(buff, 'a', 19); assertEqualInt(ARCHIVE_WARN, archive_read_data(a, buff, 19)); assertEqualMem(buff, "aaaaaaaaaaaaaaaaaaa", 19); } else { assertEqualInt(ARCHIVE_FAILED, archive_read_data(a, buff, 19)); assertEqualString(archive_error_string(a), "Unsupported ZIP compression method (deflation)"); assert(archive_errno(a) != 0); } assertEqualInt(ARCHIVE_EOF, archive_read_next_header(a, &ae)); assertEqualString("ZIP 2.0 (deflation)", archive_format_name(a)); /* Verify the number of files read. */ failure("the archive file has three files"); assertEqualInt(3, archive_file_count(a)); assertEqualIntA(a, ARCHIVE_FILTER_NONE, archive_filter_code(a, 0)); assertEqualIntA(a, ARCHIVE_FORMAT_ZIP, archive_format(a)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); }
static int setup_xattr(struct archive_read_disk *a, struct archive_entry *entry, const char *name, int fd) { ssize_t size; void *value = NULL; const char *accpath; accpath = archive_entry_sourcepath(entry); if (accpath == NULL) accpath = archive_entry_pathname(entry); #if HAVE_FGETXATTR if (fd >= 0) size = fgetxattr(fd, name, NULL, 0); else if (!a->follow_symlinks) size = lgetxattr(accpath, name, NULL, 0); else size = getxattr(accpath, name, NULL, 0); #elif HAVE_FGETEA if (fd >= 0) size = fgetea(fd, name, NULL, 0); else if (!a->follow_symlinks) size = lgetea(accpath, name, NULL, 0); else size = getea(accpath, name, NULL, 0); #endif if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't query extended attribute"); return (ARCHIVE_WARN); } if (size > 0 && (value = malloc(size)) == NULL) { archive_set_error(&a->archive, errno, "Out of memory"); return (ARCHIVE_FATAL); } #if HAVE_FGETXATTR if (fd >= 0) size = fgetxattr(fd, name, value, size); else if (!a->follow_symlinks) size = lgetxattr(accpath, name, value, size); else size = getxattr(accpath, name, value, size); #elif HAVE_FGETEA if (fd >= 0) size = fgetea(fd, name, value, size); else if (!a->follow_symlinks) size = lgetea(accpath, name, value, size); else size = getea(accpath, name, value, size); #endif if (size == -1) { archive_set_error(&a->archive, errno, "Couldn't read extended attribute"); return (ARCHIVE_WARN); } archive_entry_xattr_add_entry(entry, name, value, size); free(value); return (ARCHIVE_OK); }
bool OdinPatcher::Impl::processContents() { archive_entry *entry; int laRet; int mzRet; zipFile zf = MinizipUtils::ctxGetZipFile(zOutput); while ((laRet = archive_read_next_header(aInput, &entry)) == ARCHIVE_OK) { if (cancelled) return false; const char *name = archive_entry_pathname(entry); if (!name) { continue; } updateDetails(name); if (strcmp(name, "boot.img") == 0) { LOGD("Handling boot.img"); // Boot images should never be over about 30 MiB. This check is here // so the patcher won't try to read a multi-gigabyte system image // into RAM la_int64_t size = archive_entry_size(entry); if (size > 30 * 1024 * 1024) { LOGE("Boot image exceeds 30 MiB: %" PRId64, size); error = ErrorCode::BootImageTooLargeError; return false; } std::vector<unsigned char> data; if (size > 0) { data.reserve(size); } char buf[10240]; la_ssize_t n; while ((n = archive_read_data(aInput, buf, sizeof(buf))) > 0) { data.insert(data.end(), buf, buf + n); } if (n != 0) { LOGE("libarchive: Failed to read data: %s", archive_error_string(aInput)); error = ErrorCode::ArchiveReadDataError; return false; } if (!MultiBootPatcher::patchBootImage(pc, info, &data, &error)) { return false; } auto errorRet = MinizipUtils::addFile(zf, name, data); if (errorRet != ErrorCode::NoError) { error = errorRet; return false; } continue; } else if (!StringUtils::starts_with(name, "cache.img") && !StringUtils::starts_with(name, "system.img")) { LOGD("Skipping %s", name); if (archive_read_data_skip(aInput) != ARCHIVE_OK) { LOGE("libarchive: Failed to skip data: %s", archive_error_string(aInput)); error = ErrorCode::ArchiveReadDataError; return false; } continue; } LOGD("Handling %s", name); std::string zipName(name); if (StringUtils::ends_with(name, ".ext4")) { zipName.erase(zipName.size() - 5); } zipName += ".sparse"; // Ha! I'll be impressed if a Samsung firmware image does NOT need zip64 int zip64 = archive_entry_size(entry) > ((1ll << 32) - 1); zip_fileinfo zi; memset(&zi, 0, sizeof(zi)); // Open file in output zip mzRet = zipOpenNewFileInZip2_64( zf, // file zipName.c_str(), // filename &zi, // zip_fileinfo nullptr, // extrafield_local 0, // size_extrafield_local nullptr, // extrafield_global 0, // size_extrafield_global nullptr, // comment Z_DEFLATED, // method Z_DEFAULT_COMPRESSION, // level 0, // raw zip64 // zip64 ); if (mzRet != ZIP_OK) { LOGE("minizip: Failed to open new file in output zip: %s", MinizipUtils::zipErrorString(mzRet).c_str()); error = ErrorCode::ArchiveWriteHeaderError; return false; } la_ssize_t nRead; char buf[10240]; while ((nRead = archive_read_data(aInput, buf, sizeof(buf))) > 0) { if (cancelled) return false; mzRet = zipWriteInFileInZip(zf, buf, nRead); if (mzRet != ZIP_OK) { LOGE("minizip: Failed to write %s in output zip: %s", zipName.c_str(), MinizipUtils::zipErrorString(mzRet).c_str()); error = ErrorCode::ArchiveWriteDataError; zipCloseFileInZip(zf); return false; } } if (nRead != 0) { LOGE("libarchive: Failed to read %s: %s", name, archive_error_string(aInput)); error = ErrorCode::ArchiveReadDataError; zipCloseFileInZip(zf); return false; } // Close file in output zip mzRet = zipCloseFileInZip(zf); if (mzRet != ZIP_OK) { LOGE("minizip: Failed to close file in output zip: %s", MinizipUtils::zipErrorString(mzRet).c_str()); error = ErrorCode::ArchiveWriteDataError; return false; } } if (laRet != ARCHIVE_EOF) { LOGE("libarchive: Failed to read header: %s", archive_error_string(aInput)); error = ErrorCode::ArchiveReadHeaderError; return false; } if (cancelled) return false; return true; }
static int archive_write_cpio_header(struct archive_write *a, struct archive_entry *entry) { struct cpio *cpio; const char *p, *path; int pathlength, ret; struct cpio_header h; cpio = (struct cpio *)a->format_data; ret = 0; path = archive_entry_pathname(entry); pathlength = strlen(path) + 1; /* Include trailing null. */ memset(&h, 0, sizeof(h)); format_octal(070707, &h.c_magic, sizeof(h.c_magic)); format_octal(archive_entry_dev(entry), &h.c_dev, sizeof(h.c_dev)); /* * TODO: Generate artificial inode numbers rather than just * re-using the ones off the disk. That way, the 18-bit c_ino * field only limits the number of files in the archive. */ if (archive_entry_ino(entry) > 0777777) { archive_set_error(&a->archive, ERANGE, "large inode number truncated"); ret = ARCHIVE_WARN; } format_octal(archive_entry_ino(entry) & 0777777, &h.c_ino, sizeof(h.c_ino)); format_octal(archive_entry_mode(entry), &h.c_mode, sizeof(h.c_mode)); format_octal(archive_entry_uid(entry), &h.c_uid, sizeof(h.c_uid)); format_octal(archive_entry_gid(entry), &h.c_gid, sizeof(h.c_gid)); format_octal(archive_entry_nlink(entry), &h.c_nlink, sizeof(h.c_nlink)); if (archive_entry_filetype(entry) == AE_IFBLK || archive_entry_filetype(entry) == AE_IFCHR) format_octal(archive_entry_dev(entry), &h.c_rdev, sizeof(h.c_rdev)); else format_octal(0, &h.c_rdev, sizeof(h.c_rdev)); format_octal(archive_entry_mtime(entry), &h.c_mtime, sizeof(h.c_mtime)); format_octal(pathlength, &h.c_namesize, sizeof(h.c_namesize)); /* Non-regular files don't store bodies. */ if (archive_entry_filetype(entry) != AE_IFREG) archive_entry_set_size(entry, 0); /* Symlinks get the link written as the body of the entry. */ p = archive_entry_symlink(entry); if (p != NULL && *p != '\0') format_octal(strlen(p), &h.c_filesize, sizeof(h.c_filesize)); else format_octal(archive_entry_size(entry), &h.c_filesize, sizeof(h.c_filesize)); ret = (a->compressor.write)(a, &h, sizeof(h)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); ret = (a->compressor.write)(a, path, pathlength); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); cpio->entry_bytes_remaining = archive_entry_size(entry); /* Write the symlink now. */ if (p != NULL && *p != '\0') ret = (a->compressor.write)(a, p, strlen(p)); return (ret); }
bool Drumkit::install( const QString& path ) { _INFOLOG( QString( "Install drumkit %1" ).arg( path ) ); #ifdef H2CORE_HAVE_LIBARCHIVE int r; struct archive* arch; struct archive_entry* entry; arch = archive_read_new(); #if ARCHIVE_VERSION_NUMBER < 3000000 archive_read_support_compression_all( arch ); #else archive_read_support_filter_all( arch ); #endif archive_read_support_format_all( arch ); #if ARCHIVE_VERSION_NUMBER < 3000000 if ( ( r = archive_read_open_file( arch, path.toLocal8Bit(), 10240 ) ) ) { #else if ( ( r = archive_read_open_filename( arch, path.toLocal8Bit(), 10240 ) ) ) { #endif _ERRORLOG( QString( "archive_read_open_file() [%1] %2" ).arg( archive_errno( arch ) ).arg( archive_error_string( arch ) ) ); archive_read_close( arch ); #if ARCHIVE_VERSION_NUMBER < 3000000 archive_read_finish( arch ); #else archive_read_free( arch ); #endif return false; } bool ret = true; QString dk_dir = Filesystem::usr_drumkits_dir() + "/"; while ( ( r = archive_read_next_header( arch, &entry ) ) != ARCHIVE_EOF ) { if ( r != ARCHIVE_OK ) { _ERRORLOG( QString( "archive_read_next_header() [%1] %2" ).arg( archive_errno( arch ) ).arg( archive_error_string( arch ) ) ); ret = false; break; } QString np = dk_dir + archive_entry_pathname( entry ); QByteArray newpath = np.toLocal8Bit(); archive_entry_set_pathname( entry, newpath.data() ); r = archive_read_extract( arch, entry, 0 ); if ( r == ARCHIVE_WARN ) { _WARNINGLOG( QString( "archive_read_extract() [%1] %2" ).arg( archive_errno( arch ) ).arg( archive_error_string( arch ) ) ); } else if ( r != ARCHIVE_OK ) { _ERRORLOG( QString( "archive_read_extract() [%1] %2" ).arg( archive_errno( arch ) ).arg( archive_error_string( arch ) ) ); ret = false; break; } } archive_read_close( arch ); #if ARCHIVE_VERSION_NUMBER < 3000000 archive_read_finish( arch ); #else archive_read_free( arch ); #endif return ret; #else // H2CORE_HAVE_LIBARCHIVE #ifndef WIN32 // GUNZIP QString gzd_name = path.left( path.indexOf( "." ) ) + ".tar"; FILE* gzd_file = fopen( gzd_name.toLocal8Bit(), "wb" ); gzFile gzip_file = gzopen( path.toLocal8Bit(), "rb" ); if ( !gzip_file ) { _ERRORLOG( QString( "Error reading drumkit file: %1" ).arg( path ) ); gzclose( gzip_file ); fclose( gzd_file ); return false; } uchar buf[4096]; while ( gzread( gzip_file, buf, 4096 ) > 0 ) { fwrite( buf, sizeof( uchar ), 4096, gzd_file ); } gzclose( gzip_file ); fclose( gzd_file ); // UNTAR TAR* tar_file; QByteArray tar_path = gzd_name.toLocal8Bit(); if ( tar_open( &tar_file, tar_path.data(), NULL, O_RDONLY, 0, TAR_GNU ) == -1 ) { _ERRORLOG( QString( "tar_open(): %1" ).arg( QString::fromLocal8Bit( strerror( errno ) ) ) ); return false; } bool ret = true; char dst_dir[1024]; QString dk_dir = Filesystem::usr_drumkits_dir() + "/"; strncpy( dst_dir, dk_dir.toLocal8Bit(), 1024 ); if ( tar_extract_all( tar_file, dst_dir ) != 0 ) { _ERRORLOG( QString( "tar_extract_all(): %1" ).arg( QString::fromLocal8Bit( strerror( errno ) ) ) ); ret = false; } if ( tar_close( tar_file ) != 0 ) { _ERRORLOG( QString( "tar_close(): %1" ).arg( QString::fromLocal8Bit( strerror( errno ) ) ) ); ret = false; } return ret; #else // WIN32 _ERRORLOG( "WIN32 NOT IMPLEMENTED" ); return false; #endif #endif } };
static void test_filename(const char *prefix, int dlen, int flen) { char buff[8192]; char filename[400]; char dirname[400]; struct archive_entry *ae; struct archive *a; size_t used; int separator = 0; int i = 0; if (prefix != NULL) { strcpy(filename, prefix); i = (int)strlen(prefix); } if (dlen > 0) { for (; i < dlen; i++) filename[i] = 'a'; filename[i++] = '/'; separator = 1; } for (; i < dlen + flen + separator; i++) filename[i] = 'b'; filename[i] = '\0'; strcpy(dirname, filename); /* Create a new archive in memory. */ assert((a = archive_write_new()) != NULL); assertA(0 == archive_write_set_format_ustar(a)); assertA(0 == archive_write_add_filter_none(a)); assertA(0 == archive_write_set_bytes_per_block(a,0)); assertA(0 == archive_write_open_memory(a, buff, sizeof(buff), &used)); /* * Write a file to it. */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, filename); archive_entry_set_mode(ae, S_IFREG | 0755); failure("dlen=%d, flen=%d", dlen, flen); if (flen > 100) { assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae)); } else { assertEqualIntA(a, 0, archive_write_header(a, ae)); } archive_entry_free(ae); /* * Write a dir to it (without trailing '/'). */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, dirname); archive_entry_set_mode(ae, S_IFDIR | 0755); failure("dlen=%d, flen=%d", dlen, flen); if (flen >= 100) { assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae)); } else { assertEqualIntA(a, 0, archive_write_header(a, ae)); } archive_entry_free(ae); /* Tar adds a '/' to directory names. */ strcat(dirname, "/"); /* * Write a dir to it (with trailing '/'). */ assert((ae = archive_entry_new()) != NULL); archive_entry_copy_pathname(ae, dirname); archive_entry_set_mode(ae, S_IFDIR | 0755); failure("dlen=%d, flen=%d", dlen, flen); if (flen >= 100) { assertEqualIntA(a, ARCHIVE_FAILED, archive_write_header(a, ae)); } else { assertEqualIntA(a, 0, archive_write_header(a, ae)); } archive_entry_free(ae); /* Close out the archive. */ assertEqualIntA(a, ARCHIVE_OK, archive_write_close(a)); assertEqualInt(ARCHIVE_OK, archive_write_free(a)); /* * Now, read the data back. */ assert((a = archive_read_new()) != NULL); assertA(0 == archive_read_support_format_all(a)); assertA(0 == archive_read_support_filter_all(a)); assertA(0 == archive_read_open_memory(a, buff, used)); if (flen <= 100) { /* Read the file and check the filename. */ assertA(0 == archive_read_next_header(a, &ae)); failure("dlen=%d, flen=%d", dlen, flen); assertEqualString(filename, archive_entry_pathname(ae)); assertEqualInt((S_IFREG | 0755), archive_entry_mode(ae)); } /* * Read the two dirs and check the names. * * Both dirs should read back with the same name, since * tar should add a trailing '/' to any dir that doesn't * already have one. */ if (flen <= 99) { assertA(0 == archive_read_next_header(a, &ae)); assert((S_IFDIR | 0755) == archive_entry_mode(ae)); failure("dlen=%d, flen=%d", dlen, flen); assertEqualString(dirname, archive_entry_pathname(ae)); } if (flen <= 99) { assertA(0 == archive_read_next_header(a, &ae)); assert((S_IFDIR | 0755) == archive_entry_mode(ae)); assertEqualString(dirname, archive_entry_pathname(ae)); } /* Verify the end of the archive. */ failure("This fails if entries were written that should not have been written. dlen=%d, flen=%d", dlen, flen); assertEqualInt(1, archive_read_next_header(a, &ae)); assertEqualIntA(a, ARCHIVE_OK, archive_read_close(a)); assertEqualInt(ARCHIVE_OK, archive_read_free(a)); }
static int do_extract(struct archive *a, struct archive_entry *ae, const char *location, int nfiles, struct pkg *pkg, struct pkg *local) { int retcode = EPKG_OK; int ret = 0, cur_file = 0; char path[MAXPATHLEN], pathname[MAXPATHLEN], rpath[MAXPATHLEN]; struct stat st; const struct stat *aest; bool renamed = false; const struct pkg_file *rf; struct pkg_config_file *rcf; struct sbuf *newconf; bool automerge = pkg_object_bool(pkg_config_get("AUTOMERGE")); unsigned long set, clear; #ifndef HAVE_ARC4RANDOM srand(time(NULL)); #endif if (nfiles == 0) return (EPKG_OK); pkg_emit_extract_begin(pkg); pkg_emit_progress_start(NULL); newconf = sbuf_new_auto(); do { ret = ARCHIVE_OK; sbuf_clear(newconf); rf = NULL; rcf = NULL; pkg_absolutepath(archive_entry_pathname(ae), path, sizeof(path)); snprintf(pathname, sizeof(pathname), "%s%s%s", location ? location : "", *path == '/' ? "" : "/", path ); strlcpy(rpath, pathname, sizeof(rpath)); aest = archive_entry_stat(ae); archive_entry_fflags(ae, &set, &clear); if (lstat(rpath, &st) != -1) { /* * We have an existing file on the path, so handle it */ if (!S_ISDIR(aest->st_mode)) { pkg_debug(2, "Old version found, renaming"); pkg_add_file_random_suffix(rpath, sizeof(rpath), 12); renamed = true; } if (!S_ISDIR(st.st_mode) && S_ISDIR(aest->st_mode)) { if (S_ISLNK(st.st_mode)) { if (stat(rpath, &st) == -1) { pkg_emit_error("Dead symlink %s", rpath); } else { pkg_debug(2, "Directory is a symlink, use it"); pkg_emit_progress_tick(cur_file++, nfiles); continue; } } } } archive_entry_set_pathname(ae, rpath); /* load in memory the content of config files */ if (pkg_is_config_file(pkg, path, &rf, &rcf)) { pkg_debug(1, "Populating config_file %s", pathname); size_t len = archive_entry_size(ae); rcf->content = malloc(len); archive_read_data(a, rcf->content, len); if (renamed && (!automerge || local == NULL)) strlcat(pathname, ".pkgnew", sizeof(pathname)); } /* * check if the file is already provided by previous package */ if (!automerge) attempt_to_merge(renamed, rcf, local, pathname, path, newconf); if (sbuf_len(newconf) == 0 && (rcf == NULL || rcf->content == NULL)) { pkg_debug(1, "Extracting: %s", archive_entry_pathname(ae)); int install_as_user = (getenv("INSTALL_AS_USER") != NULL); int extract_flags = EXTRACT_ARCHIVE_FLAGS; if (install_as_user) { /* when installing as user don't try to set file ownership */ extract_flags &= ~ARCHIVE_EXTRACT_OWNER; } ret = archive_read_extract(a, ae, extract_flags); } else { if (sbuf_len(newconf) == 0) { sbuf_cat(newconf, rcf->content); sbuf_finish(newconf); } pkg_debug(2, "Writing conf in %s", pathname); unlink(rpath); FILE *f = fopen(rpath, "w+"); fprintf(f, "%s", sbuf_data(newconf)); fclose(f); } if (ret != ARCHIVE_OK) { /* * show error except when the failure is during * extracting a directory and that the directory already * exists. * this allow to install packages linux_base from * package for example */ if (archive_entry_filetype(ae) != AE_IFDIR || !is_dir(pathname)) { pkg_emit_error("archive_read_extract(): %s", archive_error_string(a)); retcode = EPKG_FATAL; goto cleanup; } } /* Reapply modes to the directories to work around a problem on FreeBSD 9 */ if (archive_entry_filetype(ae) == AE_IFDIR) chmod(pathname, aest->st_mode); pkg_emit_progress_tick(cur_file++, nfiles); /* Rename old file */ if (renamed) { pkg_debug(1, "Renaming %s -> %s", rpath, pathname); #ifdef HAVE_CHFLAGS bool old = false; if (set & NOCHANGESFLAGS) chflags(rpath, 0); if (lstat(pathname, &st) != -1) { old = true; if (st.st_flags & NOCHANGESFLAGS) chflags(pathname, 0); } #endif if (rename(rpath, pathname) == -1) { #ifdef HAVE_CHFLAGS /* restore flags */ if (old) chflags(pathname, st.st_flags); #endif pkg_emit_error("cannot rename %s to %s: %s", rpath, pathname, strerror(errno)); retcode = EPKG_FATAL; goto cleanup; } #ifdef HAVE_CHFLAGS /* Restore flags */ chflags(pathname, set); #endif } if (string_end_with(pathname, ".pkgnew")) pkg_emit_notice("New configuration file: %s", pathname); renamed = false; } while ((ret = archive_read_next_header(a, &ae)) == ARCHIVE_OK); if (ret != ARCHIVE_EOF) { pkg_emit_error("archive_read_next_header(): %s", archive_error_string(a)); retcode = EPKG_FATAL; } cleanup: pkg_emit_progress_tick(nfiles, nfiles); pkg_emit_extract_finished(pkg); if (renamed && retcode == EPKG_FATAL) { #ifdef HAVE_CHFLAGS if (set & NOCHANGESFLAGS) chflags(rpath, set & ~NOCHANGESFLAGS); #endif unlink(rpath); } return (retcode); }
static int archive_write_zip_header(struct archive_write *a, struct archive_entry *entry) { struct zip *zip; uint8_t h[SIZE_LOCAL_FILE_HEADER]; uint8_t e[SIZE_EXTRA_DATA_LOCAL]; uint8_t *d; struct zip_file_header_link *l; struct archive_string_conv *sconv; int ret, ret2 = ARCHIVE_OK; int64_t size; mode_t type; /* Entries other than a regular file or a folder are skipped. */ type = archive_entry_filetype(entry); if (type != AE_IFREG && type != AE_IFDIR && type != AE_IFLNK) { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Filetype not supported"); return ARCHIVE_FAILED; }; /* Directory entries should have a size of 0. */ if (type == AE_IFDIR) archive_entry_set_size(entry, 0); zip = a->format_data; /* Setup default conversion. */ if (zip->opt_sconv == NULL && !zip->init_default_conversion) { zip->sconv_default = archive_string_default_conversion_for_write(&(a->archive)); zip->init_default_conversion = 1; } if (zip->flags == 0) { /* Initialize the general purpose flags. */ zip->flags = ZIP_FLAGS; if (zip->opt_sconv != NULL) { if (strcmp(archive_string_conversion_charset_name( zip->opt_sconv), "UTF-8") == 0) zip->flags |= ZIP_FLAGS_UTF8_NAME; #if HAVE_NL_LANGINFO } else if (strcmp(nl_langinfo(CODESET), "UTF-8") == 0) { zip->flags |= ZIP_FLAGS_UTF8_NAME; #endif } } d = zip->data_descriptor; size = archive_entry_size(entry); zip->remaining_data_bytes = size; /* Append archive entry to the central directory data. */ l = (struct zip_file_header_link *) malloc(sizeof(*l)); if (l == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip header data"); return (ARCHIVE_FATAL); } #if defined(_WIN32) && !defined(__CYGWIN__) /* Make sure the path separators in pahtname, hardlink and symlink * are all slash '/', not the Windows path separator '\'. */ l->entry = __la_win_entry_in_posix_pathseparator(entry); if (l->entry == entry) l->entry = archive_entry_clone(entry); #else l->entry = archive_entry_clone(entry); #endif if (l->entry == NULL) { archive_set_error(&a->archive, ENOMEM, "Can't allocate zip header data"); free(l); return (ARCHIVE_FATAL); } l->flags = zip->flags; if (zip->opt_sconv != NULL) sconv = zip->opt_sconv; else sconv = zip->sconv_default; if (sconv != NULL) { const char *p; size_t len; if (archive_entry_pathname_l(entry, &p, &len, sconv) != 0) { if (errno == ENOMEM) { archive_entry_free(l->entry); free(l); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory for Pathname"); return (ARCHIVE_FATAL); } archive_set_error(&a->archive, ARCHIVE_ERRNO_FILE_FORMAT, "Can't translate Pathname '%s' to %s", archive_entry_pathname(entry), archive_string_conversion_charset_name(sconv)); ret2 = ARCHIVE_WARN; } if (len > 0) archive_entry_set_pathname(l->entry, p); /* * Although there is no character-set regulation for Symlink, * it is suitable to convert a character-set of Symlinke to * what those of the Pathname has been converted to. */ if (type == AE_IFLNK) { if (archive_entry_symlink_l(entry, &p, &len, sconv)) { if (errno == ENOMEM) { archive_entry_free(l->entry); free(l); archive_set_error(&a->archive, ENOMEM, "Can't allocate memory " " for Symlink"); return (ARCHIVE_FATAL); } /* * Even if the strng conversion failed, * we should not report the error since * thre is no regulation for. */ } else if (len > 0) archive_entry_set_symlink(l->entry, p); } } /* If all characters in a filename are ASCII, Reset UTF-8 Name flag. */ if ((l->flags & ZIP_FLAGS_UTF8_NAME) != 0 && is_all_ascii(archive_entry_pathname(l->entry))) l->flags &= ~ZIP_FLAGS_UTF8_NAME; /* Initialize the CRC variable and potentially the local crc32(). */ l->crc32 = crc32(0, NULL, 0); if (type == AE_IFLNK) { const char *p = archive_entry_symlink(l->entry); if (p != NULL) size = strlen(p); else size = 0; zip->remaining_data_bytes = 0; archive_entry_set_size(l->entry, size); l->compression = COMPRESSION_STORE; l->compressed_size = size; } else { l->compression = zip->compression; l->compressed_size = 0; } l->next = NULL; if (zip->central_directory == NULL) { zip->central_directory = l; } else { zip->central_directory_end->next = l; } zip->central_directory_end = l; /* Store the offset of this header for later use in central * directory. */ l->offset = zip->written_bytes; memset(h, 0, sizeof(h)); archive_le32enc(&h[LOCAL_FILE_HEADER_SIGNATURE], ZIP_SIGNATURE_LOCAL_FILE_HEADER); archive_le16enc(&h[LOCAL_FILE_HEADER_VERSION], ZIP_VERSION_EXTRACT); archive_le16enc(&h[LOCAL_FILE_HEADER_FLAGS], l->flags); archive_le16enc(&h[LOCAL_FILE_HEADER_COMPRESSION], l->compression); archive_le32enc(&h[LOCAL_FILE_HEADER_TIMEDATE], dos_time(archive_entry_mtime(entry))); archive_le16enc(&h[LOCAL_FILE_HEADER_FILENAME_LENGTH], (uint16_t)path_length(l->entry)); switch (l->compression) { case COMPRESSION_STORE: /* Setting compressed and uncompressed sizes even when * specification says to set to zero when using data * descriptors. Otherwise the end of the data for an * entry is rather difficult to find. */ archive_le32enc(&h[LOCAL_FILE_HEADER_COMPRESSED_SIZE], (uint32_t)size); archive_le32enc(&h[LOCAL_FILE_HEADER_UNCOMPRESSED_SIZE], (uint32_t)size); break; #ifdef HAVE_ZLIB_H case COMPRESSION_DEFLATE: archive_le32enc(&h[LOCAL_FILE_HEADER_UNCOMPRESSED_SIZE], (uint32_t)size); zip->stream.zalloc = Z_NULL; zip->stream.zfree = Z_NULL; zip->stream.opaque = Z_NULL; zip->stream.next_out = zip->buf; zip->stream.avail_out = (uInt)zip->len_buf; if (deflateInit2(&zip->stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -15, 8, Z_DEFAULT_STRATEGY) != Z_OK) { archive_set_error(&a->archive, ENOMEM, "Can't init deflate compressor"); return (ARCHIVE_FATAL); } break; #endif } /* Formatting extra data. */ archive_le16enc(&h[LOCAL_FILE_HEADER_EXTRA_LENGTH], sizeof(e)); archive_le16enc(&e[EXTRA_DATA_LOCAL_TIME_ID], ZIP_SIGNATURE_EXTRA_TIMESTAMP); archive_le16enc(&e[EXTRA_DATA_LOCAL_TIME_SIZE], 1 + 4 * 3); e[EXTRA_DATA_LOCAL_TIME_FLAG] = 0x07; archive_le32enc(&e[EXTRA_DATA_LOCAL_MTIME], (uint32_t)archive_entry_mtime(entry)); archive_le32enc(&e[EXTRA_DATA_LOCAL_ATIME], (uint32_t)archive_entry_atime(entry)); archive_le32enc(&e[EXTRA_DATA_LOCAL_CTIME], (uint32_t)archive_entry_ctime(entry)); archive_le16enc(&e[EXTRA_DATA_LOCAL_UNIX_ID], ZIP_SIGNATURE_EXTRA_NEW_UNIX); archive_le16enc(&e[EXTRA_DATA_LOCAL_UNIX_SIZE], 1 + (1 + 4) * 2); e[EXTRA_DATA_LOCAL_UNIX_VERSION] = 1; e[EXTRA_DATA_LOCAL_UNIX_UID_SIZE] = 4; archive_le32enc(&e[EXTRA_DATA_LOCAL_UNIX_UID], (uint32_t)archive_entry_uid(entry)); e[EXTRA_DATA_LOCAL_UNIX_GID_SIZE] = 4; archive_le32enc(&e[EXTRA_DATA_LOCAL_UNIX_GID], (uint32_t)archive_entry_gid(entry)); archive_le32enc(&d[DATA_DESCRIPTOR_UNCOMPRESSED_SIZE], (uint32_t)size); ret = __archive_write_output(a, h, sizeof(h)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += sizeof(h); ret = write_path(l->entry, a); if (ret <= ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += ret; ret = __archive_write_output(a, e, sizeof(e)); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += sizeof(e); if (type == AE_IFLNK) { const unsigned char *p; p = (const unsigned char *)archive_entry_symlink(l->entry); ret = __archive_write_output(a, p, (size_t)size); if (ret != ARCHIVE_OK) return (ARCHIVE_FATAL); zip->written_bytes += size; l->crc32 = crc32(l->crc32, p, (unsigned)size); } if (ret2 != ARCHIVE_OK) return (ret2); return (ARCHIVE_OK); }
int pkg_update(const char *name, const char *packagesite, bool force) { char url[MAXPATHLEN]; struct archive *a = NULL; struct archive_entry *ae = NULL; char repofile[MAXPATHLEN]; char repofile_unchecked[MAXPATHLEN]; char tmp[MAXPATHLEN]; const char *dbdir = NULL; const char *repokey; unsigned char *sig = NULL; int siglen = 0; int fd, rc = EPKG_FATAL, ret; struct stat st; time_t t = 0; sqlite3 *sqlite; char *archreq = NULL; const char *myarch; int64_t res; const char *tmpdir; snprintf(url, MAXPATHLEN, "%s/repo.txz", packagesite); tmpdir = getenv("TMPDIR"); if (tmpdir == NULL) tmpdir = "/tmp"; strlcpy(tmp, tmpdir, sizeof(tmp)); strlcat(tmp, "/repo.txz.XXXXXX", sizeof(tmp)); fd = mkstemp(tmp); if (fd == -1) { pkg_emit_error("Could not create temporary file %s, " "aborting update.\n", tmp); return (EPKG_FATAL); } if (pkg_config_string(PKG_CONFIG_DBDIR, &dbdir) != EPKG_OK) { pkg_emit_error("Cant get dbdir config entry"); return (EPKG_FATAL); } snprintf(repofile, sizeof(repofile), "%s/%s.sqlite", dbdir, name); if (force) t = 0; /* Always fetch */ else { if (stat(repofile, &st) != -1) { t = st.st_mtime; /* add 1 minute to the timestamp because * repo.sqlite is always newer than repo.txz, * 1 minute should be enough. */ t += 60; } } rc = pkg_fetch_file_to_fd(url, fd, t); close(fd); if (rc != EPKG_OK) { goto cleanup; } if (eaccess(repofile, W_OK) == -1) { pkg_emit_error("Insufficient privilege to update %s\n", repofile); rc = EPKG_ENOACCESS; goto cleanup; } a = archive_read_new(); archive_read_support_compression_all(a); archive_read_support_format_tar(a); archive_read_open_filename(a, tmp, 4096); while (archive_read_next_header(a, &ae) == ARCHIVE_OK) { if (strcmp(archive_entry_pathname(ae), "repo.sqlite") == 0) { snprintf(repofile_unchecked, sizeof(repofile_unchecked), "%s.unchecked", repofile); archive_entry_set_pathname(ae, repofile_unchecked); /* * The repo should be owned by root and not writable */ archive_entry_set_uid(ae, 0); archive_entry_set_gid(ae, 0); archive_entry_set_perm(ae, 0644); archive_read_extract(a, ae, EXTRACT_ARCHIVE_FLAGS); } if (strcmp(archive_entry_pathname(ae), "signature") == 0) { siglen = archive_entry_size(ae); sig = malloc(siglen); archive_read_data(a, sig, siglen); } } if (pkg_config_string(PKG_CONFIG_REPOKEY, &repokey) != EPKG_OK) { free(sig); return (EPKG_FATAL); } if (repokey != NULL) { if (sig != NULL) { ret = rsa_verify(repofile_unchecked, repokey, sig, siglen - 1); if (ret != EPKG_OK) { pkg_emit_error("Invalid signature, " "removing repository.\n"); unlink(repofile_unchecked); free(sig); rc = EPKG_FATAL; goto cleanup; } free(sig); } else { pkg_emit_error("No signature found in the repository. " "Can not validate against %s key.", repokey); rc = EPKG_FATAL; unlink(repofile_unchecked); goto cleanup; } } /* check is the repository is for valid architecture */ sqlite3_initialize(); if (sqlite3_open(repofile_unchecked, &sqlite) != SQLITE_OK) { unlink(repofile_unchecked); pkg_emit_error("Corrupted repository"); rc = EPKG_FATAL; goto cleanup; } pkg_config_string(PKG_CONFIG_ABI, &myarch); archreq = sqlite3_mprintf("select count(arch) from packages " "where arch not GLOB '%q'", myarch); if (get_pragma(sqlite, archreq, &res) != EPKG_OK) { sqlite3_free(archreq); pkg_emit_error("Unable to query repository"); rc = EPKG_FATAL; sqlite3_close(sqlite); goto cleanup; } if (res > 0) { pkg_emit_error("At least one of the packages provided by" "the repository is not compatible with your abi: %s", myarch); rc = EPKG_FATAL; sqlite3_close(sqlite); goto cleanup; } sqlite3_close(sqlite); sqlite3_shutdown(); if (rename(repofile_unchecked, repofile) != 0) { pkg_emit_errno("rename", ""); rc = EPKG_FATAL; goto cleanup; } if ((rc = remote_add_indexes(name)) != EPKG_OK) goto cleanup; rc = EPKG_OK; cleanup: if (a != NULL) archive_read_finish(a); (void)unlink(tmp); return (rc); }