From 6aece33b3f3af6a161e326af36bc894427fcf5ad Mon Sep 17 00:00:00 2001 From: Doug Zongker Date: Mon, 1 Feb 2010 14:40:12 -0800 Subject: [PATCH] add a one-argument version of package_extract_file Add a version of package_extract_file that returns the file data as its return value (to be consumed by some other edify function that expects to receive a bunch of binary data as an argument). Lets us avoid having two copies of a big file in memory (extracting it into /tmp, which is a ramdisk, and then having something load it into memory) when doing things like radio updates. Change-Id: Ie26ece5fbae457eb0ddcd8a13d74d78a769fbc70 --- minzip/Zip.c | 37 ++++++++++++++++++ minzip/Zip.h | 7 ++++ updater/install.c | 95 ++++++++++++++++++++++++++++++++++------------- 3 files changed, 114 insertions(+), 25 deletions(-) diff --git a/minzip/Zip.c b/minzip/Zip.c index 8cdb898..46d2f82 100644 --- a/minzip/Zip.c +++ b/minzip/Zip.c @@ -810,6 +810,43 @@ bool mzExtractZipEntryToFile(const ZipArchive *pArchive, return true; } +typedef struct { + unsigned char* buffer; + long len; +} BufferExtractCookie; + +static bool bufferProcessFunction(const unsigned char *data, int dataLen, + void *cookie) { + BufferExtractCookie *bec = (BufferExtractCookie*)cookie; + + memmove(bec->buffer, data, dataLen); + bec->buffer += dataLen; + bec->len -= dataLen; + + return true; +} + +/* + * Uncompress "pEntry" in "pArchive" to buffer, which must be large + * enough to hold mzGetZipEntryUncomplen(pEntry) bytes. + */ +bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive, + const ZipEntry *pEntry, unsigned char *buffer) +{ + BufferExtractCookie bec; + bec.buffer = buffer; + bec.len = mzGetZipEntryUncompLen(pEntry); + + bool ret = mzProcessZipEntryContents(pArchive, pEntry, + bufferProcessFunction, (void*)&bec); + if (!ret || bec.len != 0) { + LOGE("Can't extract entry to memory buffer.\n"); + return false; + } + return true; +} + + /* Helper state to make path translation easier and less malloc-happy. */ typedef struct { diff --git a/minzip/Zip.h b/minzip/Zip.h index 1c1df2f..9f99fba 100644 --- a/minzip/Zip.h +++ b/minzip/Zip.h @@ -168,6 +168,13 @@ bool mzIsZipEntryIntact(const ZipArchive *pArchive, const ZipEntry *pEntry); bool mzExtractZipEntryToFile(const ZipArchive *pArchive, const ZipEntry *pEntry, int fd); +/* + * Inflate and write an entry to a memory buffer, which must be long + * enough to hold mzGetZipEntryUncomplen(pEntry) bytes. + */ +bool mzExtractZipEntryToBuffer(const ZipArchive *pArchive, + const ZipEntry *pEntry, unsigned char* buffer); + /* * Inflate all entries under zipDir to the directory specified by * targetDir, which must exist and be a writable directory. diff --git a/updater/install.c b/updater/install.c index aa80d75..934acaa 100644 --- a/updater/install.c +++ b/updater/install.c @@ -315,37 +315,82 @@ char* PackageExtractDirFn(const char* name, State* state, // package_extract_file(package_path, destination_path) +// or +// package_extract_file(package_path) +// to return the entire contents of the file as the result of this +// function (the char* returned points to a long giving the length +// followed by that many bytes of data). char* PackageExtractFileFn(const char* name, State* state, int argc, Expr* argv[]) { - if (argc != 2) { - return ErrorAbort(state, "%s() expects 2 args, got %d", name, argc); + if (argc != 1 && argc != 2) { + return ErrorAbort(state, "%s() expects 1 or 2 args, got %d", + name, argc); } - char* zip_path; - char* dest_path; - if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; - bool success = false; + if (argc == 2) { + // The two-argument version extracts to a file. - ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; - const ZipEntry* entry = mzFindZipEntry(za, zip_path); - if (entry == NULL) { - fprintf(stderr, "%s: no %s in package\n", name, zip_path); - goto done; + char* zip_path; + char* dest_path; + if (ReadArgs(state, argv, 2, &zip_path, &dest_path) < 0) return NULL; + + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + const ZipEntry* entry = mzFindZipEntry(za, zip_path); + if (entry == NULL) { + fprintf(stderr, "%s: no %s in package\n", name, zip_path); + goto done2; + } + + FILE* f = fopen(dest_path, "wb"); + if (f == NULL) { + fprintf(stderr, "%s: can't open %s for write: %s\n", + name, dest_path, strerror(errno)); + goto done2; + } + success = mzExtractZipEntryToFile(za, entry, fileno(f)); + fclose(f); + + done2: + free(zip_path); + free(dest_path); + return strdup(success ? "t" : ""); + } else { + // The one-argument version returns the contents of the file + // as the result. + + char* zip_path; + char* buffer = NULL; + + if (ReadArgs(state, argv, 1, &zip_path) < 0) return NULL; + + ZipArchive* za = ((UpdaterInfo*)(state->cookie))->package_zip; + const ZipEntry* entry = mzFindZipEntry(za, zip_path); + if (entry == NULL) { + fprintf(stderr, "%s: no %s in package\n", name, zip_path); + goto done1; + } + + long size = mzGetZipEntryUncompLen(entry); + buffer = malloc(size + sizeof(long)); + if (buffer == NULL) { + fprintf(stderr, "%s: failed to allocate %ld bytes for %s\n", + name, size+sizeof(long), zip_path); + goto done1; + } + + *(long *)buffer = size; + success = mzExtractZipEntryToBuffer( + za, entry, (unsigned char *)(buffer + sizeof(long))); + + done1: + free(zip_path); + if (!success) { + free(buffer); + buffer = malloc(sizeof(long)); + *(long *)buffer = -1L; + } + return buffer; } - - FILE* f = fopen(dest_path, "wb"); - if (f == NULL) { - fprintf(stderr, "%s: can't open %s for write: %s\n", - name, dest_path, strerror(errno)); - goto done; - } - success = mzExtractZipEntryToFile(za, entry, fileno(f)); - fclose(f); - - done: - free(zip_path); - free(dest_path); - return strdup(success ? "t" : ""); }