From 8846e087c09c28a2dfb731d7c873f113bc899940 Mon Sep 17 00:00:00 2001 From: James Buren Date: Sun, 11 Jul 2021 05:14:20 +0000 Subject: [PATCH] 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 --- firmware/common/zip.c | 178 +++++++++++++++++++++++++++++++++++++++++ firmware/include/zip.h | 4 + 2 files changed, 182 insertions(+) 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 #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);