diff options
Diffstat (limited to 'src/package.c')
-rw-r--r-- | src/package.c | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/src/package.c b/src/package.c new file mode 100644 index 0000000..168a6d8 --- /dev/null +++ b/src/package.c @@ -0,0 +1,310 @@ +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include "package.h" + +struct package_item; + +struct package_item { + char *name; + char *path; + struct package_item *next; +}; + +struct package { + char *base; + int count; + struct package_item *items; + struct package_item *last; +}; + +static int copy(FILE *f, FILE *t, uint32_t len) { + static char buf[2048*4]; + + while (len > 0) { + uint32_t n; + + if (feof(f)) + return -1; + + n = len > sizeof(buf) ? sizeof(buf) : len; + + if (fread(buf, n, 1, f) != 1) + return -1; + + if (fwrite(buf, n, 1, t) != 1) + return -1; + + len -= n; + } + + return 0; +} + +static char *tmp(char *fn, int l) { + char *t; + + if (!(t = getenv("TMPDIR"))) + if (!(t = getenv("TEMP"))) + if (!(t = getenv("TMP"))) + t = "/tmp"; + + snprintf(fn, l, "%s/pkgXXXXXX", t); + return fn; +} + +static struct package_item *item_new(const char *name, const char *path) { + struct package_item *i = NULL; + + if (!(i = malloc(sizeof(struct package_item)))) + return NULL; + + memset(i, 0, sizeof(struct package_item)); + + if (!(i->name = strdup(name))) + goto fail; + + if (!(i->path = strdup(path))) + goto fail; + + return i; + +fail: + if(i) { + if (i->path) + free(i->path); + if (i->name) + free(i->name); + free(i); + } + + return NULL; +} + +struct package* package_open(const char *fn) { + struct package *p; + FILE *f = NULL; + char path[PATH_MAX]; + + if (!(p = malloc(sizeof(struct package)))) { + fprintf(stderr, "Failed to allocate package structure.\n"); + return NULL; + } + + memset(p, 0, sizeof(struct package)); + + tmp(path, sizeof(path)); + if (!mkdtemp(path)) { + fprintf(stderr, "Failed to create temporary directory.\n"); + goto finish; + } + + if (!(p->base = strdup(path))) { + fprintf(stderr, "Failed to allocate memory.\n"); + goto finish; + } + + if (fn) { + if (!(f = fopen(fn, "r"))) { + fprintf(stderr, "Failed to open <%s>: %s\n", fn, strerror(errno)); + goto finish; + } + + for (;;) { + uint32_t len; + char name[PACKAGE_ITEM_NAME_LEN+1]; + FILE *d = NULL; + struct package_item *pi; + + if (!fread(name, sizeof(name)-1, 1, f)) + break; + + name[sizeof(name)-1] = 0; + + if (fread(&len, sizeof(len), 1, f) != 1) { + fprintf(stderr, "Short read while reading length: %s\n", strerror(errno)); + goto finish; + } + + snprintf(path, sizeof(path), "%s/%i", p->base, p->count++); + if (!(d = fopen(path, "w+"))) { + fprintf(stderr, "Failed to open target file: %s\n", strerror(errno)); + goto finish; + } + + if (copy(f, d, len) < 0) { + fprintf(stderr, "Copy failed: %s\n", strerror(errno)); + fclose(d); + unlink(path); + goto finish; + } + + fclose(d); + if (!(pi = item_new(name, path))) { + unlink(path); + fprintf(stderr, "Failed to allocate memory.\n"); + goto finish; + } + + assert(!!p->last == !!p->items); + + if (p->last) { + p->last->next = pi; + p->last = pi; + } else + p->items = p->last = pi; + } + + fclose(f); + } + + return p; + +finish: + if (f) + fclose(f); + + if (p) + package_remove(p); + + return NULL; +} + +int package_save(struct package *p, const char *fn) { + FILE *f, *s = NULL; + int r = 0; + struct package_item *i; + + assert(p && fn); + + if (!(f = fopen(fn, "w+"))) + return -1; + + for (i = p->items; i; i = i->next) { + char name[PACKAGE_ITEM_NAME_LEN+1]; + uint32_t len; + struct stat st; + + if (!i->name || !i->path) + continue; + + if (!(s = fopen(i->path, "r"))) + continue; + + //fprintf(stderr, "Saving %s (%s)...\n", i->name, i->path); + + memset(name, 0, sizeof(name)); + strncpy(name, i->name, sizeof(name)-1); + + if (fwrite(name, sizeof(name)-1, 1, f) != 1) { + r = 1; + break; + } + + if (fstat(fileno(s), &st) < 0) { + fprintf(stderr, "Could not get file size: %s\n", strerror(errno)); + r = 1; + break; + } + + len = st.st_size; + + if (fwrite(&len, sizeof(len), 1, f) != 1) { + r = 1; + break; + } + + if (copy(s, f, len) < 0) { + fprintf(stderr, "Could not copy file to package: %s\n", strerror(errno)); + r = 1; + break; + } + + fclose(s); + s = NULL; + } + + if (s) + fclose(s); + + fclose(f); + + if (r != 0) + unlink(fn); + + return r; +} + +const char *package_get_item(struct package* p, const char *name) { + struct package_item *i; + char path[PATH_MAX]; + assert(p && name); + + for (i = p->items; i; i = i->next) { + if (strncmp(name, i->name, PACKAGE_ITEM_NAME_LEN) == 0) + return i->path; + } + + snprintf(path, sizeof(path), "%s/%i", p->base, p->count++); + + if (!(i = item_new(name, path))) { + unlink(path); + return NULL; + } + + assert(!!p->last == !!p->items); + + if (p->last) { + p->last->next = i; + p->last = i; + } else + p->items = p->last = i; + + return i->path; +} + +void package_remove(struct package *p) { + struct package_item *i, *n; + assert(p); + + for (i = p->items; i; i = n) { + n = i->next; + + if (i->path) + if (unlink(i->path)) + if (errno != ENOENT) + fprintf(stderr, "Failed to remove <%s>: %s\n", i->path, strerror(errno)); + + free(i->path); + free(i->name); + free(i); + } + + if (p->base) { + if (rmdir(p->base)) + fprintf(stderr, "Failed to remove <%s>: %s\n", p->base, strerror(errno)); + + free(p->base); + } + free(p); +} + + +int package_foreach(struct package *p, int (*cb) (struct package *p, char *name, char *path, void *u), void *u) { + struct package_item *i; + assert(p); + + for (i = p->items; i; i = i->next) + if (cb(p, i->name, i->path, u) < 0) + return -1; + + return 0; +} |