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
This commit is contained in:
parent
a1bcca645b
commit
8846e087c0
2 changed files with 182 additions and 0 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue