summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Buren <braewoods+rb@braewoods.net>2021-07-11 05:14:20 +0000
committerJames Buren <braewoods+rb@braewoods.net>2021-07-11 05:14:20 +0000
commit8846e087c09c28a2dfb731d7c873f113bc899940 (patch)
treec3b9f2196b12b75bad8fede2226f47188f944ea0
parenta1bcca645bbe10f74cb4888b0f22a0f33637c794 (diff)
downloadrockbox-8846e087c0.tar.gz
rockbox-8846e087c0.zip
zip: implement zip extraction support
This adds code sufficient to extract files to available storage given a suitable root directory to extract to. It works on an already open zip handle and also supports chain-loading a secondary callback in the event that integration into the process is desired. Change-Id: Id200d8f20d84a0cbd22906470de8bbd21d4525ef
-rw-r--r--firmware/common/zip.c178
-rw-r--r--firmware/include/zip.h4
2 files changed, 182 insertions, 0 deletions
diff --git a/firmware/common/zip.c b/firmware/common/zip.c
index c2e5da6651..84b94c4fb9 100644
--- a/firmware/common/zip.c
+++ b/firmware/common/zip.c
@@ -22,9 +22,12 @@
#include "zip.h"
#include <string.h>
#include "file.h"
+#include "dir.h"
#include "system.h"
+#include "errno.h"
#include "core_alloc.h"
#include "timefuncs.h"
+#include "pathfuncs.h"
#include "crc32.h"
#include "rbendian.h"
@@ -157,6 +160,16 @@ struct zip_mem {
off_t mem_size;
};
+struct zip_extract {
+ zip_callback cb;
+ void* ctx;
+ size_t name_offset;
+ size_t name_size;
+ char* name;
+ int file;
+ char path[MAX_PATH];
+};
+
static int zip_read_ed(struct zip* z) {
const off_t file_size = z->size(z);
const off_t max_size = sizeof(struct zip_ed_disk) + ZIP_MAX_LENGTH;
@@ -606,6 +619,100 @@ static void zip_mem_init(struct zip_mem* z, int zip_handle, int mem_handle, cons
z->mem_size = mem_size;
}
+static int zip_extract_start(const struct zip_args* args, struct zip_extract* ze) {
+ size_t name_length;
+ const char* dir;
+ size_t dir_length;
+
+ if ((name_length = strlcpy(ze->name, args->name, ze->name_size)) >= ze->name_size)
+ return 5;
+
+ if ((dir_length = path_dirname(ze->name, &dir)) > 0) {
+ char c = ze->name[dir_length];
+
+ ze->name[dir_length] = '\0';
+
+ if (!dir_exists(ze->path)) {
+ const char* path = ze->name;
+ const char* name;
+
+ while (parse_path_component(&path, &name) > 0) {
+ size_t offset = path - ze->name;
+ char c = ze->name[offset];
+
+ ze->name[offset] = '\0';
+
+ if (mkdir(ze->path) < 0 && errno != EEXIST)
+ return 6;
+
+ ze->name[offset] = c;
+ }
+ }
+
+ ze->name[dir_length] = c;
+ }
+
+ if (ze->name[name_length - 1] == PATH_SEPCH) {
+ if (mkdir(ze->path) < 0 && errno != EEXIST)
+ return 7;
+
+ return 0;
+ }
+
+ if ((ze->file = creat(ze->path, 0666)) < 0)
+ return 8;
+
+ return 0;
+}
+
+static int zip_extract_data(const struct zip_args* args, struct zip_extract* ze) {
+ if (write(ze->file, args->block, args->block_size) != (ssize_t) args->block_size) {
+ return 9;
+ }
+
+ return 0;
+}
+
+static int zip_extract_end(const struct zip_args* args, struct zip_extract* ze) {
+ int rv;
+
+ if (ze->file >= 0) {
+ rv = close(ze->file);
+
+ ze->file = -1;
+
+ if (rv < 0)
+ return 10;
+ }
+
+ if (modtime(ze->path, args->mtime) < 0)
+ return 11;
+
+ return 0;
+}
+
+static int zip_extract_callback(const struct zip_args* args, int pass, void* ctx) {
+ struct zip_extract* ze = ctx;
+ int rv;
+
+ if (ze->cb != NULL && (rv = ze->cb(args, pass, ze->ctx)) != 0)
+ return rv;
+
+ switch (pass) {
+ case ZIP_PASS_START:
+ return zip_extract_start(args, ze);
+
+ case ZIP_PASS_DATA:
+ return zip_extract_data(args, ze);
+
+ case ZIP_PASS_END:
+ return zip_extract_end(args, ze);
+
+ default:
+ return 1;
+ }
+}
+
struct zip* zip_open(const char* name, bool try_mem) {
int file = -1;
int mem_handle = -1;
@@ -692,6 +799,77 @@ read_entries:
return zip_read_entries(z);
}
+int zip_extract(struct zip* z, const char* root, zip_callback cb, void* ctx) {
+ int rv;
+ int ze_handle = -1;
+ struct zip_extract* ze;
+ char* path;
+ size_t size;
+ size_t length;
+
+ if (root == NULL || root[0] == '\0')
+ root = PATH_ROOTSTR;
+
+ if (root[0] != PATH_SEPCH) {
+ rv = -1;
+ goto bail;
+ }
+
+ if (!dir_exists(root)) {
+ rv = 1;
+ goto bail;
+ }
+
+ if ((ze_handle = zip_core_alloc(sizeof(struct zip_extract))) < 0) {
+ rv = 2;
+ goto bail;
+ }
+
+ ze = core_get_data(ze_handle);
+ ze->cb = cb;
+ ze->ctx = ctx;
+ ze->file = -1;
+
+ path = ze->path;
+ size = sizeof(ze->path);
+ length = strlcpy(path, root, size);
+
+ if (length >= size) {
+ rv = 3;
+ goto bail;
+ }
+
+ path += length;
+ size -= length;
+
+ if (path[-1] != PATH_SEPCH) {
+ length = strlcpy(path, PATH_SEPSTR, size);
+
+ if (length >= size) {
+ rv = 4;
+ goto bail;
+ }
+
+ path += length;
+ size -= length;
+ }
+
+ ze->name_offset = path - ze->path;
+ ze->name_size = size;
+ ze->name = path;
+
+ rv = zip_read_deep(z, zip_extract_callback, ze);
+
+bail:
+ if (ze_handle >= 0) {
+ if (ze->file >= 0)
+ close(ze->file);
+
+ core_free(ze_handle);
+ }
+ return rv;
+}
+
void zip_close(struct zip* z) {
if (z == NULL)
return;
diff --git a/firmware/include/zip.h b/firmware/include/zip.h
index f6870a53d2..70d225cfd7 100644
--- a/firmware/include/zip.h
+++ b/firmware/include/zip.h
@@ -58,6 +58,10 @@ int zip_read_shallow(struct zip* z, zip_callback cb, void* ctx);
// this can also pickup where a successful shallow read leftoff
int zip_read_deep(struct zip* z, zip_callback cb, void* ctx);
+// extract the contents to an existing directory
+// this can also pickup where a successful shallow read leftoff
+int zip_extract(struct zip* z, const char* root, zip_callback cb, void* ctx);
+
// returns system resources
void zip_close(struct zip* z);