From owner-p4-projects@FreeBSD.ORG Fri May 7 07:56:26 2010 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 2DAA31065675; Fri, 7 May 2010 07:56:26 +0000 (UTC) Delivered-To: perforce@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [69.147.83.52]) by hub.freebsd.org (Postfix) with ESMTP id DD54A1065670 for ; Fri, 7 May 2010 07:56:25 +0000 (UTC) (envelope-from gcooper@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [69.147.83.41]) by mx1.freebsd.org (Postfix) with ESMTP id CA4C18FC0A for ; Fri, 7 May 2010 07:56:25 +0000 (UTC) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.3/8.14.3) with ESMTP id o477uPeA059567 for ; Fri, 7 May 2010 07:56:25 GMT (envelope-from gcooper@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.3/8.14.3/Submit) id o477uPco059565 for perforce@freebsd.org; Fri, 7 May 2010 07:56:25 GMT (envelope-from gcooper@FreeBSD.org) Date: Fri, 7 May 2010 07:56:25 GMT Message-Id: <201005070756.o477uPco059565@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to gcooper@FreeBSD.org using -f From: Garrett Cooper To: Perforce Change Reviews Precedence: bulk Cc: Subject: PERFORCE change 177884 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 07 May 2010 07:56:26 -0000 http://p4web.freebsd.org/@@177884?ac=10 Change 177884 by gcooper@gcooper-bayonetta on 2010/05/07 07:55:38 Commit archive(3) work, again. Affected files ... .. //depot/projects/soc2007/gcooper-pkg_install-enhancements-simplified/lib/libpkg/file.c#2 edit Differences ... ==== //depot/projects/soc2007/gcooper-pkg_install-enhancements-simplified/lib/libpkg/file.c#2 (text+ko) ==== @@ -21,11 +21,15 @@ #include __FBSDID("$FreeBSD: src/lib/libpkg/file.c,v 1.1 2010/04/23 11:07:43 flz Exp $"); -#include "pkg.h" +#include +#include +#include #include +#include +#include +#include #include #include -#include /* Quick check to see if a file exists */ Boolean @@ -334,37 +338,230 @@ } } -/* Unpack a tar file */ +#define EXTRACT_ARCHIVE_FLAGS (ARCHIVE_EXTRACT_OWNER |ARCHIVE_EXTRACT_PERM| \ + ARCHIVE_EXTRACT_TIME |ARCHIVE_EXTRACT_ACL | \ + ARCHIVE_EXTRACT_FFLAGS|ARCHIVE_EXTRACT_XATTR) + +/* + * Unpack a single file from a tar-file to a file descriptor; this is written + * like so as an optimization to abbreviate the extract to *open step, as well + * as to reduce the number of required steps needed when unpacking a tarball on + * disk, as the previous method employed with tar(1) used -q // --fast-read . + * + * Return NULL on failure, and non-NULL on success + * + * XXX (gcooper): this is currently implemented with FILE* / fopen(3) due to + * legacy issues that need to be sorted out over the next couple of weeks for + * 1) locking to function properly, and to gain the potential performance boost + * by using mmap(2), and read(2) (ugh). + * + * But first things first, we need a working solution with minimal changes; + * then we move on from there. + * + * [The return code info will eventually be...] + * + * Return -1 on failure, a value greater than 0 on success [in accordance with + * open(2)]. + */ +FILE* +unpack_file_to_fd(const char *pkg, const char *file) +{ + struct archive *archive; + struct archive_entry *archive_entry; + Boolean found_match = FALSE; + + const char *entry_pathname = NULL; + const char *error = NULL; + const char *pkg_name_humanized; + + FILE *fd = NULL; + /* int fd = -1; */ + int archive_fd = -1, r; + + if (pkg == NULL || strcmp(pkg, "-") == 0) + pkg_name_humanized = "(stdin)"; + else + pkg_name_humanized = pkg; + + if (Verbose) + printf("%s: will extract %s from %s\n", + __func__, file, pkg_name_humanized); + + archive = archive_read_new(); + archive_read_support_compression_all(archive); + archive_read_support_format_tar(archive); + + /* + * Avoid potential race conditions with archive_read_open_filename(3), + * by opening the file beforehand. + */ + if (pkg == NULL) + archive_fd = fileno(stdin); + else + archive_fd = open(pkg, O_RDONLY); + + /* The initial open failed */ + if (archive_fd == -1) + warn("%s: unable to open the package from %s", + __func__, pkg_name_humanized); + /* archive(3) failed to open the file. */ + else if (archive_read_open_fd(archive, archive_fd, + ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) { + + error = archive_error_string(archive); + warnx("%s: unable to open the package from %s: %s", + __func__, pkg_name_humanized, error); + + } + else + while (error == NULL && found_match == FALSE && + (r = archive_read_next_header(archive, &archive_entry)) == + ARCHIVE_OK) { + + entry_pathname = archive_entry_pathname(archive_entry); + + if (strncmp(file, entry_pathname, PATH_MAX) == 0) { + + /* + * Regardless of whether or not extract passes, + * we found our target file so let's exit + * quickly because the underlying issue is most + * likely unrecoverable. + */ + found_match = TRUE; + + r = archive_read_extract(archive, archive_entry, + EXTRACT_ARCHIVE_FLAGS); + if (r == ARCHIVE_OK) { + if (Verbose) + printf("X - %s\n", + entry_pathname); + fd = fopen(entry_pathname, "r"); + } else { + error = archive_error_string(archive); + warnx("%s: extraction for %s failed: " + "%s", __func__, pkg_name_humanized, + error); + } + + } else + if (Verbose) + printf("S - %s\n", entry_pathname); + + } + + archive_read_finish(archive); + + /* Close any open descriptors. */ + if (0 <= archive_fd) + close(archive_fd); + + return fd; + +} + +/* + * Unpack a tar file, or a subset of the contents. + * + * Return 0 on success, 1 on failure + * + * NOTE: the exit code is 0 / 1 so that this can be fed directly into exit + * when doing piped tar commands for copying hierarchies *hint*, *hint*. + */ int -unpack(const char *pkg, const char *flist) +unpack(const char *pkg, const char *file_expr) { - const char *comp, *cp; - char suff[80]; + struct archive *archive; + struct archive_entry *archive_entry; + Boolean extract_whole_archive = FALSE; + const char *entry_pathname = NULL; + const char *error = NULL; + const char *pkg_name_humanized; + int archive_fd = -1, r, serrno; + + if (file_expr == NULL || strcmp("*", file_expr) == 0) + extract_whole_archive = TRUE; + + if (pkg == NULL || strcmp(pkg, "-") == 0) + pkg_name_humanized = "(stdin)"; + else + pkg_name_humanized = pkg; - comp = ""; - /* - * Figure out by a crude heuristic whether this or not this is probably - * compressed and whichever compression utility was used (gzip or bzip2). - */ - if (strcmp(pkg, "-")) { - cp = strrchr(pkg, '.'); - if (cp) { - strcpy(suff, cp + 1); - if (strchr(suff, 'z') || strchr(suff, 'Z')) { - if (strchr(suff, 'b')) - comp = "-j"; + if (Verbose) { + if (extract_whole_archive) + printf("%s: %s - will extract whole archive\n", + pkg_name_humanized, __func__); else - comp = "-z"; - } + printf("%s: %s - will extract files that match " + "expression: %s\n", + pkg_name_humanized, __func__, file_expr); + } + + archive = archive_read_new(); + archive_read_support_compression_all(archive); + archive_read_support_format_tar(archive); + + /* + * Avoid potential race conditions with archive_read_open_filename(3), + * by opening the file beforehand. + */ + if (pkg == NULL) + archive_fd = fileno(stdin); + else + archive_fd = open(pkg, O_RDONLY); + + /* The initial open failed */ + if (archive_fd == -1) + warn("%s: unable to open the package from %s", + __func__, pkg_name_humanized); + else if (archive_read_open_fd(archive, archive_fd, + ARCHIVE_DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) { + + error = archive_error_string(archive); + warnx("%s: unable to open the package from %s: %s", + __func__, pkg_name_humanized, error); + } - } - else - comp = "-j"; - if (vsystem("/usr/bin/tar -xp %s -f '%s' %s", comp, pkg, flist ? flist : "")) { - warnx("tar extract of %s failed!", pkg); - return 1; - } - return 0; + else + while (error == NULL && + (r = archive_read_next_header(archive, &archive_entry)) == + ARCHIVE_OK) { + + entry_pathname = archive_entry_pathname(archive_entry); + + /* Let's extract the whole archive, or just a file. */ + if (extract_whole_archive == TRUE || + (fnmatch(file_expr, entry_pathname, + FNM_PATHNAME)) == 0) { + + r = archive_read_extract(archive, archive_entry, + EXTRACT_ARCHIVE_FLAGS); + if (r == ARCHIVE_OK) { + if (Verbose) + printf("X - %s\n", + entry_pathname); + } else { + error = archive_error_string(archive); + warnx("%s: extraction for %s failed: " + "%s", __func__, pkg_name_humanized, + error); + } + + } else + if (Verbose) + printf("S - %s\n", entry_pathname); + + } + + serrno = errno; + archive_read_finish(archive); + + /* Close any open descriptors. */ + if (0 <= archive_fd) + close(archive_fd); + + return (error == NULL && errno == 0 ? 0 : 1); + } /*