#include #include #include #include #include #include #include #include #include #include #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, const char *name, const 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; }