From owner-p4-projects@FreeBSD.ORG Sat Jul 24 20:51:16 2010 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id C87BF1065675; Sat, 24 Jul 2010 20:51:15 +0000 (UTC) Delivered-To: perforce@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 8CC801065672 for ; Sat, 24 Jul 2010 20:51:15 +0000 (UTC) (envelope-from jlaffaye@FreeBSD.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id 859188FC17 for ; Sat, 24 Jul 2010 20:51:15 +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 o6OKpFvY030161 for ; Sat, 24 Jul 2010 20:51:15 GMT (envelope-from jlaffaye@FreeBSD.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.3/8.14.3/Submit) id o6OKpF5t030159 for perforce@freebsd.org; Sat, 24 Jul 2010 20:51:15 GMT (envelope-from jlaffaye@FreeBSD.org) Date: Sat, 24 Jul 2010 20:51:15 GMT Message-Id: <201007242051.o6OKpF5t030159@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to jlaffaye@FreeBSD.org using -f From: Julien Laffaye To: Perforce Change Reviews Precedence: bulk Cc: Subject: PERFORCE change 181439 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: Sat, 24 Jul 2010 20:51:16 -0000 http://p4web.freebsd.org/@@181439?ac=10 Change 181439 by jlaffaye@jlaffaye-chulak on 2010/07/24 20:51:13 Add support for fetching packages. Read them on the fly thanks to libarchive(3) callbacks. Fix segfault by not plist_free'ing an unitialised plist. Affected files ... .. //depot/projects/soc2010/pkg_complete/lib/libpkg/pkg.h#7 edit .. //depot/projects/soc2010/pkg_complete/lib/libpkg/url.c#3 edit .. //depot/projects/soc2010/pkg_complete/usr.sbin/pkg_install/add/extract.c#6 edit .. //depot/projects/soc2010/pkg_complete/usr.sbin/pkg_install/add/perform.c#6 edit Differences ... ==== //depot/projects/soc2010/pkg_complete/lib/libpkg/pkg.h#7 (text+ko) ==== @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -144,6 +145,12 @@ }; STAILQ_HEAD(reqr_by_head, reqr_by_entry); +struct fetch_data { + FILE *ftp; + int pkgfd; + char buf[8192]; +}; + /* Prototypes */ /* Misc */ int vsystem(const char *, ...); @@ -173,6 +180,8 @@ Boolean issymlink(const char *); Boolean isURL(const char *); const char *fileGetURL(const char *, const char *, int); +int fetch_archive(struct archive *, const char *, const char *, + Boolean); char *fileFindByPath(const char *, const char *); char *fileGetContents(const char *); ssize_t write_file(const char *, const char *); ==== //depot/projects/soc2010/pkg_complete/lib/libpkg/url.c#3 (text+ko) ==== @@ -23,15 +23,23 @@ #include #include +#include #include +#include #include #include #include /* NOTE: stdio must come before fetch. */ #include "pkg.h" +static ssize_t archive_read_cb(struct archive *, void *, const void **); +static int archive_open_cb(struct archive *a, void *); +static int archive_close_cb(struct archive *, void *); + /* * Try and fetch a file by URL, returning the directory name for where * it's unpacked, if successful. + * XXX (jlaffaye): to be removed when all call to fileGetURL() are converted to + * fetch_archive() */ const char * fileGetURL(const char *base, const char *spec, int keep_package) @@ -113,11 +121,11 @@ fetchDebug = (Verbose > 0); if ((ftp = fetchGetURL(fname, Verbose ? "v" : NULL)) == NULL) { warnx("Error: Unable to get %s: %s\n", fname, - fetchLastErrString); + fetchLastErrString); /* If the fetch fails, yank the package. */ if (keep_package && unlink(pkg) < 0) { warnx("failed to remove partially fetched package: %s", - pkg); + pkg); } return (NULL); } @@ -182,3 +190,170 @@ return (rp); } + +/* + * Setup the archive `a' callbacks to read data from an URL `spec' via fetch(3). + * If `spec' is not an URL, the function try to find the location of the file + * via `base' or via the environment variable `PKG_ADD_BASE'. + * Returns 0 on success, 1 otherwise. + */ +int +fetch_archive(struct archive *a, const char *base, const char *spec, + Boolean keep_package) +{ + struct fetch_data *data = NULL; + char *cp, *hint, *tmp; + char fname[FILENAME_MAX]; + char pkg[FILENAME_MAX]; + int retcode = 0; + + if ((data = malloc(sizeof(struct fetch_data))) == NULL) + err(EXIT_FAILURE, "malloc()"); + + if (!isURL(spec)) { + /* + * We've been given an existing URL (that's known-good) and now + * we need to construct a composite one out of that and the + * basename we were handed as a dependency. + */ + if (base != NULL) { + strcpy(fname, base); + /* + * Advance back two slashes to get to the root of the + * package hierarchy + */ + cp = strrchr(fname, '/'); + if (cp) { + *cp = '\0'; /* chop name */ + cp = strrchr(fname, '/'); + } + if (cp != NULL) { + *(cp + 1) = '\0'; + strcat(cp, "All/"); + strcat(cp, spec); + strcat(cp, ".tbz"); + } else { + retcode = 1; + goto cleanup; + } + } + /* Special tip that sysinstall left for us */ + else if ((hint = getenv("PKG_ADD_BASE")) != NULL) { + /* + * Otherwise, we've been given an environment variable + * hinting at the right location from sysinstall + */ + strcpy(fname, hint); + strcat(fname, spec); + strcat(fname, ".tbz"); + } + /* We dont have an url and are unable to guess one */ + else { + retcode = 1; + goto cleanup; + } + } + else + strcpy(fname, spec); + + if (keep_package) { + tmp = getenv("PKGDIR"); + strlcpy(pkg, tmp ? tmp : ".", sizeof(pkg)); + tmp = basename(fname); + strlcat(pkg, "/", sizeof(pkg)); + strlcat(pkg, tmp, sizeof(pkg)); + + data->pkgfd = open(pkg, O_WRONLY|O_CREAT|O_TRUNC, 0644); + if (data->pkgfd == -1) { + warn("Error: Unable to open %s", pkg); + retcode = 1; + goto cleanup; + } + } else + data->pkgfd = 0; + + fetchDebug = (Verbose > 0); + if ((data->ftp = fetchGetURL(fname, Verbose ? "v" : NULL)) == NULL) { + warnx("Error: Unable to get %s: %s\n", fname, fetchLastErrString); + /* If the fetch fails, yank the package. */ + if (keep_package && unlink(pkg) < 0) + warnx("failed to remove partially fetched package: %s", pkg); + retcode = 1; + goto cleanup; + } + + if (isatty(0) || Verbose) { + printf("Fetching %s...", fname); + fflush(stdout); + } + + if (archive_read_open(a, data, archive_open_cb, archive_read_cb, + archive_close_cb) != ARCHIVE_OK) { + warnx("Can not open '%s': %s", pkg, archive_error_string(a)); + retcode = 1; + goto cleanup; + } + + cleanup: + if (retcode == 1 && data != NULL) + free(data); + + return (retcode); +} + +/* + * Libarchive callback called when more data is needed. + * Read the data from the fetch(3) file descriptor and store it into buf. + * If `pkgfd' is a valid file descriptor, also write the data on disk. + * Returns the read size, 0 on EOF, -1 on error. + */ +static ssize_t +archive_read_cb(struct archive *a, void *client_data, const void **buf) +{ + ssize_t r; + struct fetch_data *data = client_data; + + *buf = data->buf; + if ((r = fread(data->buf, 1, sizeof(data->buf), data->ftp)) < 1) + if (ferror(data->ftp)) { + archive_set_error(a, 0, "error while fetching : %s", + fetchLastErrString); + return (-1); + } + + if (data->pkgfd > 0 && r > 0) + if (write(data->pkgfd, buf, r) != r) { + archive_set_error(a, 0, "can not write to package file: %s", + strerror(errno)); + return (-1); + } + + return (r); +} + +/* + * Libarchive callback called by archive_open() + * Since all the job is done in fetch_archive(), always return success. + */ +static int +archive_open_cb(struct archive *a, void *client_data) +{ + return (ARCHIVE_OK); +} + +/* + * Libarchive callback called by archive_close(). + * Release the file descriptors and free the structure. + */ +static int +archive_close_cb(struct archive *a, void *client_data) +{ + struct fetch_data *data = client_data; + + fclose(data->ftp); + if (data->pkgfd > 0) + close(data->pkgfd); + free(data); + + return (ARCHIVE_OK); +} ==== //depot/projects/soc2010/pkg_complete/usr.sbin/pkg_install/add/extract.c#6 (text+ko) ==== @@ -43,7 +43,8 @@ if ((plist_buf = malloc(s+1)) == NULL) err(EXIT_FAILURE, "malloc()"); if (archive_read_data(a, plist_buf, s) != s) { - warnx("Can not extract plist: %s", archive_error_string(a)); + warnx("Can not extract %s: %s", CONTENTS_FNAME, + archive_error_string(a)); return (1); } plist_buf[s] = '\0'; @@ -52,7 +53,7 @@ retcode = read_plist_from_buffer(pkg, plist_buf, s); free(plist_buf); if (retcode != 0) { - warnx("Unable to parse plist!"); + warnx("Unable to parse %s!", CONTENTS_FNAME); return (1); } ==== //depot/projects/soc2010/pkg_complete/usr.sbin/pkg_install/add/perform.c#6 (text+ko) ==== @@ -73,20 +73,23 @@ /* * TODO: - * dowload the package if it is an URL, read from stdin if "-" * Deal with master/slave modes. * add support for complete packages */ if (isURL(fname)) { - /* TODO: add support */ - return (1); + if (fetch_archive(a, NULL, fname, KeepPackage) != 0) { + warnx("Can not fetch '%s' - aborting", fname); + retcode = 1; + goto cleanup; + } } else { if (strcmp(fname, "-") == 0) fd = fileno(stdin); else { if ((fd = open(fname, O_RDONLY)) == -1) { - warn("open(%s)", fname); - return (1); + warn("Can not open '%s' for reading", fname); + retcode = 1; + goto cleanup; } } @@ -102,11 +105,12 @@ pathname = archive_entry_pathname(entry); if (strcmp(pathname, CONTENTS_FNAME) == 0) { if (extract_plist(a, entry, &pkg) != 0) { - warnx("Can not extract & parse " CONTENTS_FNAME); + warnx("Can not proceed without packing list - aborting"); retcode = 1; goto cleanup; } - extract_package(a, &pkg); + retcode = extract_package(a, &pkg); + free_plist(&pkg); } else if (strcmp(pathname, "+PKG_COMPLETE") == 0) { if (Verbose) printf("'%s' is a complete package...\n", fname); @@ -126,8 +130,6 @@ cleanup: if (a != NULL) archive_read_finish(a); - if (pkg.head != NULL) - free_plist(&pkg); return (retcode); # if 0