PERFORCE change 177884 for review
Garrett Cooper
gcooper at FreeBSD.org
Fri May 7 07:56:26 UTC 2010
http://p4web.freebsd.org/@@177884?ac=10
Change 177884 by gcooper at 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 <sys/cdefs.h>
__FBSDID("$FreeBSD: src/lib/libpkg/file.c,v 1.1 2010/04/23 11:07:43 flz Exp $");
-#include "pkg.h"
+#include <sys/wait.h>
+#include <archive.h>
+#include <archive_entry.h>
#include <err.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <pkg.h>
#include <pwd.h>
#include <time.h>
-#include <sys/wait.h>
/* 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);
+
}
/*
More information about the p4-projects
mailing list