From 0643628a3b9c359a78862d09828e83b562feea08 Mon Sep 17 00:00:00 2001 From: Nathan Wagner Date: Thu, 17 Nov 2016 09:08:02 +0000 Subject: [PATCH] add elf dependency tracking --- Makefile | 34 ++++++--- db.sql | 61 ++++++++++----- elf/elf.h | 10 +++ lib/zpm.c | 218 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 286 insertions(+), 37 deletions(-) diff --git a/Makefile b/Makefile index 2e9f06e..71c066b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -CFLAGS=-Wall -std=c99 -Ilib -I. +CFLAGS=-Wall -std=c99 -Ilib -Ielf -I. LZMAFLAGS=-Ilzma/api -Ilzma/tuklib -Ilzma/lzma -Ilzma/common -Ilzma/check -Ilzma/lz -Ilzma/rangecoder -Ilzma/simple -Ilzma/delta -DHAVE_CHECK_CRC64 -DHAVE_CHECK_CRC32 -DHAVE_ENCODER_LZMA2 -DHAVE_DECODER_LZMA2 -DHAVE_MF_BT4 @@ -10,7 +10,7 @@ LZMAOBJ=$(filter-out lzma/common/stream_encoder_mt.o, $(LZMASRC:%.c=%.o)) curdir=$(shell pwd) -ZPKGBIN=zpm-addfile zpm-extract zpm-init +ZPKGBIN=zpm-addfile zpm-extract zpm-init zpm-vercmp def: programs d: printf '%s\n' $(LZMAOBJ) @@ -22,10 +22,13 @@ lzma.c: mklzma lzma.o: lzma.c gcc -std=c99 -Wall -c -o $@ $< +stest: $(ZPKGBIN) + PATH=$(curdir)/t:$(curdir):$(PATH) t/$(T).t + test: $(ZPKGBIN) PATH=$(curdir)/t:$(curdir):$(PATH) prove -e '' t/*.t -programs: elftype soname zpm-addfile zpm-extract zpm-init +programs: elftype soname zpm-soneed zpm-addfile zpm-extract zpm-init zpm-vercmp uncompress: uncompress.o $(CC) $(CFLAGS) -o $@ $+ -llzma @@ -36,17 +39,20 @@ elftype: elf/elftype.c soname: elf/soname.c $(CC) -Ielf $(CFLAGS) -o $@ $+ -zpm-addfile: zpm-addfile.o libzpm.a - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lzpm +zpm-soneed: elf/needed.c + $(CC) -Ielf $(CFLAGS) -o $@ $+ + +zpm-addfile: zpm-addfile.o libzpm.a libelf.a + $(CC) -Ielf $(CFLAGS) $(LDFLAGS) -o $@ $< -lzpm -lelf zpm-hash: zpm-hash.o libzpm.a - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lzpm + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lzpm -lelf zpm-init: zpm-init.o libzpm.a - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lzpm + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lzpm -lelf zpm-extract: zpm-extract.o libzpm.a - $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lzpm + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lzpm -lelf newdb.c: db.sql echo "char createdb[] = {" > $@ @@ -59,11 +65,19 @@ lib/sqlite3.o: lib/sqlite3.c lib/config.h $(LZMAOBJ): $(CC) $(CFLAGS) $(LZMAFLAGS) -c -o $@ $*.c -libzpm.a: lib/sha256.o lib/db.o lib/compress.o lib/uncompress.o newdb.o lib/zpm.o \ +lib/zpm.o: newdb.c + +zpm-vercmp: vercmp.o + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< + +libzpm.a: lib/sha256.o lib/db.o lib/compress.o lib/uncompress.o lib/zpm.o \ lib/sqlite3.o \ $(LZMAOBJ) ar rcuv $@ $? +libelf.a: elf/libelf.o + ar rcuv $@ $? + install: elftype install -D zpm $(DESTDIR)/bin/zpm install -D zpm-note $(DESTDIR)/bin/zpm-note @@ -79,4 +93,4 @@ install: elftype #SPOOL=$(DESTDIR)/var/lib/admin/notes ./zpm-sequence -c notes clean: - rm -f *.o lib/*.o $(LZMAOBJ) liblzma.a zpm-addfile soname + rm -f *.o lib/*.o $(LZMAOBJ) liblzma.a libelf.a libzpm.a zpm-addfile soname diff --git a/db.sql b/db.sql index be2a3b3..fab5b24 100644 --- a/db.sql +++ b/db.sql @@ -4,6 +4,7 @@ PRAGMA application_id = 0x5a504442; PRAGMA user_version = 1; -- should be faster with rowid due to the blob content +-- these are really just blobs of data CREATE TABLE files ( hash text primary key, size integer, @@ -12,6 +13,8 @@ CREATE TABLE files ( ) ; +-- information about packages +-- a package is identified by a package,version,release triple create table packages ( package text, version text, -- the upstream version string @@ -29,14 +32,19 @@ create table packages ( without rowid ; +-- files contained in a package create table packagefiles ( + -- package id triple package text, version text, release integer, - path text, + + path text, -- filesystem path mode text, -- perms, use text for octal rep? username text, -- name of owner groupname text, -- group of owner + uid integer, -- numeric uid, generally ignored + gid integer, -- numeric gid, generally ignored --filetype integer default 0, -- 0 regular file, 1 directory, 2 symlink -- regular file if null target and not null hash -- except that we could not know the hash, or care @@ -45,36 +53,45 @@ create table packagefiles ( -- hard link if not null hash and not null target -- device special files add dev number column -- fifos add mode? Can encode filetype in mode. - target text, -- link target - hash text, -- what should go here, null for dir? - mtime integer, -- seconds since epoch, but allow finer? + target text, -- link target for symlinks + hash text, -- null if no actual content, i.e. anything but a regular file + mtime integer, -- seconds since epoch, finer resolution probably not needed primary key (package,version,release,path), foreign key (package,version,release) references packages (package,version,release) on delete cascade ) without rowid ; +create table elfinfo ( + file text, -- hash of blob + elftype text, + foreign key (file) references files on delete cascade +); + -- TODO just elf information? -- and just hash, not package? -create table libraries ( - package text, - subpackage text, - path text, - soname text +create table elflibraries ( + file text primary key, + soname text, + foreign key (file) references files on delete cascade ) +without rowid ; -create table librarydeps ( - package text, - subpackage text, - path text, - soname text -- soname of dependency -); +create table elfneeded ( + file text, + needed text, -- soname of dependency + primary key (file, needed), + foreign key (file) references files on delete cascade +) +without rowid +; -- package scripts: table of package, stage, file create table scripts ( package text, - subpackage text, + version text, + release integer, stage text, hash text ); @@ -82,11 +99,14 @@ create table scripts ( -- package dependencies: table of package, dependency, dep type (package, soname) create table packagedeps ( package text, - subpackage text, - requires text, -- package name - subreq text, -- if requires only a sub package, probably most common for libs + version text, + release integer, + required text, -- package name + -- following can be null for not checked minversion text, - maxversion text + minrelease integer, + maxversion text, + maxrelease integer ); -- capability labels @@ -106,4 +126,5 @@ create table packagegroups ( package text, "group" text ); + commit; diff --git a/elf/elf.h b/elf/elf.h index ad2d03e..9100cf4 100644 --- a/elf/elf.h +++ b/elf/elf.h @@ -569,4 +569,14 @@ typedef struct { #define ELFOSABI_CLOUDABI 17 #define ELFOSABI_OPENVOS 18 +/* libelf.c */ +Elf64_Ehdr *libelf_header(void *elf); +Elf64_Shdr *libelf_shdr(void *elf, int n); +Elf64_Shdr *libelf_sht_strtab(void *elf); +char *libelf_sectionname(Elf64_Shdr *section, Elf64_Shdr *strtab); +Elf64_Shdr *libelf_section_n(void *elf, int n); +Elf64_Shdr *libelf_section(void *elf, int type); +int libelf_type(void *elf); +int libelf_iself(void *elf); + #endif diff --git a/lib/zpm.c b/lib/zpm.c index d97bebe..9d62ad7 100644 --- a/lib/zpm.c +++ b/lib/zpm.c @@ -11,6 +11,7 @@ #include #include "zpm.h" +#include "elf.h" #include "sha256.h" @@ -265,10 +266,13 @@ static 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)) { + 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); return 0; break; } @@ -494,6 +498,192 @@ int zpm_hash(char *path, char *hash, uint32_t flags) { return 1; } +static sqlite3_stmt *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); + + return ifile; +} + +static int set_elf_info(sqlite3 *db, char *hash, char *content, size_t length) { + if (length >= sizeof (Elf64_Ehdr) && libelf_iself(content)) { + char *strtab; + Elf64_Dyn *dyn; + int i; + Elf64_Phdr *phdr; + Elf64_Ehdr *hdr; + sqlite3_stmt *ifile; + int rc; + + /* go ahead and set up elf information now */ + /* clear existing for this hash */ + ifile = run_for_hash(db, "delete from elfinfo where file = ?", hash); + do { + rc = sqlite3_step(ifile); +#if 0 + if (rc == SQLITE_ROW) { + int nc; + fprintf(stderr, "delete row has %d columns: ", sqlite3_column_count(ifile)); + nc = sqlite3_column_count(ifile); + for (i = 0; i < nc; i++) { + char *r; + r = sqlite3_column_text(ifile, i); + fprintf(stderr, ", %s", r); + } + fprintf(stderr, "\n"); + } +#endif + } while (rc == SQLITE_ROW); + if (rc != SQLITE_DONE) { + SQLERROR(sqlite3_errmsg(db)); + sqlite3_finalize(ifile); + fprintf(stderr, "error clearing elf info: %d\n", rc); + return 0; + } + sqlite3_finalize(ifile); + ifile = run_for_hash(db, "delete from elflibraries where file = ?", hash); + do { + rc = sqlite3_step(ifile); + } while (rc == SQLITE_ROW); + if (rc != SQLITE_DONE) { + SQLERROR(sqlite3_errmsg(db)); + sqlite3_finalize(ifile); + fprintf(stderr, "error clearing elf library: %d\n", rc); + return 0; + } + sqlite3_finalize(ifile); + ifile = run_for_hash(db, "delete from elfneeded where file = ?", hash); + do { + rc = sqlite3_step(ifile); + } while (rc == SQLITE_ROW); + if (rc != SQLITE_DONE) { + SQLERROR(sqlite3_errmsg(db)); + sqlite3_finalize(ifile); + fprintf(stderr, "error clearing elf needed\n"); + return 0; + } + sqlite3_finalize(ifile); + + hdr = libelf_header(content); + /* if lib, set soname */ + if (libelf_type(content) == ET_DYN) { + char *elf; + Elf64_Shdr *shdr, *dynsect, *dynstrtab = 0; + + elf = (char *)content; + for (i = 0; i < hdr->e_shnum; i++) { + shdr = (Elf64_Shdr *)(elf + hdr->e_shoff + i * hdr->e_shentsize); + if (shdr->sh_type == SHT_DYNAMIC) { + dynsect = shdr; + } else if (shdr->sh_type == SHT_STRTAB && i == hdr->e_shstrndx) { + dynstrtab = shdr; + } + } + if (!dynstrtab) { + exit(8); + } + if (!dynsect) { + exit(9); + } + + char *name; + Elf64_Shdr *dyntab; + name = elf + dynstrtab->sh_offset; + for (i = 0; i < hdr->e_shnum; i++) { + shdr = (Elf64_Shdr *)(elf + hdr->e_shoff + i * hdr->e_shentsize); + if (shdr->sh_type == SHT_STRTAB && !strcmp(".dynstr", name+shdr->sh_name)) { + dyntab = shdr; + } + } + if (!dyntab) { + exit(10); + } + + char *dynname; + Elf64_Dyn *dent; + dynname = elf + dyntab->sh_offset; + + for (dent = (Elf64_Dyn *)(elf + dynsect->sh_offset); dent->d_tag != DT_NULL; dent++) { + if (dent->d_tag == DT_SONAME) { + char *soname = dynname + dent->d_un.d_val; + sqlite3_prepare_v2(db, "insert into elflibraries (file,soname) values (?,?)",-1, &ifile, 0); + sqlite3_bind_text(ifile,1,hash,64,SQLITE_STATIC); + sqlite3_bind_text(ifile,2,soname,-1,SQLITE_STATIC); + rc = sqlite3_step(ifile); + if (rc != SQLITE_DONE) { + SQLERROR(sqlite3_errmsg(db)); + sqlite3_finalize(ifile); + fprintf(stderr, "error setting library soname\n"); + return 0; + } + sqlite3_finalize(ifile); + } + } + } + + /* if exe, set neededs */ + if (libelf_type(content) == ET_EXEC) { + Elf64_Shdr *dsect; + 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); + } + } + dyn = (Elf64_Dyn *)(elf + dsect->sh_offset); + if (!dyn) { + exit(9); + } + 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); + fprintf(stderr, "%s needs %s\n", hash, need); + rc = sqlite3_step(ifile); + if (rc != SQLITE_DONE) { + SQLERROR(sqlite3_errmsg(db)); + sqlite3_finalize(ifile); + fprintf(stderr, "error setting needed library\n"); + return 0; + } + sqlite3_reset(ifile); + } + dyn++; + } + sqlite3_finalize(ifile); + } + } + return 1; +} + #if 1 int zpm_import(struct zpm *pkg, char *path, uint32_t flags, char *hash) { int fd; @@ -550,6 +740,7 @@ int zpm_import(struct zpm *pkg, char *path, uint32_t flags, char *hash) { } content = mmap(0, sbuf.st_size, PROT_READ,MAP_PRIVATE, fd, 0); + close(fd); if (!content) { pkg->error = errno; fprintf(stderr, "%s can't mmap %s: %s\n", __FUNCTION__, path,strerror(errno)); @@ -566,13 +757,15 @@ int zpm_import(struct zpm *pkg, char *path, uint32_t flags, char *hash) { hash[64] = 0; //fprintf(stderr, "file %s: %s\n", path, hash); - /* prepare and bind */ /* TODO check null */ sqlite3 *db = pkg->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; } @@ -587,6 +780,7 @@ int zpm_import(struct zpm *pkg, char *path, uint32_t flags, char *hash) { /* didn't find a row */ SQLERROR(sqlite3_errmsg(db)); zpm_rollback(pkg); + munmap(content, sbuf.st_size); return 0; } haverow = 1; @@ -596,6 +790,7 @@ int zpm_import(struct zpm *pkg, char *path, uint32_t flags, char *hash) { /* 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); @@ -603,6 +798,7 @@ int zpm_import(struct zpm *pkg, char *path, uint32_t flags, char *hash) { /* 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 */ } @@ -616,12 +812,10 @@ int zpm_import(struct zpm *pkg, char *path, uint32_t flags, char *hash) { outbuf = compresslzma(content, sbuf.st_size, &outlen); if (!outbuf) { fprintf(stderr, "compresslzma failed\n"); + munmap(content, sbuf.st_size); 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 @@ -639,6 +833,7 @@ int zpm_import(struct zpm *pkg, char *path, uint32_t flags, char *hash) { SQLERROR(sqlite3_errmsg(db)); fprintf(stderr, "cant prepare data\n"); zpm_rollback(pkg); + munmap(content, sbuf.st_size); return 0; } @@ -647,6 +842,7 @@ int zpm_import(struct zpm *pkg, char *path, uint32_t flags, char *hash) { SQLERROR(sqlite3_errmsg(db)); fprintf(stderr, "cant bind size\n"); zpm_rollback(pkg); + munmap(content, sbuf.st_size); return 0; } sqlite3_bind_blob64(ifile, 2, outbuf, (sqlite3_int64)outlen, SQLITE_STATIC); @@ -654,6 +850,7 @@ int zpm_import(struct zpm *pkg, char *path, uint32_t flags, char *hash) { SQLERROR(sqlite3_errmsg(db)); fprintf(stderr, "cant bind content\n"); zpm_rollback(pkg); + munmap(content, sbuf.st_size); return 0; } sqlite3_bind_text(ifile, 3, hash, 64, SQLITE_STATIC); @@ -661,6 +858,7 @@ int zpm_import(struct zpm *pkg, char *path, uint32_t flags, char *hash) { SQLERROR(sqlite3_errmsg(db)); fprintf(stderr, "cant bind hash\n"); zpm_rollback(pkg); + munmap(content, sbuf.st_size); return 0; } rc = sqlite3_step(ifile); @@ -668,6 +866,7 @@ int zpm_import(struct zpm *pkg, char *path, uint32_t flags, char *hash) { SQLERROR(sqlite3_errmsg(db)); sqlite3_finalize(ifile); zpm_rollback(pkg); + munmap(content, sbuf.st_size); return 0; } sqlite3_finalize(ifile); @@ -675,12 +874,17 @@ int zpm_import(struct zpm *pkg, char *path, uint32_t flags, char *hash) { /* commit */ //zpm_commit(pkg); - } else { /* don't need the original file now */ + + } + + if (!set_elf_info(pkg->db, hash, content, sbuf.st_size)) { + fprintf(stderr, "setting elf info failed\n"); munmap(content, sbuf.st_size); - close(fd); + return 0; } + munmap(content, sbuf.st_size); /* if package and not nopackage flag, add to package */ if (pkg->pkgname && (!ZPM_NOPACKAGE)) { -- 2.40.0