#define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include "zpm.h" #include "elf.h" #include "sha256.h" 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; } /* wrapper for sqlite3_exec */ int zpm_exec(struct zpm *z, const char *sql, int(*callback)(void *, int, char **, char**), void *arg, char **errmsg) { return sqlite3_exec(z->db, sql, callback, arg, errmsg); } 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_readonly(struct zpm *z) { if (z->db && sqlite3_db_readonly(z->db, "main")) { return 1; } return 0; } 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) { char *error; switch (sqlite3_exec(pkg->db, createdb, (int (*)(void *,int,char **,char **))0, NULL, &error)) { case SQLITE_OK: break; default: SQLERROR(sqlite3_errmsg(pkg->db)); fprintf(stderr, "error: %s\n", error); sqlite3_free(error); zpm_rollback(pkg); return 0; break; } return 1; } /* assumes pkg->db is set */ static int setupdb(struct zpm *zpm) { char *errstr = 0; int appid, dbver; zpm->error = 0; appid = zpm_db_pragma(zpm, 1); dbver = zpm_db_pragma(zpm, 2); if (appid != 0x5a504442) { fprintf(stderr, "unknown database type\n"); zpm->error = 1; return 0; } if (dbver > 1) { fprintf(stderr, "version %d zpm db detected, this program only works with version 1 databases\n", dbver); zpm->error = 1; return 0; } sqlite3_exec(zpm->db, "pragma foreign_keys = ON;", NULL, NULL, &errstr); if (errstr) { free(zpm->errmsg); zpm->errmsg = strdup(errstr); fprintf(stderr, "sqlite foreign key error: %s\n", errstr); sqlite3_free(errstr); zpm->error = 1; return 0; } /* TODO add vercmp */ return 1; } struct zpm *zpm_clearmem(struct zpm *zpm) { if (!zpm) { zpm = malloc(sizeof *zpm); } if (zpm) { *zpm = (struct zpm){0}; } return zpm; } static void zpm_set_db_errmsg(struct zpm *zpm, const char *msg) { if (zpm) { if (zpm->dberrmsg) { free(zpm->dberrmsg); } if (msg) { zpm->dberrmsg = strdup(msg); if (!zpm->dberrmsg) { zpm->error = 1; } } else { zpm->dberrmsg = 0; } } } int zpm_init(struct zpm *pkg, char *path) { int rc; sqlite3 *db = 0; int appid; if (!pkg) { return 0; } zpm_clearmem(pkg); rc = sqlite3_open_v2(path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL); if (rc) { pkg->error = 1; pkg->dbresult = rc; zpm_set_db_errmsg(pkg, sqlite3_errstr(rc)); if (db) { sqlite3_close(db); } fprintf(stderr, "error (%d): %s: %s\n", rc, pkg->dberrmsg ? pkg->dberrmsg : "null", path); return 0; } pkg->db = db; pkg->path = strdup(path); appid = zpm_db_pragma(pkg, 1); switch (appid) { case 0: if (!zpm_db_initialize(pkg)) { sqlite3_close(db); return 1; }; break; case 0x5a504442: /* already initialized */ break; default: fprintf(stderr, "unknown database type\n"); sqlite3_close(db); return 0; break; } if (!setupdb(pkg)) { sqlite3_close(db); pkg->db = 0; return 0; } zpm_addvercmp(pkg); return 1; } int zpm_open(struct zpm *zpm, char *path) { int rc; sqlite3 *db = 0; zpm_clearmem(zpm); if (!path) { path = getenv("ZPMDB"); } if (!path) { path = "/var/lib/zpm/local.db"; } rc = sqlite3_open_v2(path, &db, SQLITE_OPEN_READWRITE, NULL); if (rc) { SQLERROR(sqlite3_errmsg(db)); sqlite3_close(db); zpm->error = 1; fprintf(stderr, "path = %s\n", path); return 0; } zpm->db = db; zpm->path = strdup(path); if (!setupdb(zpm)) { sqlite3_close(db); zpm->db = 0; zpm->error = 1; return 0; } zpm_addvercmp(zpm); 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; } static int zpm_sqlite_vercmp(void *not_used, int lena, const void *a, int lenb, const void *b) { int rv; char *bufa, *bufb; if (not_used != 0) fprintf(stderr, "sqlite vercmp not_used = %p\n", not_used); if (lena == 0 && lenb > 0) return 1; if (lenb == 0 && lena > 0) return -1; bufa = sqlite3_malloc(lena+1); bufb = sqlite3_malloc(lenb+1); strncpy(bufa, a, lena); strncpy(bufb, b, lenb); bufa[lena] = 0; bufb[lenb] = 0; rv = zpm_vercmp(bufa, bufb); sqlite3_free(bufa); sqlite3_free(bufb); return rv; } int zpm_addvercmp(struct zpm *pkg) { return sqlite3_create_collation( pkg->db, "vercmp", SQLITE_UTF8, NULL, zpm_sqlite_vercmp ); } int zpm_extract(struct zpm *pkg, char *hash, char *path, mode_t mode) { int rc; int blobsize; void *xzdata; int type; int out; sqlite3_stmt *ifile; size_t len; char *tmpfile = 0; sqlite3 *db; if (!pkg || !pkg->db) { return 0; } db = pkg->db; if (!path) { path = "-"; } 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: %s\n", hash); 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); if (strcmp(path, "-")) { len = strlen(path); tmpfile = malloc(len+8+1); if (!tmpfile) { fprintf(stderr, "malloc error\n"); return 0; } sprintf(tmpfile, "%sXXXXXX", path); out = open(tmpfile, O_CREAT|O_WRONLY|O_TRUNC, 0600); if (out == -1) { fprintf(stderr, "can't open output file %s: %s\n", tmpfile, strerror(errno)); sqlite3_finalize(ifile); sqlite3_close(db); free(tmpfile); return 0; } } else { out = 1; } #if 0 fprintf(stderr, "uncompressing %d bytes at %p, expect %lld\n", blobsize, xzdata, (long long int)size); #endif uncompresslzma(xzdata, blobsize, out); close(out); sqlite3_finalize(ifile); rc = 1; if (tmpfile) { if (chmod(tmpfile, mode) == -1) { fprintf(stderr, "can't chmod %s: %s\n", tmpfile, strerror(errno)); rc = 0; } else if (rename(tmpfile, path) == -1) { fprintf(stderr, "extract rename failed: %s\n", strerror(errno)); rc = 0; } } if (rc == 0 && tmpfile) { if (unlink(tmpfile) == -1) { fprintf(stderr, "unlink tmpfile %s fail: %s\n", tmpfile, strerror(errno)); } } free(tmpfile); return rc; } static int run_for_hash(sqlite3 *db, char *sql, char *hash) { int rc; sqlite3_stmt *ifile; rc = sqlite3_prepare_v2(db, sql, -1, &ifile, 0); if (rc != SQLITE_OK) { SQLERROR(sqlite3_errmsg(db)); return 0; } /* hash, filename */ sqlite3_bind_text(ifile, 1, hash, 64, SQLITE_STATIC); do { rc = sqlite3_step(ifile); } while (rc == SQLITE_ROW); sqlite3_finalize(ifile); return rc == SQLITE_DONE; } #define SQLERP(db, msg) fprintf(stderr, "%s: %s\n", msg, sqlite3_errmsg(db)) static int set_elf_info(struct zpm *zpm, char *hash, char *content, size_t length) { char *strtab; Elf64_Dyn *dyn; int i; Elf64_Phdr *phdr; Elf64_Ehdr *hdr; sqlite3_stmt *ifile; sqlite3 *db = zpm->db; if (length < sizeof (Elf64_Ehdr) || !libelf_iself(content)) { /* not an elf file */ return 1; } /* clear existing for this hash */ if (!run_for_hash(db, "delete from elflibraries where file = ?", hash)) { SQLERP(db, "error clearing elf library"); return 0; } if (!run_for_hash(db, "delete from elfneeded where file = ?", hash)) { SQLERP(db, "error clearing elf needed"); return 0; } hdr = libelf_header(content); /* if lib, set soname */ if (libelf_type(content) == ET_DYN) { char *soname = libelf_soname(content); if (soname) { zpm_db_run(zpm, "insert into elflibraries (file,soname) values (%Q,%Q);", hash, soname); if (zpm->error) { return 0; } } /* some dyn don't have an soname, so we don't * need to warn */ } /* if exe, set neededs */ /* libraries can have neededs too */ if (libelf_type(content) == ET_EXEC || libelf_type(content) == ET_DYN) { Elf64_Shdr *dsect = 0; char *elf; elf = (char *)content; /* find program header table */ for (i = 0; i < hdr->e_phnum; i++) { phdr = (Elf64_Phdr *)(elf + hdr->e_phoff + i * hdr->e_phentsize); if (phdr->p_type == PT_DYNAMIC) { dsect = (Elf64_Shdr *)(elf + phdr->p_offset); } } if (!dsect) { /* no dynamic section found */ return 1; } dyn = (Elf64_Dyn *)dsect; dsect = libelf_section(elf, SHT_DYNAMIC); Elf64_Shdr *strsect; strsect = libelf_section_n(elf, dsect->sh_link); strtab = elf + strsect->sh_offset; sqlite3_prepare_v2(db, "insert into elfneeded (file,needed) values (?,?)",-1, &ifile, 0); sqlite3_bind_text(ifile,1,hash,64,SQLITE_STATIC); while (dyn->d_tag != DT_NULL) { if (dyn->d_tag == DT_NEEDED) { char *need; int rc; need = strtab + dyn->d_un.d_val; if (strlen(need) == 0) continue; sqlite3_bind_text(ifile,2,need,strlen(need),SQLITE_STATIC); #if 0 fprintf(stderr, "%s needs %s\n", hash, need); #endif rc = sqlite3_step(ifile); if (rc != SQLITE_DONE) { SQLERP(db, "error setting needed library"); sqlite3_finalize(ifile); return 0; } sqlite3_reset(ifile); } dyn++; } sqlite3_finalize(ifile); } return 1; } int zpm_import(struct zpm *zpm, char *path, uint32_t flags, char *hash) { int fd; void *content = 0; struct stat sbuf; sqlite3_stmt *ifile = 0; int haverow = 0,havedata = 0; int rc,type; char hashbuf[65]; /* xz compress it */ size_t outlen = 0; void *outbuf; if (!zpm || !zpm->db || !path) { return 0; } /* use local if caller didn't pass in room */ if (!hash) { hash = hashbuf; } if (flags) { fprintf(stderr, "zpm_import unused flags = %d\n", flags); } /* mmap the file */ fd = open(path, O_RDONLY); if (fd == -1) { zpm->error = errno; fprintf(stderr, "%s can't open %s: %s\n", __FUNCTION__, path,strerror(errno)); return 0; } if (fstat(fd, &sbuf) == -1) { zpm->error = errno; fprintf(stderr, "%s can't fstat %s: %s\n", __FUNCTION__, path,strerror(errno)); return 0; } /* not a regular file? */ if (!S_ISREG(sbuf.st_mode)) { char *ftype; switch (sbuf.st_mode & S_IFMT) { case S_IFSOCK: ftype = "socket"; break; case S_IFLNK : ftype = "symlink"; break; case S_IFBLK : ftype = "block device"; break; case S_IFDIR : ftype = "directory"; break; case S_IFCHR : ftype = "character device"; break; case S_IFIFO : ftype = "fifo"; break; default: ftype = "unknown file type"; break; } /* TODO this is ok, just stored differently */ fprintf(stderr, "%s can't import %s file: %s\n", __FUNCTION__, ftype, path); zpm->error = EINVAL; return 0; } content = mmap(0, sbuf.st_size, PROT_READ,MAP_PRIVATE, fd, 0); close(fd); if (!content) { zpm->error = errno; fprintf(stderr, "%s can't mmap %s: %s\n", __FUNCTION__, path,strerror(errno)); return 0; } zpm_hash_mem(content, sbuf.st_size, hash); hash[64] = 0; /* TODO check null */ sqlite3 *db = zpm->db; /* prepare and bind */ 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)); munmap(content, sbuf.st_size); 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)); munmap(content, sbuf.st_size); return 0; } haverow = 1; 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); munmap(content, sbuf.st_size); 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); munmap(content, sbuf.st_size); return 0; /* which is fine, just need to update the row then */ } havedata = sqlite3_column_int(ifile, 1); } sqlite3_finalize(ifile); if (!havedata) { /* compress */ // fprintf(stderr, "compressing\n"); outbuf = compresslzma(content, sbuf.st_size, &outlen); if (!outbuf) { fprintf(stderr, "compresslzma failed\n"); munmap(content, sbuf.st_size); return 0; } //// fprintf(stderr, "compress finished\n"); /* insert */ if (haverow) { rc = sqlite3_prepare_v2(db, "update files set size = ?, content = ? where hash = ?", -1, &ifile,0); } else { rc = sqlite3_prepare_v2(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"); munmap(content, sbuf.st_size); return 0; } rc = sqlite3_bind_int64(ifile, 1, (sqlite3_int64)sbuf.st_size); if (rc != SQLITE_OK) { SQLERROR(sqlite3_errmsg(db)); sqlite3_finalize(ifile); fprintf(stderr, "cant bind size\n"); munmap(content, sbuf.st_size); return 0; } rc = sqlite3_bind_blob64(ifile, 2, outbuf, (sqlite3_int64)outlen, SQLITE_STATIC); if (rc != SQLITE_OK) { SQLERROR(sqlite3_errmsg(db)); sqlite3_finalize(ifile); fprintf(stderr, "cant bind content\n"); munmap(content, sbuf.st_size); return 0; } rc = sqlite3_bind_text(ifile, 3, hash, 64, SQLITE_STATIC); if (rc != SQLITE_OK) { SQLERROR(sqlite3_errmsg(db)); fprintf(stderr, "cant bind hash\n"); sqlite3_finalize(ifile); munmap(content, sbuf.st_size); return 0; } rc = sqlite3_step(ifile); if (rc != SQLITE_DONE) { SQLERROR(sqlite3_errmsg(db)); sqlite3_finalize(ifile); munmap(content, sbuf.st_size); return 0; } sqlite3_finalize(ifile); /* don't need the original file now */ } if (!set_elf_info(zpm, hash, content, sbuf.st_size)) { fprintf(stderr, "setting elf info failed\n"); munmap(content, sbuf.st_size); return 0; } munmap(content, sbuf.st_size); /* return */ return 1; }