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
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 @@
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
@@ -169,6 +169,13 @@
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 @@
// 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" : "");
}