#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; } /* NULL? Create? */ int zpm_open(struct zpm *pkg, char *path) { int rc; char *errstr = 0; sqlite3 *db = 0; pkg->db = 0; pkg->path = 0; pkg->version = 0; pkg->release = 0; pkg->pkgname = 0; pkg->installed = 0; /* TODO some way to determine if the DB is newly created ? */ /* could check for tables, if there are any, then check version, etc */ rc = sqlite3_open(path, &db); if (rc) { SQLERROR(sqlite3_errmsg(db)); 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; } pkg->path = dupstr(path); pkg->db = db; /* 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,havedata; 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); /* compress */ outbuf = compresslzma(content, sbuf.st_size, &outlen); // fprintf(stderr, "compressed to %zu\n", outlen); /* don't need the original file now */ munmap(content, sbuf.st_size); close(fd); /* prepare and bind */ /* TODO check null */ sqlite3 *db = pkg->db; rc = sqlite3_prepare(db, "select size, content is not null from files where hash = ?", -1, &ifile,0); if (rc != SQLITE_OK) { SQLERROR(sqlite3_errmsg(db)); return 1; } /* 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) { /* start a transaction */ // do that outside of here //zpm_begin(pkg); /* insert */ if (haverow) { rc = sqlite3_prepare(db, "update files set size = ?, content = ? where hash = ?", -1, &ifile,0); } else { // fprintf(stderr, "missing file data\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); } /* 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