#include #include #include #include #include #include #include #include #include "zpm.h" #include "sha256.h" #if 0 struct zpm { sqlite3 *db; char *path; /* path to package file */ char *version; int release; char *pkgname; time_t installed; /* install time, 0 for not installed */ }; struct zpm_file { char *path; int mode; uint32_t filetype; char *tags; char *owner; char *group; char *hash; /* could be fixed length */ time_t mtime; struct zpm_file *next; /* so you can make a linked list */ }; /* NULL? Create? */ /* associate with a package ? if only one? first? */ int zpm_open(struct zpm *pkg, char *path); int zpm_pkgname(char *base, char *version, int release); /* construct a package file name */ /* flags for preserving mode, owner, etc */ /* puts hash of import in hash */ /* path can be a hash, with an "INTERNAL" flag, i.e. internally import */ #define ZPM_MODE 0x1 #define ZPM_OWNER 0x2 #define ZPM_MTIME 0x4 #define ZPM_INTERNAL 0x8 #define ZPM_NOBLOB 0x10 /* don't run scripts on install */ #define ZPM_NOSCRIPTS 0x10 /* don't associate the file with a package, just do a raw insert */ /* otherwise, associate it with the current package */ #define ZPM_NOPACKAGE 0x20 int zpm_import(struct zpm *zp, char *path, uint32_t flags, uint8_t *hash); /* link and unlink hashes to packages */ int zpm_link(struct zpm *pkg, char *path, char *hash, struct zpm_file *fileinfo); int zpm_unlink(struct zpm *pkg, char *path); /* tag a file. relative to "current package" */ int zpm_tag(struct zpm *zp, char *path, char *tags); /* should this be broken up into separage functions ? */ int zpm_md(struct zpm *zp, char *path, int mode, char *owner, char *group, time_t mtime); /* export hash to dest */ int zpm_extract(struct zpm *pkg, char *hash, char *path, int mode); /* export path to dest */ int zpm_export(struct zpm *zp, char *path, uint32_t flags, char *dest); int zpm_close(struct zpm *zp); /* attach a signature to a package */ int zpm_sign(struct zpm *z, size_t s, void *signature); /* set the package info to the nth package, -1 to return count? */ /* further import/exports and such will be relative to this package */ int zpm_package(struct zpm *zp, int n); /* get file information */ int zpm_stat(struct zpm *z, struct zpm_file *f, int n); /* will also set the package context to the new package */ int zpm_newpkg(struct zpm *z, char *base, char *version, int release); /* transactions */ int zpm_begin(struct zpm *z); int zpm_commit(struct zpm *z); int zpm_rollback(struct zpm *z); /* higher level operations */ /* install or uninstall the package */ /* flag for keeping the blobs in local */ /* what about tag filtering */ int zpm_install(struct zpm *z, struct zpm *local, uint32_t flags); int zpm_uninstall(struct zpm *local); /* slurp up the actual blobs */ /* what about versioning them if they change */ int zpm_preserve(struct zpm *local); /* check file integrity */ int zpm_checkinstall(struct zpm *local); int zpm_merge(struct zpm *z, struct zpm *src, uint32_t flags); void uncompresslzma(void *buf, size_t bufsize, FILE *out); #define SQLERROR(x) fprintf(stderr, "%s %d: %s\n", __func__, __LINE__, (x)) #endif static char *dupstr(char *s) { size_t n; char *d; n = strlen(s); d = malloc(n+1); if (d) { d = strcpy(d, s); } return d; } #if 0 int zpm_newpkg(struct zpm *z, char *base, char *version, int release) { char *sql = "insert or ignore into packages (package,version,release) values (?,?,?)"; int rc; sqlite3_stmt *ifile; rc = sqlite3_prepare(db, sql, -1, &ifile,0); if (rc != SQLITE_OK) { SQLERROR(sqlite3_errmsg(db)); return 0; } rc = sqlite3_bind_text(ifile, 1, base, strlen(base), SQLITE_STATIC); if (rc != SQLITE_OK) { SQLERROR(sqlite3_errmsg(db)); fprintf(stderr, "cant bind package name\n"); zpm_rollback(pkg); return 0; } sqlite3_bind_text(ifile, 2, version, strlen(version), SQLITE_STATIC); sqlite3_bind_int(ifile, 3, release) rc = sqlite3_step(ifile); if (rc != SQLITE_DONE) { SQLERROR(sqlite3_errmsg(db)); sqlite3_finalize(ifile); return 0; } sqlite3_finalize(ifile); z->pkg = dupstr(base); z->version = dupstr(version); z->release = release; } #endif int zpm_begin(struct zpm *z) { char *errstr = 0; sqlite3_exec(z->db, "begin;", NULL, NULL, &errstr); if (errstr) { fprintf(stderr, "sqlite begin error: %s\n", errstr); sqlite3_free(errstr); return 0; } return 1; } int zpm_commit(struct zpm *z) { char *errstr = 0; sqlite3_exec(z->db, "commit;", NULL, NULL, &errstr); if (errstr) { fprintf(stderr, "sqlite commit error: %s\n", errstr); sqlite3_free(errstr); return 0; } return 1; } int zpm_rollback(struct zpm *z) { char *errstr = 0; sqlite3_exec(z->db, "rollback;", NULL, NULL, &errstr); if (errstr) { fprintf(stderr, "sqlite rollback error: %s\n", errstr); sqlite3_free(errstr); return 0; } return 1; } int zpm_db_set_pragma(struct zpm *db, int pragma, int value) { int rc; char *sql; sqlite3_stmt *s; switch (pragma) { case 1: sql = "pragma application_id = ?;"; break; case 2: sql = "pragma user_version = ?;"; break; default: return -1; break; } rc = sqlite3_prepare_v2(db->db, sql, -1, &s, 0); if (rc != SQLITE_OK) { SQLERROR(sqlite3_errmsg(db->db)); return -1; } sqlite3_bind_int(s, 1, value); if (rc != SQLITE_OK) { SQLERROR(sqlite3_errmsg(db->db)); fprintf(stderr, "cant bind pragma value\n"); sqlite3_finalize(s); return -1; } rc = sqlite3_step(s); if (rc != SQLITE_DONE) { SQLERROR(sqlite3_errmsg(db->db)); fprintf(stderr, "cant set pragma\n"); sqlite3_finalize(s); return -1; } sqlite3_finalize(s); return value; } int zpm_db_pragma(struct zpm *db, int pragma) { int rc; int value = -1; char *sql = 0; sqlite3_stmt *s; switch (pragma) { case 1: sql = "pragma application_id;"; break; case 2: sql = "pragma user_version;"; break; default: return -1; break; } rc = sqlite3_prepare_v2(db->db, sql, -1, &s, 0); if (rc != SQLITE_OK) { SQLERROR(sqlite3_errmsg(db->db)); fprintf(stderr, "%s, errnum = %d\n", sqlite3_errmsg(db->db), rc); /* TODO just abort? */ return -1; } rc = sqlite3_step(s); if (rc == SQLITE_ROW) { value = sqlite3_column_int(s, 0); } sqlite3_finalize(s); return value; } static #include "newdb.c" int zpm_db_initialize(struct zpm *pkg) { fprintf(stderr, "initializing zpm database\n"); switch (sqlite3_exec(pkg->db, createdb, (int (*)(void *,int,char **,char **))0, NULL, NULL)) { case SQLITE_OK: break; default: SQLERROR(sqlite3_errmsg(pkg->db)); return 0; break; } return 1; } /* NULL? Create? */ int zpm_open(struct zpm *pkg, char *path) { int rc; char *errstr = 0; sqlite3 *db = 0; int appid, dbver; pkg->db = 0; pkg->path = 0; pkg->version = 0; pkg->release = 0; pkg->pkgname = 0; pkg->installed = 0; rc = sqlite3_open(path, &db); if (rc) { SQLERROR(sqlite3_errmsg(db)); sqlite3_close(db); return 0; } pkg->db = db; pkg->path = dupstr(path); appid = zpm_db_pragma(pkg, 1); dbver = zpm_db_pragma(pkg, 2); fprintf(stderr, "db appid = %x, dbver = %d\n", appid, dbver); switch (appid) { case 0: if (!zpm_db_initialize(pkg)) { sqlite3_close(db); return 1; }; break; case 0x5a504442: break; default: fprintf(stderr, "unknown database type\n"); sqlite3_close(db); return 0; break; } if (dbver > 1) { fprintf(stderr, "version %d zpm db detected, this program only works with version 1 databases\n", dbver); sqlite3_close(db); return 0; } sqlite3_exec(pkg->db, "pragma foreign_keys = ON;", NULL, NULL, &errstr); if (errstr) { fprintf(stderr, "sqlite foreign key error: %s\n", errstr); sqlite3_free(errstr); sqlite3_close(db); return 0; } /* TODO if this is a new database, create structures */ /* get a package. what if more than one? what if none? */ return 1; } int zpm_close(struct zpm *pkg) { if (pkg) { sqlite3_close(pkg->db); free(pkg->path); } /* TODO free any malloced names and such */ return 1; } int zpm_extract(struct zpm *pkg, char *hash, char *path, int mode) { int rc; int blobsize; int64_t size; void *xzdata; int type; FILE *out; sqlite3_stmt *ifile; /* TODO check null */ sqlite3 *db = pkg->db; rc = sqlite3_prepare(db, "select size, content from files where hash = ?", -1, &ifile,0); if (rc != SQLITE_OK) { SQLERROR(sqlite3_errmsg(db)); return 0; } /* hash, filename */ sqlite3_bind_text(ifile, 1, hash, 64, SQLITE_STATIC); rc = sqlite3_step(ifile); if (rc == SQLITE_DONE) { /* didn't find a row */ sqlite3_finalize(ifile); sqlite3_close(db); fprintf(stderr, "no such hash\n"); return 0; } /* either way we're done with this now */ if (rc != SQLITE_ROW) { SQLERROR(sqlite3_errmsg(db)); sqlite3_finalize(ifile); sqlite3_close(db); return 0; } type = sqlite3_column_type(ifile, 0); if (type == SQLITE_NULL) { fprintf(stderr, "no file size\n"); sqlite3_finalize(ifile); sqlite3_close(db); return 0; } type = sqlite3_column_type(ifile, 1); if (type == SQLITE_NULL) { fprintf(stderr, "no file data\n"); sqlite3_finalize(ifile); sqlite3_close(db); return 0; } size = sqlite3_column_int64(ifile, 0); xzdata = (void *)sqlite3_column_blob(ifile, 1); blobsize = sqlite3_column_bytes(ifile, 1); out = fopen(path, "w"); if (!out) { fprintf(stderr, "can't open output file %s\n", path); sqlite3_finalize(ifile); sqlite3_close(db); return 5; } //fwrite(xzdata, blobsize, 1, stdout); fprintf(stderr, "uncompressing %d bytes at %p, expect %lld\n", blobsize, xzdata, (long long int)size); uncompresslzma(xzdata, blobsize, out); fclose(out); sqlite3_finalize(ifile); return 0; } #if 1 int zpm_import(struct zpm *pkg, char *path, uint32_t flags, char *hash) { int fd; void *content; struct stat sbuf; unsigned char tmp[32]; hash_state md; sqlite3_stmt *ifile; int haverow = 0,havedata = 0; int j,rc,type; char hashbuf[65]; /* xz compress it */ size_t outlen = 0; void *outbuf; if (!pkg || !pkg->db || !path) { return 0; } /* use local if caller didn't pass in room */ if (!hash) { hash = hashbuf; } /* mmap the file */ fd = open(path, O_RDONLY); if (fd == -1) { return 0; } if (fstat(fd, &sbuf) == -1) { return 0; } /* not a regular file? */ if (!S_ISREG(sbuf.st_mode)) { /* TODO this is ok, just stored differently */ return 0; } content = mmap(0, sbuf.st_size, PROT_READ,MAP_PRIVATE, fd, 0); if (!content) { return 0; } /* get hash */ sha256_init(&md); sha256_process(&md, content, sbuf.st_size); sha256_done(&md, tmp); for (j=0;j<32;j++) { sprintf(hash+j*2, "%02x", (unsigned)tmp[j]); } hash[64] = 0; fprintf(stderr, "file %s: %s\n", path, hash); /* prepare and bind */ /* TODO check null */ sqlite3 *db = pkg->db; rc = sqlite3_prepare_v2(db, "select size, content is not null from files where hash = ?", -1, &ifile,0); if (rc != SQLITE_OK) { SQLERROR(sqlite3_errmsg(db)); return 0; } /* hash, filename */ sqlite3_bind_text(ifile, 1, hash, 64, SQLITE_STATIC); rc = sqlite3_step(ifile); if (rc != SQLITE_DONE) { if (rc != SQLITE_ROW) { /* didn't find a row */ SQLERROR(sqlite3_errmsg(db)); zpm_rollback(pkg); return 0; } haverow = 1; // fprintf(stderr, "have row for hash\n"); type = sqlite3_column_type(ifile, 0); if (type == SQLITE_NULL) { /* TODO assert, this shouldn't be possible? */ fprintf(stderr, "no file size\n"); sqlite3_finalize(ifile); return 0; } type = sqlite3_column_type(ifile, 1); if (type == SQLITE_NULL) { /* TODO assert, this shouldn't be possible? */ fprintf(stderr, "no file data\n"); sqlite3_finalize(ifile); return 0; /* which is fine, just need to update the row then */ } havedata = sqlite3_column_int(ifile, 1); } sqlite3_finalize(ifile); if (!havedata) { /* compress */ outbuf = compresslzma(content, sbuf.st_size, &outlen); if (!outbuf) { fprintf(stderr, "compresslzma failed\n"); return 0; } fprintf(stderr, "compressed to %zu\n", outlen); /* don't need the original file now */ munmap(content, sbuf.st_size); close(fd); /* start a transaction */ // do that outside of here //zpm_begin(pkg); /* insert */ if (haverow) { fprintf(stderr, "adding file data\n"); rc = sqlite3_prepare(db, "update files set size = ?, content = ? where hash = ?", -1, &ifile,0); } else { fprintf(stderr, "creating new data row\n"); rc = sqlite3_prepare(db, "insert into files (size, content, hash) values (?,?,?)", -1, &ifile,0); } if (rc != SQLITE_OK) { SQLERROR(sqlite3_errmsg(db)); fprintf(stderr, "cant prepare data\n"); zpm_rollback(pkg); return 0; } sqlite3_bind_int64(ifile, 1, (sqlite3_int64)sbuf.st_size); if (rc != SQLITE_OK) { SQLERROR(sqlite3_errmsg(db)); fprintf(stderr, "cant bind size\n"); zpm_rollback(pkg); return 0; } sqlite3_bind_blob64(ifile, 2, outbuf, (sqlite3_int64)outlen, SQLITE_STATIC); if (rc != SQLITE_OK) { SQLERROR(sqlite3_errmsg(db)); fprintf(stderr, "cant bind content\n"); zpm_rollback(pkg); return 0; } sqlite3_bind_text(ifile, 3, hash, 64, SQLITE_STATIC); if (rc != SQLITE_OK) { SQLERROR(sqlite3_errmsg(db)); fprintf(stderr, "cant bind hash\n"); zpm_rollback(pkg); return 0; } rc = sqlite3_step(ifile); if (rc != SQLITE_DONE) { SQLERROR(sqlite3_errmsg(db)); sqlite3_finalize(ifile); zpm_rollback(pkg); return 0; } sqlite3_finalize(ifile); /* commit */ //zpm_commit(pkg); } else { /* don't need the original file now */ munmap(content, sbuf.st_size); close(fd); } /* if package and not nopackage flag, add to package */ if (pkg->pkgname && (!ZPM_NOPACKAGE)) { /* TODO */ } /* return */ return 1; } #endif #if 0 int main(int ac, char **av){ sqlite3 *db = 0; int rc; int blobsize; int64_t size; void *xzdata; int type; FILE *out; sqlite3_stmt *ifile; char *hash; char *filename; if (ac < 3) { fprintf(stderr, "usage: db hash file\n"); return 1; } rc = sqlite3_open(av[1], &db); if (rc) { SQLERROR(sqlite3_errmsg(db)); sqlite3_close(db); return 1; } } #endif #if 0 Packages are sqlite databases get application id and userver Primitive operations: add path to repo associate path with package associate blob with path? add blob to repo * extract blob to a path compare blob to filesystem path create package with info Extra primitives: record elf information about blob compress blob uncompress blob sign a package? What are we verifying? #endif