Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 7 May 2010 07:56:25 GMT
From:      Garrett Cooper <gcooper@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 177884 for review
Message-ID:  <201005070756.o477uPco059565@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
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 <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);
+
 }
 
 /*



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201005070756.o477uPco059565>