]> pd.if.org Git - zpackage/commitdiff
large commit of C work
authorNathan Wagner <nw@hydaspes.if.org>
Fri, 16 Sep 2016 23:52:48 +0000 (23:52 +0000)
committerNathan Wagner <nw@hydaspes.if.org>
Fri, 16 Sep 2016 23:52:48 +0000 (23:52 +0000)
27 files changed:
.gitignore
Commands [new file with mode: 0644]
DATABASE [new file with mode: 0644]
Makefile
README [new file with mode: 0644]
compress.c [new file with mode: 0644]
db.sql [new file with mode: 0644]
lib/compress.c
lib/db.c [new file with mode: 0644]
lib/sha256.h [new file with mode: 0644]
lib/uncompress.c [new file with mode: 0644]
lib/zpm.c [new file with mode: 0644]
uncompress.c [new file with mode: 0644]
zpm-addfile.c [new file with mode: 0644]
zpm-addtopackage [new file with mode: 0755]
zpm-extract.c [new file with mode: 0644]
zpm-extractfile.c [new file with mode: 0644]
zpm-fileinfo [new file with mode: 0755]
zpm-getlibs [new file with mode: 0755]
zpm-install [new file with mode: 0755]
zpm-ipkgfile [new file with mode: 0755]
zpm-newpackage [new file with mode: 0755]
zpm-pkg [new file with mode: 0755]
zpm-pkginfo [new file with mode: 0755]
zpm-preserve
zpm-showpkg [new file with mode: 0755]
zpm.h [new file with mode: 0644]

index 941a63944438f7224a4465c8a42258243f6b996e..ecc5e6aa96c84c2dd4a387975c0fe4a2838ebf95 100644 (file)
@@ -1 +1,10 @@
 elftype
+*.o
+*.a
+zpm-addfile
+zpm-extract
+uncompress
+*.zpm
+*.db
+soname
+compress
diff --git a/Commands b/Commands
new file mode 100644 (file)
index 0000000..82049d4
--- /dev/null
+++ b/Commands
@@ -0,0 +1,32 @@
+note:
+       add a note file, - from stdin, list if none
+
+edit:
+       edit a note file
+
+ack:
+       acknowledge a note file
+
+build:
+       build a package from source
+       - take file names on stdin and build package from them
+       - <name> repackage an installed package
+
+install:
+       install a package, - <name> for from a file
+
+remove:
+       remove a package
+
+db:
+       edit package databases used for finding packages
+
+info:
+       get information on a package
+
+track:
+       add a package to config tracked packages
+
+add: add to a repository/database
+
+clean: clean a repository/database
diff --git a/DATABASE b/DATABASE
new file mode 100644 (file)
index 0000000..d54da7b
--- /dev/null
+++ b/DATABASE
@@ -0,0 +1,140 @@
+Packages are sqlite databases
+
+get application id and userver
+
+Primitive operations:
+
+add blob to repo
+add path to repo
+associate path with package
+associate blob with path?
+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?
+
+Signatures
+----------
+
+things:
+
+files, just a table of file contents, hash to content,
+avoids redundancy.  possibly type? size? internal compression?
+blobs might make more sense as a name.
+
+package files: table of pathnames in a package, with metadata
+file hash, type (directory, file, symlink, etc), permissions,
+owner, group, acls?, config file, and package name...
+
+create table paths (
+       path    text,
+       mode    integer, -- perms, use text for octal rep?
+       hash    text, -- what should go here, null for dir
+       mtime   integer -- seconds since epoch, but allow finer?
+);
+
+create table packagefiles (
+       package text,
+       subpackage      text, -- libs, dev, client, server, whatever
+       path    text,
+       filetype        text -- e.g. config, etc?
+);
+
+-- TODO just elf information?
+create table libraries (
+       package text,
+       subpackage      text,
+       path    text,
+       soname  text
+);
+
+create table librarydeps (
+       package text,
+       subpackage      text,
+       path    text,
+       soname  text -- soname of dependency
+);
+
+-- package scripts: table of package, stage, file
+create table scripts (
+       package text,
+       subpackage      text,
+       hash    text
+);
+
+-- 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
+       minversion      text,
+       maxversion      text
+);
+
+-- capability labels
+create table provides (
+       package text,
+       subpackage      text,
+       label   text -- a capability label
+);
+
+create table requires (
+       package text,
+       subpackage      text,
+       label   text -- a capability label
+);
+
+create table packages (
+       package text,
+       version text, -- the upstream version string
+       release integer, -- the local release number
+       description     text,
+       architecture    text,
+       url     text,
+       licenses        text, -- hash of actual license?  need table for more than one?
+       packager        text,
+       build_date      integer,
+       install_date    integer
+);
+
+create table packagegroups (
+       package text,
+       group   text
+);
+
+packages: table of package info
+name, version, release,
+tied package?  i.e. another package/version/release this one goes with.
+
+Actual Package Install DB:
+--------------------------
+
+packages table:
+package, name, version, release, install ts, uninstall/upgrade ts
+
+contents table:
+from package files table in package, plus install info
+
+Installing a Package
+--------------------
+
+insert into DB.packages the package info from the .zpkg file, leave install date null
+extract the pre-install script and run it.  abort if exit failure
+insert into the contents table info from the ZP.packagefiles table
+for each file in the new contents, extract the file to a temporary location
+in the same directory, rename into place, update as installed
+for each file in the old contents, remove the file if it's not in the new contents
+delete the row from the contents table
+update the uninstall and install ts as a single transaction
+extract and run post-install script
+
+pre/post install scripts get arguments:
+package name, new version, new release, old version, old release
+old version/release only if upgrade.
index 239047f9d40cb0113e1866151736d9315c4d4204..053af317e05bcf4e425210a4d6bf306ddc58cc92 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,12 @@
-CFLAGS=-Wall -std=c99
+CFLAGS=-Wall -std=c99 -I. -L.
+LDFLAGS=-L.
 
-programs: elftype soname
+def: libzpm.a
+
+programs: elftype soname zpm-addfile zpm-extract
+
+uncompress: uncompress.o 
+       $(CC) $(CFLAGS) -o $@ $+ -llzma -lsqlite3
 
 elftype: elf/elftype.c
        $(CC) $(CFLAGS) -o $@ $+
@@ -8,6 +14,20 @@ elftype: elf/elftype.c
 soname: elf/soname.c
        $(CC) $(CFLAGS) -o $@ $+
 
+zpm-addfile: zpm-addfile.o libzpm.a
+       $(CC) $(CFLAGS) -o $@ $< -lsqlite3 -llzma -lzpm
+
+zpm-extract: zpm-extract.o libzpm.a
+       $(CC) $(CFLAGS) -o $@ $< -lsqlite3 -llzma -lzpm
+
+newdb.c: db.sql
+       echo "char createdb[] = {" > $@
+       xxd -i < $< >> $@
+       echo "};" >> $@
+
+libzpm.a: lib/sha256.o lib/db.o lib/compress.o lib/uncompress.o newdb.o lib/zpm.o
+       ar rcuv $@ $+
+
 install: elftype
        install -D zpm $(DESTDIR)/bin/zpm
        install -D zpm-note $(DESTDIR)/bin/zpm-note
@@ -21,3 +41,6 @@ install: elftype
        install -D elftype $(DESTDIR)/usr/sbin/elftype
        install -D soname $(DESTDIR)/usr/sbin/soname
        #SPOOL=$(DESTDIR)/var/lib/admin/notes ./zpm-sequence -c notes
+
+clean:
+       rm -f *.o
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..e58cc90
--- /dev/null
+++ b/README
@@ -0,0 +1,72 @@
+Features
+--------
+
+binary installs
+
+package metadata
+
+package integrity
+       gpg signatures
+       sha256 sums
+       sha3 sums
+
+package contents integrity
+       sha256 sums of installed files
+       sha3 sums
+       sha1 sums
+       lists of directory contents
+
+package database editing
+       e.g. marking a file as a configuration file
+
+repackaging
+       creating a package from the current state of an installed package
+
+package a tarball
+       create a package from a tarball
+
+package from stdin
+       like cpio
+
+package a directory
+       like tar
+
+build from shell scripts
+
+rebuild a package
+
+write ahead log for package filesystem operations
+       it should always be possible for the package system
+       to figure out if it was interrupted
+
+install, uninstall, upgrade, and downgrade hooks
+
+dependency tracking
+
+network fetching of packages and package databases
+reference counting of dynamic libs
+
+simple packaging constructs
+       use shell scripts where possible
+       use shell functions as hooks and callbacks
+
+package is just a tarball
+can contain more than one package
+xz compressed
+
+version?
+package-name/version/files/{usr/bin/foo, etc}
+package-name/version/info/hooks - shell script of functions
+package-name/version/info/pkginfo - shell readable
+package-name/version/info/manifest? derivable from tarball
+package-name/version/info/dynamiclibs, or greppable from manifest?
+
+/var/lib/zpac:
+packages/installed/<package>/files/sha1/ab/ab/hash -> file
+librefs/<library name>/<package> (or file?)
+sha1/ab/ab/<sha1hashs> -> file
+fileowner/sha1/ab/ab/hash/packagename - can be more than one
+
+need to mark a package as held for no automatic upgrade
+
+configure to run a command after an any package work
diff --git a/compress.c b/compress.c
new file mode 100644 (file)
index 0000000..648a6d9
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * add a file/files to an sqlite db
+ * in the 'files' table.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+
+#include "lzma.h"
+
+#include "sha256.h"
+
+void *compresslzma(void *buf, size_t bufsize, size_t *len) {
+       /* xz compress it */
+       size_t outsize;
+       void *outbuf;
+       size_t outlen = 0;
+
+       outsize = lzma_stream_buffer_bound(bufsize);
+       outbuf = malloc(outsize);
+       /* TODO adjust encoding level for size */
+       lzma_easy_buffer_encode(6, LZMA_CHECK_CRC64, NULL,
+                       buf, bufsize,
+                       outbuf, &outlen, outsize
+                       );
+       *len = outlen;
+       return outbuf;
+}
+
+int main(int ac, char **av){
+       int i, j;
+
+       /* insert each file */
+       for (i = 1; i < ac; i++) {
+               int fd;
+               void *content;
+               struct stat sbuf;
+               unsigned char tmp[32];
+               char hash[65]; /* hash in hex */
+               hash_state md;
+
+               fd = open(av[i], O_RDONLY);
+               if (fd == -1) {
+                       exit(1);
+               }
+               if (fstat(fd, &sbuf) == -1) {
+                       exit(1);
+               }
+               /* not a regular file? */
+               if (!S_ISREG(sbuf.st_mode)) {
+                       exit(1);
+               }
+
+               content = mmap(0, sbuf.st_size, PROT_READ,MAP_PRIVATE, fd, 0);
+               if (!content) {
+                       exit(2);
+               }
+
+               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", av[i], hash);
+
+               /* xz compress it */
+               size_t outlen = 0;
+               FILE *save;
+               void *outbuf;
+
+               outbuf = compresslzma(content, sbuf.st_size, &outlen);
+               save = fopen("foo.xz", "w");
+               fwrite(outbuf, outlen, 1, save);
+               fclose(save);
+               free(outbuf);
+
+               //Find out the maximum size of the compressed output for single-call compression:
+//                     zlib: compressBound()
+//                     liblzma: lzma_stream_buffer_bound()
+
+//                     Compressing:
+//zlib: compress() or compress2()
+ //     liblzma: lzma_easy_buffer_encode() or lzma_stream_buffer_encode()
+
+  //    Decompressing:
+//zlib: uncompress()
+ //     liblzma: lzma_stream_buffer_decode()
+
+  //    See container.h (src/liblzma/api/lzma/container.h in the source tree) for details how to use these functions.
+
+               /* either way we're done with this now */
+               munmap(content, sbuf.st_size);
+       }
+       return 0;
+}
diff --git a/db.sql b/db.sql
new file mode 100644 (file)
index 0000000..7ad22b9
--- /dev/null
+++ b/db.sql
@@ -0,0 +1,108 @@
+begin;
+
+PRAGMA application_id = 0x5a504442;
+PRAGMA user_version = 1;
+
+-- should be faster with rowid due to the blob content
+CREATE TABLE files (
+       hash text primary key,
+               size integer,
+               compression text,
+               content blob
+)
+;
+
+create table packages (
+       package text,
+       version text, -- the upstream version string
+       release integer, -- the local release number
+       description     text,
+       architecture    text,
+       url     text,
+       licenses        text, -- hash of actual license?  need table for more than one?
+       packager        text,
+       build_time      integer default (strftime('%s', 'now')),
+       install_time    integer,
+       primary key (package,version,release)
+)
+without rowid
+;
+
+create table packagefiles (
+       package text,
+       version text,
+       release integer,
+       path    text,
+       mode    text, -- perms, use text for octal rep?
+       username        text, -- name of owner
+       groupname       text, -- group of owner
+       --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
+       -- directory if null hash and null target
+       -- symlink if null hash and not null target
+       -- 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?
+       primary key (package,version,release,path),
+       foreign key (package,version,release) references packages (package,version,release) on delete cascade
+)
+without rowid
+;
+
+-- TODO just elf information?
+-- and just hash, not package?
+create table libraries (
+       package text,
+       subpackage      text,
+       path    text,
+       soname  text
+)
+;
+
+create table librarydeps (
+       package text,
+       subpackage      text,
+       path    text,
+       soname  text -- soname of dependency
+);
+
+-- package scripts: table of package, stage, file
+create table scripts (
+       package text,
+       subpackage      text,
+       stage   text,
+       hash    text
+);
+
+-- 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
+       minversion      text,
+       maxversion      text
+);
+
+-- capability labels
+create table provides (
+       package text,
+       subpackage      text,
+       label   text -- a capability label
+);
+
+create table requires (
+       package text,
+       subpackage      text,
+       label   text -- a capability label
+);
+
+create table packagegroups (
+       package text,
+       "group" text
+);
+commit;
index a7427cd231988a597a486de02c34b92bbafc2800..5101688a9e83c433afc0554babcb2274b6a59b03 100644 (file)
@@ -1,8 +1,3 @@
-/*
- * add a file/files to an sqlite db
- * in the 'files' table.
- */
-
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -16,6 +11,8 @@
 
 #include "lzma.h"
 
+/* TODO pass in the outbuf ? */
+/* or wrap lzma_stream_buffer_bound */
 void *compresslzma(void *buf, size_t bufsize, size_t *len) {
        /* xz compress it */
        size_t outsize;
diff --git a/lib/db.c b/lib/db.c
new file mode 100644 (file)
index 0000000..e09732e
--- /dev/null
+++ b/lib/db.c
@@ -0,0 +1,70 @@
+/*
+ * add a file/files to an sqlite db
+ * in the 'files' table.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <sys/mman.h>
+
+#include <sqlite3.h>
+#include "sha256.h"
+
+
+static int callback(void *NotUsed, int argc, char **argv, char **azColName){
+       int i;
+       for(i=0; i<argc; i++){
+               printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
+       }
+       printf("\n");
+       return 0;
+}
+
+static char *create_table = "create table if not exists files (hash text primary key, size integer, compression text, content blob)";
+
+struct dbh {
+       sqlite3 *db;
+       char *errmsg;
+       int rc;
+};
+
+#define SQLERROR(x) fprintf(stderr, "%s %d: %s\n", __func__, __LINE__, (x))
+int begin(sqlite3 *db) {
+       int rc;
+       char *err;
+
+       rc = sqlite3_exec(db, "begin;", callback, 0, &err);
+       if (rc != SQLITE_OK) {
+               SQLERROR(err);
+               sqlite3_free(err);
+       }
+       return rc;
+}
+
+int commit(sqlite3 *db) {
+       int rc;
+       char *err;
+
+       rc = sqlite3_exec(db, "commit;", callback, 0, &err);
+       if (rc != SQLITE_OK) {
+               SQLERROR(err);
+               sqlite3_free(err);
+       }
+       return rc;
+}
+
+int rollback(sqlite3 *db) {
+       int rc;
+       char *err;
+
+       rc = sqlite3_exec(db, "rollback;", callback, 0, &err);
+       if (rc != SQLITE_OK) {
+               SQLERROR(err);
+               sqlite3_free(err);
+       }
+       return rc;
+}
diff --git a/lib/sha256.h b/lib/sha256.h
new file mode 100644 (file)
index 0000000..a72f81f
--- /dev/null
@@ -0,0 +1,213 @@
+#ifndef TOMCRYPT_H_
+#define TOMCRYPT_H_
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
+#include <limits.h>
+
+#include <stdint.h>
+
+/* max size of either a cipher/hash block or symmetric key [largest of the two] */
+#define MAXBLOCKSIZE  128
+
+/* descriptor table size */
+#define TAB_SIZE      32
+
+/* error codes [will be expanded in future releases] */
+enum {
+   CRYPT_OK=0,             /* Result OK */
+   CRYPT_ERROR,            /* Generic Error */
+   CRYPT_NOP,              /* Not a failure but no operation was performed */
+
+   CRYPT_INVALID_KEYSIZE,  /* Invalid key size given */
+   CRYPT_INVALID_ROUNDS,   /* Invalid number of rounds */
+   CRYPT_FAIL_TESTVECTOR,  /* Algorithm failed test vectors */
+
+   CRYPT_BUFFER_OVERFLOW,  /* Not enough space for output */
+   CRYPT_INVALID_PACKET,   /* Invalid input packet given */
+
+   CRYPT_INVALID_PRNGSIZE, /* Invalid number of bits for a PRNG */
+   CRYPT_ERROR_READPRNG,   /* Could not read enough from PRNG */
+
+   CRYPT_INVALID_CIPHER,   /* Invalid cipher specified */
+   CRYPT_INVALID_HASH,     /* Invalid hash specified */
+   CRYPT_INVALID_PRNG,     /* Invalid PRNG specified */
+
+   CRYPT_MEM,              /* Out of memory */
+
+   CRYPT_PK_TYPE_MISMATCH, /* Not equivalent types of PK keys */
+   CRYPT_PK_NOT_PRIVATE,   /* Requires a private PK key */
+
+   CRYPT_INVALID_ARG,      /* Generic invalid argument */
+   CRYPT_FILE_NOTFOUND,    /* File Not Found */
+
+   CRYPT_PK_INVALID_TYPE,  /* Invalid type of PK key */
+   CRYPT_PK_INVALID_SYSTEM,/* Invalid PK system specified */
+   CRYPT_PK_DUP,           /* Duplicate key already in key ring */
+   CRYPT_PK_NOT_FOUND,     /* Key not found in keyring */
+   CRYPT_PK_INVALID_SIZE,  /* Invalid size input for PK parameters */
+
+   CRYPT_INVALID_PRIME_SIZE,/* Invalid size of prime requested */
+   CRYPT_PK_INVALID_PADDING /* Invalid padding on input */
+};
+
+#define LTC_MUTEX_GLOBAL(x)
+#define LTC_MUTEX_PROTO(x)
+#define LTC_MUTEX_TYPE(x)
+#define LTC_MUTEX_INIT(x)
+#define LTC_MUTEX_LOCK(x)
+#define LTC_MUTEX_UNLOCK(x)
+
+#define STORE32H(x, y)                                                                     \
+     { (y)[0] = (unsigned char)(((x)>>24)&255); (y)[1] = (unsigned char)(((x)>>16)&255);   \
+       (y)[2] = (unsigned char)(((x)>>8)&255); (y)[3] = (unsigned char)((x)&255); }
+
+#define LOAD32H(x, y)                            \
+     { x = ((unsigned long)((y)[0] & 255)<<24) | \
+           ((unsigned long)((y)[1] & 255)<<16) | \
+           ((unsigned long)((y)[2] & 255)<<8)  | \
+           ((unsigned long)((y)[3] & 255)); }
+
+#define STORE64H(x, y)                                                                     \
+   { (y)[0] = (unsigned char)(((x)>>56)&255); (y)[1] = (unsigned char)(((x)>>48)&255);     \
+     (y)[2] = (unsigned char)(((x)>>40)&255); (y)[3] = (unsigned char)(((x)>>32)&255);     \
+     (y)[4] = (unsigned char)(((x)>>24)&255); (y)[5] = (unsigned char)(((x)>>16)&255);     \
+     (y)[6] = (unsigned char)(((x)>>8)&255); (y)[7] = (unsigned char)((x)&255); }
+
+/* rotates the hard way */
+#define ROL(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
+#define ROR(x, y) ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
+#define ROLc(x, y) ( (((unsigned long)(x)<<(unsigned long)((y)&31)) | (((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
+#define RORc(x, y) ( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | ((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL)
+
+
+#ifndef MIN
+   #define MIN(x, y) ( ((x)<(y))?(x):(y) )
+#endif
+
+struct sha256_state {
+    uint64_t length;
+    uint32_t state[8], curlen;
+    unsigned char buf[64];
+};
+
+typedef union Hash_state {
+    char dummy[1];
+    struct sha256_state sha256;
+    void *data;
+} hash_state;
+
+/** hash descriptor */
+extern  struct ltc_hash_descriptor {
+    /** name of hash */
+    char *name;
+    /** internal ID */
+    unsigned char ID;
+    /** Size of digest in octets */
+    unsigned long hashsize;
+    /** Input block size in octets */
+    unsigned long blocksize;
+    /** ASN.1 OID */
+    unsigned long OID[16];
+    /** Length of DER encoding */
+    unsigned long OIDlen;
+
+    /** Init a hash state
+      @param hash   The hash to initialize
+      @return CRYPT_OK if successful
+    */
+    int (*init)(hash_state *hash);
+    /** Process a block of data 
+      @param hash   The hash state
+      @param in     The data to hash
+      @param inlen  The length of the data (octets)
+      @return CRYPT_OK if successful
+    */
+    int (*process)(hash_state *hash, const unsigned char *in, unsigned long inlen);
+    /** Produce the digest and store it
+      @param hash   The hash state
+      @param out    [out] The destination of the digest
+      @return CRYPT_OK if successful
+    */
+    int (*done)(hash_state *hash, unsigned char *out);
+    /** Self-test
+      @return CRYPT_OK if successful, CRYPT_NOP if self-tests have been disabled
+    */
+    int (*test)(void);
+
+    /* accelerated hmac callback: if you need to-do multiple packets just use the generic hmac_memory and provide a hash callback */
+    int  (*hmac_block)(const unsigned char *key, unsigned long  keylen,
+                       const unsigned char *in,  unsigned long  inlen, 
+                             unsigned char *out, unsigned long *outlen);
+
+} hash_descriptor[];
+
+int sha256_init(hash_state * md);
+int sha256_process(hash_state * md, const unsigned char *in, unsigned long inlen);
+int sha256_done(hash_state * md, unsigned char *hash);
+int sha256_test(void);
+extern const struct ltc_hash_descriptor sha256_desc;
+
+int find_hash(const char *name);
+int find_hash_id(unsigned char ID);
+int find_hash_oid(const unsigned long *ID, unsigned long IDlen);
+int find_hash_any(const char *name, int digestlen);
+int register_hash(const struct ltc_hash_descriptor *hash);
+int unregister_hash(const struct ltc_hash_descriptor *hash);
+int hash_is_valid(int idx);
+
+LTC_MUTEX_PROTO(ltc_hash_mutex)
+
+int hash_memory(int hash, 
+                const unsigned char *in,  unsigned long inlen, 
+                      unsigned char *out, unsigned long *outlen);
+int hash_memory_multi(int hash, unsigned char *out, unsigned long *outlen,
+                      const unsigned char *in, unsigned long inlen, ...);
+
+#ifndef LTC_NO_FILE
+int hash_filehandle(int hash, FILE *in, unsigned char *out, unsigned long *outlen);
+int hash_file(int hash, const char *fname, unsigned char *out, unsigned long *outlen);
+#endif
+
+/* a simple macro for making hash "process" functions */
+#define HASH_PROCESS(func_name, compress_name, state_var, block_size)                       \
+int func_name (hash_state * md, const unsigned char *in, unsigned long inlen)               \
+{                                                                                           \
+    unsigned long n;                                                                        \
+    int           err;                                                                      \
+    LTC_ARGCHK(md != NULL);                                                                 \
+    LTC_ARGCHK(in != NULL);                                                                 \
+    if (md-> state_var .curlen > sizeof(md-> state_var .buf)) {                             \
+       return CRYPT_INVALID_ARG;                                                            \
+    }                                                                                       \
+    while (inlen > 0) {                                                                     \
+        if (md-> state_var .curlen == 0 && inlen >= block_size) {                           \
+           if ((err = compress_name (md, (unsigned char *)in)) != CRYPT_OK) {               \
+              return err;                                                                   \
+           }                                                                                \
+           md-> state_var .length += block_size * 8;                                        \
+           in             += block_size;                                                    \
+           inlen          -= block_size;                                                    \
+        } else {                                                                            \
+           n = MIN(inlen, (block_size - md-> state_var .curlen));                           \
+           memcpy(md-> state_var .buf + md-> state_var.curlen, in, (size_t)n);              \
+           md-> state_var .curlen += n;                                                     \
+           in             += n;                                                             \
+           inlen          -= n;                                                             \
+           if (md-> state_var .curlen == block_size) {                                      \
+              if ((err = compress_name (md, md-> state_var .buf)) != CRYPT_OK) {            \
+                 return err;                                                                \
+              }                                                                             \
+              md-> state_var .length += 8*block_size;                                       \
+              md-> state_var .curlen = 0;                                                   \
+           }                                                                                \
+       }                                                                                    \
+    }                                                                                       \
+    return CRYPT_OK;                                                                        \
+}
+
+#endif
+/* TOMCRYPT_H_ */
diff --git a/lib/uncompress.c b/lib/uncompress.c
new file mode 100644 (file)
index 0000000..867e5bc
--- /dev/null
@@ -0,0 +1,104 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/mman.h>
+
+#include <sqlite3.h>
+
+#include "sha256.h"
+
+#include "lzma.h"
+
+void uncompresslzma(void *buf, size_t bufsize, FILE *out) {
+       lzma_stream s = LZMA_STREAM_INIT;
+       lzma_stream *strm;
+       
+       uint8_t outbuf[BUFSIZ];
+
+       int ret;
+
+       strm = &s;
+
+       ret = lzma_stream_decoder(strm, UINT64_MAX, 0);
+       /* The only reasonable error here is LZMA_MEM_ERROR. */
+       if (ret != LZMA_OK) {
+               fprintf(stderr, "%s", ret == LZMA_MEM_ERROR ? strerror(ENOMEM)
+                               : "Internal error (bug)");
+               exit(EXIT_FAILURE);
+       }
+
+       strm->avail_in = bufsize;
+       strm->next_in = buf;
+       strm->avail_out = BUFSIZ;
+       strm->next_out = outbuf;
+
+        lzma_action action = LZMA_RUN;
+
+       while (1) {
+               ret = lzma_code(strm, action);
+
+               // Write and check write error before checking decoder error.
+               // This way as much data as possible gets written to output
+               // even if decoder detected an error.
+               if (strm->avail_out == 0 || ret != LZMA_OK) {
+                       const size_t write_size = BUFSIZ - strm->avail_out;
+
+                       if (fwrite(outbuf, 1, write_size, out) != write_size) {
+                               // Wouldn't be a surprise if writing to stderr
+                               // would fail too but at least try to show an
+                               // error message.
+                               fprintf(stderr, "Cannot write to output file stream: "
+                                               "%s", strerror(errno));
+                               exit(EXIT_FAILURE);
+                       }
+
+                       strm->next_out = outbuf;
+                       strm->avail_out = BUFSIZ;
+               }
+
+               if (ret != LZMA_OK) {
+                       if (ret == LZMA_STREAM_END) {
+                               // lzma_stream_decoder() already guarantees
+                               // that there's no trailing garbage.
+                               assert(strm->avail_in == 0);
+                               //assert(action == LZMA_FINISH);
+                               return;
+                       }
+
+                       const char *msg;
+                       switch (ret) {
+                               case LZMA_MEM_ERROR:
+                                       msg = strerror(ENOMEM);
+                                       break;
+
+                               case LZMA_FORMAT_ERROR:
+                                       msg = "File format not recognized";
+                                       break;
+
+                               case LZMA_OPTIONS_ERROR:
+                                       // FIXME: Better message?
+                                       msg = "Unsupported compression options";
+                                       break;
+
+                               case LZMA_DATA_ERROR:
+                                       msg = "File is corrupt";
+                                       break;
+
+                               case LZMA_BUF_ERROR:
+                                       msg = "Unexpected end of input";
+                                       break;
+
+                               default:
+                                       msg = "Internal error (bug)";
+                                       break;
+                       }
+
+                       fprintf(stderr, "xz: %s\n", msg);
+                       exit(EXIT_FAILURE);
+               }
+       }
+}
diff --git a/lib/zpm.c b/lib/zpm.c
new file mode 100644 (file)
index 0000000..37c7e7b
--- /dev/null
+++ b/lib/zpm.c
@@ -0,0 +1,539 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#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
diff --git a/uncompress.c b/uncompress.c
new file mode 100644 (file)
index 0000000..8b5d98f
--- /dev/null
@@ -0,0 +1,198 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/mman.h>
+
+#include <sqlite3.h>
+
+#include "sha256.h"
+
+#include "lzma.h"
+
+void uncompresslzma(void *buf, size_t bufsize, FILE *out) {
+       lzma_stream s = LZMA_STREAM_INIT;
+       lzma_stream *strm;
+       
+       uint8_t outbuf[BUFSIZ];
+
+       int ret;
+
+       strm = &s;
+
+       ret = lzma_stream_decoder(strm, UINT64_MAX, 0);
+       /* The only reasonable error here is LZMA_MEM_ERROR. */
+       if (ret != LZMA_OK) {
+               fprintf(stderr, "%s", ret == LZMA_MEM_ERROR ? strerror(ENOMEM)
+                               : "Internal error (bug)");
+               exit(EXIT_FAILURE);
+       }
+
+       strm->avail_in = bufsize;
+       strm->next_in = buf;
+       strm->avail_out = BUFSIZ;
+       strm->next_out = outbuf;
+
+        lzma_action action = LZMA_RUN;
+
+       while (1) {
+               ret = lzma_code(strm, action);
+
+               // Write and check write error before checking decoder error.
+               // This way as much data as possible gets written to output
+               // even if decoder detected an error.
+               if (strm->avail_out == 0 || ret != LZMA_OK) {
+                       const size_t write_size = BUFSIZ - strm->avail_out;
+
+                       if (fwrite(outbuf, 1, write_size, out) != write_size) {
+                               // Wouldn't be a surprise if writing to stderr
+                               // would fail too but at least try to show an
+                               // error message.
+                               fprintf(stderr, "Cannot write to output file stream: "
+                                               "%s", strerror(errno));
+                               exit(EXIT_FAILURE);
+                       }
+
+                       strm->next_out = outbuf;
+                       strm->avail_out = BUFSIZ;
+               }
+
+               if (ret != LZMA_OK) {
+                       if (ret == LZMA_STREAM_END) {
+                               // lzma_stream_decoder() already guarantees
+                               // that there's no trailing garbage.
+                               assert(strm->avail_in == 0);
+                               //assert(action == LZMA_FINISH);
+                               return;
+                       }
+
+                       const char *msg;
+                       switch (ret) {
+                               case LZMA_MEM_ERROR:
+                                       msg = strerror(ENOMEM);
+                                       break;
+
+                               case LZMA_FORMAT_ERROR:
+                                       msg = "File format not recognized";
+                                       break;
+
+                               case LZMA_OPTIONS_ERROR:
+                                       // FIXME: Better message?
+                                       msg = "Unsupported compression options";
+                                       break;
+
+                               case LZMA_DATA_ERROR:
+                                       msg = "File is corrupt";
+                                       break;
+
+                               case LZMA_BUF_ERROR:
+                                       msg = "Unexpected end of input";
+                                       break;
+
+                               default:
+                                       msg = "Internal error (bug)";
+                                       break;
+                       }
+
+                       fprintf(stderr, "xz: %s\n", msg);
+                       exit(EXIT_FAILURE);
+               }
+       }
+}
+
+#define SQLERROR(x) fprintf(stderr, "%s %d: %s\n", __func__, __LINE__, (x))
+
+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;
+       }
+
+       rc = sqlite3_prepare(db, "select size, content from files where hash = ?", -1, &ifile,0);
+       if (rc != SQLITE_OK) {
+               SQLERROR(sqlite3_errmsg(db));
+               return 1;
+       }
+
+       /* hash, filename */
+       hash = av[2];
+       filename = av[3];
+
+       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 1;
+       }
+       /* either way we're done with this now */
+
+       if (rc != SQLITE_ROW) {
+               SQLERROR(sqlite3_errmsg(db));
+               sqlite3_finalize(ifile);
+               sqlite3_close(db);
+               return 2;
+       }
+
+       type = sqlite3_column_type(ifile, 0);
+       if (type == SQLITE_NULL) {
+               fprintf(stderr, "no file size\n");
+               sqlite3_finalize(ifile);
+               sqlite3_close(db);
+               return 3;
+       }
+       type = sqlite3_column_type(ifile, 1);
+       if (type == SQLITE_NULL) {
+               fprintf(stderr, "no file data\n");
+               sqlite3_finalize(ifile);
+               sqlite3_close(db);
+               return 4;
+       }
+       size = sqlite3_column_int64(ifile, 0);
+       xzdata = (void *)sqlite3_column_blob(ifile, 1);
+       blobsize = sqlite3_column_bytes(ifile, 1);
+
+       out = fopen(filename, "w");
+       if (!out) {
+               fprintf(stderr, "can't open output file %s\n", filename);
+               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);
+       sqlite3_close(db);
+       return 0;
+}
diff --git a/zpm-addfile.c b/zpm-addfile.c
new file mode 100644 (file)
index 0000000..961b6b8
--- /dev/null
@@ -0,0 +1,20 @@
+#include <stdio.h>
+#include "zpm.h"
+
+int main(int ac, char **av){
+       struct zpm pkg;
+       char hash[65];
+
+       if (ac < 3) {
+               fprintf(stderr, "usage: db path\n");
+               return 1;
+       }
+       zpm_open(&pkg, av[1]);
+       zpm_begin(&pkg);
+       zpm_import(&pkg, av[2], 0, hash);
+       zpm_commit(&pkg);
+       zpm_close(&pkg);
+       //fprintf(stdout, "%s %s\n", hash, av[2]);
+       fprintf(stdout, "%s\n", hash);
+       return 0;
+}
diff --git a/zpm-addtopackage b/zpm-addtopackage
new file mode 100755 (executable)
index 0000000..3833c00
--- /dev/null
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+package=${1:-$ZPMPACKAGE}
+shift
+pkgver=${ZPMPACKAGEVER:-1.0}
+pkgrel=${ZPMPACKAGEREL:-1}
+
+# option for "multipackage" just to let the system know that's what you meant
+# option to take filenames from stdin
+# parse package, version, release from file if not given
+while getopts :f:v:r:d:a:u:l:p:b:P: opt; do
+       case $opt in
+               f) pkgfile="$OPTARG" ;;
+               v) pkgver="$OPTARG" ;;
+               r) pkgrel="$OPTARG" ;;
+               d) description="$OPTARG" ;;
+               a) arch="$OPTARG" ;;
+               u) url="$OPTARG" ;;
+               l) licenses="$OPTARG" ;;
+               p) packager="$OPTARG" ;;
+               b) builddate="$OPTARG" ;;
+               P) prefix="$OPTARG" ;;
+       esac
+done
+
+set -e
+if [ -z "$pkgfile" ]; then
+       pkgfile="$package-$pkgver-$pkgrel.zpm"
+fi
+
+if [ ! -f $pkgfile ]; then
+       ./zpm-newpackage $package || exit 1
+else
+       appid=$(sqlite3 $pkgfile 'pragma application_id;' | ( echo obase = 16; cat - ) | bc)
+       if [ "$appid" != "5A504442" ]; then
+               echo $pkgfile does not appear to be a zpm package file
+               exit 1
+       fi
+fi
+
+for path in $*; do
+       mtime=$(stat -c '%Y' $path)
+       uid=$(stat -c '%u' $path)
+       gid=$(stat -c '%g' $path)
+       username=$(stat -c '%U' $path)
+       groupname=$(stat -c '%G' $path)
+       mode=$(stat -c '%a' $path)
+
+       # strip off leading slashes
+       rpath=$(echo "$path" | sed -e 's|^/*||')
+       # and a leading ./
+       rpath=${rpath#./}
+       rpath=$(echo "$rpath" | sed -e 's|^/*||')
+
+       if [ -z "$rpath" ] || [ "$rpath" = '.' ]; then
+               continue
+       fi
+
+       if [ ! -z "$prefix" ]; then
+               # trailing slashes on prefix
+               prefix=$(echo "$prefix" | sed -e 's|/*$||')
+               rpath="$prefix/$rpath"
+       fi
+
+       if [ -f "$path" ]; then
+
+               hash=$(./zpm-addfile $pkgfile $path)
+
+#if [ -z "$hash" ]; then continue; fi
+
+# TODO mtime, mode
+sqlite3 $pkgfile <<EOS
+PRAGMA foreign_keys = ON;
+insert or replace into packagefiles (package,version,release,path,mode,mtime,username,groupname,hash)
+values ('$package', '$pkgver', $pkgrel, '$rpath', '$mode',$mtime, '$username','$groupname','$hash')
+;
+EOS
+elif [ -d "$path" ]; then
+sqlite3 $pkgfile <<EOS
+PRAGMA foreign_keys = ON;
+insert or replace into packagefiles (package,version,release,path,mode,mtime,username,groupname)
+values ('$package', '$pkgver', $pkgrel, '$rpath', '$mode',$mtime, '$username','$groupname')
+;
+EOS
+elif [ -l "$path" ]; then
+       target=$(readlink $path)
+sqlite3 $pkgfile <<EOS
+PRAGMA foreign_keys = ON;
+insert or replace into packagefiles (package,version,release,path,mode,mtime,username,groupname,target)
+values ('$package', '$pkgver', $pkgrel, '$rpath', '$mode',$mtime, '$username','$groupname','$target')
+;
+EOS
+fi
+#printf "%s %s%s\n" $path $rpath ${target:+" -> $target"}
+printf "%s\n" $path
+done
diff --git a/zpm-extract.c b/zpm-extract.c
new file mode 100644 (file)
index 0000000..e050bf2
--- /dev/null
@@ -0,0 +1,118 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/mman.h>
+
+#include "zpm.h"
+
+#if 1
+int main(int ac, char **av){
+       struct zpm pkg;
+       int mode = 0644;
+
+       if (ac < 3) {
+               fprintf(stderr, "usage: db hash file\n");
+               return 1;
+       }
+       zpm_open(&pkg, av[1]);
+       zpm_extract(&pkg, av[2], av[3], mode);
+       zpm_close(&pkg);
+}
+#else
+
+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;
+       }
+
+       rc = sqlite3_prepare(db, "select size, content from files where hash = ?", -1, &ifile,0);
+       if (rc != SQLITE_OK) {
+               SQLERROR(sqlite3_errmsg(db));
+               return 1;
+       }
+
+       /* hash, filename */
+       hash = av[2];
+       filename = av[3];
+
+       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 1;
+       }
+       /* either way we're done with this now */
+
+       if (rc != SQLITE_ROW) {
+               SQLERROR(sqlite3_errmsg(db));
+               sqlite3_finalize(ifile);
+               sqlite3_close(db);
+               return 2;
+       }
+
+       type = sqlite3_column_type(ifile, 0);
+       if (type == SQLITE_NULL) {
+               fprintf(stderr, "no file size\n");
+               sqlite3_finalize(ifile);
+               sqlite3_close(db);
+               return 3;
+       }
+       type = sqlite3_column_type(ifile, 1);
+       if (type == SQLITE_NULL) {
+               fprintf(stderr, "no file data\n");
+               sqlite3_finalize(ifile);
+               sqlite3_close(db);
+               return 4;
+       }
+       size = sqlite3_column_int64(ifile, 0);
+       xzdata = (void *)sqlite3_column_blob(ifile, 1);
+       blobsize = sqlite3_column_bytes(ifile, 1);
+
+       out = fopen(filename, "w");
+       if (!out) {
+               fprintf(stderr, "can't open output file %s\n", filename);
+               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);
+       sqlite3_close(db);
+       return 0;
+}
+#endif
diff --git a/zpm-extractfile.c b/zpm-extractfile.c
new file mode 100644 (file)
index 0000000..68ab0e1
--- /dev/null
@@ -0,0 +1,381 @@
+/*
+ * add a file/files to an sqlite db
+ * in the 'files' table.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <sys/mman.h>
+
+#include <sqlite3.h>
+#include "sha256.h"
+
+#include "lzma.h"
+
+
+void uncompresslzma(void *buf, size_t bufsize, FILE *out) {
+       lzma_stream s = LZMA_STREAM_INIT;
+       lzma_stream *strm;
+       
+       uint8_t outbuf[BUFSIZ];
+
+       int ret;
+
+       strm = &s;
+
+       ret = lzma_stream_decoder(strm, UINT64_MAX, 0);
+       /* The only reasonable error here is LZMA_MEM_ERROR. */
+       if (ret != LZMA_OK) {
+               fprintf(stderr, "%s", ret == LZMA_MEM_ERROR ? strerror(ENOMEM)
+                               : "Internal error (bug)");
+               exit(EXIT_FAILURE);
+       }
+
+       strm->avail_in = bufsize;
+       strm->next_in = buf;
+       strm->avail_out = BUFSIZ;
+       strm->next_out = outbuf;
+
+        lzma_action action = LZMA_RUN;
+
+       while (1) {
+               ret = lzma_code(strm, action);
+
+               // Write and check write error before checking decoder error.
+               // This way as much data as possible gets written to output
+               // even if decoder detected an error.
+               if (strm->avail_out == 0 || ret != LZMA_OK) {
+                       const size_t write_size = BUFSIZ - strm->avail_out;
+
+                       if (fwrite(outbuf, 1, write_size, out) != write_size) {
+                               // Wouldn't be a surprise if writing to stderr
+                               // would fail too but at least try to show an
+                               // error message.
+                               fprintf(stderr, "Cannot write to output file stream: "
+                                               "%s", strerror(errno));
+                               exit(EXIT_FAILURE);
+                       }
+
+                       strm->next_out = outbuf;
+                       strm->avail_out = BUFSIZ;
+               }
+
+               if (ret != LZMA_OK) {
+                       if (ret == LZMA_STREAM_END) {
+                               // lzma_stream_decoder() already guarantees
+                               // that there's no trailing garbage.
+                               assert(strm->avail_in == 0);
+                               //assert(action == LZMA_FINISH);
+                               return;
+                       }
+
+                       const char *msg;
+                       switch (ret) {
+                               case LZMA_MEM_ERROR:
+                                       msg = strerror(ENOMEM);
+                                       break;
+
+                               case LZMA_FORMAT_ERROR:
+                                       msg = "File format not recognized";
+                                       break;
+
+                               case LZMA_OPTIONS_ERROR:
+                                       // FIXME: Better message?
+                                       msg = "Unsupported compression options";
+                                       break;
+
+                               case LZMA_DATA_ERROR:
+                                       msg = "File is corrupt";
+                                       break;
+
+                               case LZMA_BUF_ERROR:
+                                       msg = "Unexpected end of input";
+                                       break;
+
+                               default:
+                                       msg = "Internal error (bug)";
+                                       break;
+                       }
+
+                       fprintf(stderr, "xz: %s\n", msg);
+                       exit(EXIT_FAILURE);
+               }
+       }
+}
+
+#define SQLERROR(x) fprintf(stderr, "%s %d: %s\n", __func__, __LINE__, (x))
+
+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;
+       }
+
+       rc = sqlite3_prepare(db, "select size, content from files where hash = ?", -1, &ifile,0);
+       if (rc != SQLITE_OK) {
+               SQLERROR(sqlite3_errmsg(db));
+               return 1;
+       }
+
+       /* hash, filename */
+       hash = av[2];
+       filename = av[3];
+
+       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 1;
+       }
+       /* either way we're done with this now */
+
+       if (rc != SQLITE_ROW) {
+               SQLERROR(sqlite3_errmsg(db));
+               sqlite3_finalize(ifile);
+               sqlite3_close(db);
+               return 2;
+       }
+
+       type = sqlite3_column_type(ifile, 0);
+       if (type == SQLITE_NULL) {
+               fprintf(stderr, "no file size\n");
+               sqlite3_finalize(ifile);
+               sqlite3_close(db);
+               return 3;
+       }
+       type = sqlite3_column_type(ifile, 1);
+       if (type == SQLITE_NULL) {
+               fprintf(stderr, "no file data\n");
+               sqlite3_finalize(ifile);
+               sqlite3_close(db);
+               return 4;
+       }
+       size = sqlite3_column_int64(ifile, 0);
+       xzdata = (void *)sqlite3_column_blob(ifile, 1);
+       blobsize = sqlite3_column_bytes(ifile, 1);
+
+       out = fopen(filename, "w");
+       if (!out) {
+               fprintf(stderr, "can't open output file %s\n", filename);
+               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);
+       sqlite3_close(db);
+       return 0;
+}
+void *compresslzma(void *buf, size_t bufsize, size_t *len) {
+       /* xz compress it */
+       size_t outsize;
+       void *outbuf;
+       size_t outlen = 0;
+
+       outsize = lzma_stream_buffer_bound(bufsize);
+       outbuf = malloc(outsize);
+       if (!outbuf) {
+               *len = 0;
+               return NULL;
+       }
+       /* TODO adjust encoding level for size */
+       lzma_easy_buffer_encode(6, LZMA_CHECK_CRC64, NULL,
+                       buf, bufsize,
+                       outbuf, &outlen, outsize
+                       );
+       *len = outlen;
+       return outbuf;
+}
+
+static int callback(void *NotUsed, int argc, char **argv, char **azColName){
+       int i;
+       for(i=0; i<argc; i++){
+               printf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
+       }
+       printf("\n");
+       return 0;
+}
+
+static char *create_table = "create table if not exists files (hash text primary key, size integer, compression text, content blob)";
+
+struct dbh {
+       sqlite3 *db;
+       char *errmsg;
+       int rc;
+};
+
+#define SQLERROR(x) fprintf(stderr, "%s %d: %s\n", __func__, __LINE__, (x))
+static int begin(sqlite3 *db) {
+       int rc;
+       char *err;
+
+       rc = sqlite3_exec(db, "begin;", callback, 0, &err);
+       if (rc != SQLITE_OK) {
+               SQLERROR(err);
+               sqlite3_free(err);
+       }
+       return rc;
+}
+
+static int commit(sqlite3 *db) {
+       int rc;
+       char *err;
+
+       rc = sqlite3_exec(db, "commit;", callback, 0, &err);
+       if (rc != SQLITE_OK) {
+               SQLERROR(err);
+               sqlite3_free(err);
+       }
+       return rc;
+}
+static int rollback(sqlite3 *db) {
+       int rc;
+       char *err;
+
+       rc = sqlite3_exec(db, "rollback;", callback, 0, &err);
+       if (rc != SQLITE_OK) {
+               SQLERROR(err);
+               sqlite3_free(err);
+       }
+       return rc;
+}
+
+int main(int ac, char **av){
+       sqlite3 *db = 0;
+       char *errmsg = 0;
+       int rc, i, j;
+
+       rc = sqlite3_open(av[1], &db);
+       if (rc) {
+               SQLERROR(sqlite3_errmsg(db));
+               sqlite3_close(db);
+               return 1;
+       }
+
+       rc = sqlite3_exec(db, create_table, callback, 0, &errmsg);
+       if (rc != SQLITE_OK) {
+               SQLERROR(errmsg);
+               sqlite3_free(errmsg);
+               sqlite3_close(db);
+               return 1;
+       }
+
+       sqlite3_stmt *ifile;
+
+       rc = sqlite3_prepare(db, "insert or ignore into files (hash,size,compression,content) values (?,?,?,?)", -1, &ifile,0);
+       if (rc != SQLITE_OK) {
+               SQLERROR(sqlite3_errmsg(db));
+               return 1;
+       }
+
+       begin(db);
+       /* insert each file */
+       for (i = 2; i < ac; i++) {
+               int fd;
+               void *content;
+               struct stat sbuf;
+               unsigned char tmp[32];
+               char hash[65];
+               hash_state md;
+
+               fd = open(av[i], O_RDONLY);
+               if (fd == -1) {
+                       rollback(db);
+                       sqlite3_close(db);
+                       exit(1);
+               }
+               if (fstat(fd, &sbuf) == -1) {
+                       rollback(db);
+                       sqlite3_close(db);
+                       exit(1);
+               }
+               /* not a regular file? */
+               if (!S_ISREG(sbuf.st_mode)) {
+                       rollback(db);
+                       sqlite3_close(db);
+                       exit(1);
+               }
+
+               content = mmap(0, sbuf.st_size, PROT_READ,MAP_PRIVATE, fd, 0);
+               if (!content) {
+                       rollback(db);
+                       sqlite3_close(db);
+                       exit(2);
+               }
+
+               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", av[i], hash);
+
+               void *xzcompressed;
+               size_t compresslen;
+
+               xzcompressed = compresslzma(content, sbuf.st_size, &compresslen);
+
+               sqlite3_bind_text(ifile, 1, hash, 64, SQLITE_STATIC);
+               sqlite3_bind_int64(ifile, 2, sbuf.st_size);
+               sqlite3_bind_text(ifile, 3, "xz", 2, SQLITE_STATIC);
+               sqlite3_bind_blob64(ifile, 4, xzcompressed, compresslen, SQLITE_STATIC);
+
+               rc = sqlite3_step(ifile);
+
+               /* either way we're done with this now */
+               munmap(content, sbuf.st_size);
+
+               if (rc != SQLITE_DONE) {
+                       SQLERROR(sqlite3_errmsg(db));
+                       sqlite3_finalize(ifile);
+                       rollback(db);
+                       sqlite3_close(db);
+                       return 1;
+               }
+               sqlite3_reset(ifile);
+               free(xzcompressed);
+               printf("%s\n", hash);
+       }
+       sqlite3_finalize(ifile);
+       commit(db);
+
+       sqlite3_close(db);
+       return 0;
+}
diff --git a/zpm-fileinfo b/zpm-fileinfo
new file mode 100755 (executable)
index 0000000..93326e5
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+package=${1:-$ZPMPACKAGE}
+shift
+pkgver=${ZPMPACKAGEVER:-1.0}
+pkgrel=${ZPMPACKAGEREL:-1}
+
+pkgroot=/
+
+# option for "multipackage" just to let the system know that's what you meant
+# option to take filenames from stdin
+# parse package, version, release from file if not given
+while getopts :f:v:r:d:a:u:l:p:b:P: opt; do
+       case $opt in
+               R) pkgroot="$OPTARG" ;;
+               S) format=shell ;;
+               f) pkgfile="$OPTARG" ;;
+               v) pkgver="$OPTARG" ;;
+               r) pkgrel="$OPTARG" ;;
+               d) description="$OPTARG" ;;
+               a) arch="$OPTARG" ;;
+               u) url="$OPTARG" ;;
+               l) licenses="$OPTARG" ;;
+               p) packager="$OPTARG" ;;
+               b) builddate="$OPTARG" ;;
+               P) prefix="$OPTARG" ;;
+       esac
+done
+
+set -e
+if [ -z "$pkgfile" ]; then
+       pkgfile="$package-$pkgver-$pkgrel.zpm"
+fi
+
+appid=$(sqlite3 $pkgfile 'pragma application_id;' | ( echo obase = 16; cat - ) | bc)
+if [ "$appid" != "5A504442" ]; then
+       echo $pkgfile does not appear to be a zpm package file
+       exit 1
+fi
+
+{
+sqlite3 $pkgfile <<EOS
+.mode line
+select * from packages where package = '$package' and version = '$pkgver' and release = $pkgrel
+;
+EOS
+} | sed -e 's/ = /=/' -e 's/^ \+//'
+
+exit 0
diff --git a/zpm-getlibs b/zpm-getlibs
new file mode 100755 (executable)
index 0000000..1e7fefc
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/sh
+
+# list a files libraries
+
+LD_TRACE_LOADED_OBJECTS=1  /lib/ld-linux-x86-64.so.2 
+
+verbose=0
+all=0
+script=0
+check=0
+
+while getopts avs arg; do
+       case $arg in
+               a) all=1;;
+               v) verbose=1;;
+       c) check=1;;
+       s) script=1;;
+       ?) printf 'usage: zpm-preserve [-av] <pkgname>\n'
+               exit 1
+               ;;
+       esac
+done
+
+pkgname="$1"
+
+pacman -Qlq "$pkgname" | while read file; do
+       if [ ! -f "$file" ]; then continue; fi
+       base=$(basename "$file")
+       if [ $all -eq 0 ] && [ "$base" = "${base#lib}" ]; then continue; fi
+
+       if [ $check -eq 1 ]; then
+               elftype -e "$file"
+               rv=$?
+               if [ $rv -ne 0 ]; then continue; fi
+               for lib in $(zpm getlibs $file | grep preserve); do
+                       echo $pkgname $file $lib
+               done
+               continue
+       fi
+
+       soname=$(soname $file)
+       if [ $? -eq 0 ] && [ -n "$soname" ]; then
+               dir=$(dirname "$file")
+               if [ "$script" -ne 0 ]; then
+                       printf 'mkdir -p "%s"\n' "$dir/preserve"
+                       printf 'ln -f "%s" "%s"\n' "$file" "$dir/preserve/"
+               else
+                       mkdir -p "$dir/preserve"
+                       if [ "$verbose" -gt 0 ]; then
+                               printf '# preserving %s\n' "$file"
+                       fi
+                       ln -f "$file" "$dir/preserve/"
+               fi
+       fi
+done
diff --git a/zpm-install b/zpm-install
new file mode 100755 (executable)
index 0000000..e722bf3
--- /dev/null
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+package=${1:-$ZPMPACKAGE}
+shift
+pkgver=${ZPMPACKAGEVER:-1.0}
+pkgrel=${ZPMPACKAGEREL:-1}
+
+pkgroot=/
+
+# option for "multipackage" just to let the system know that's what you meant
+# option to take filenames from stdin
+# parse package, version, release from file if not given
+while getopts :f:v:r:d:a:u:l:p:b:P: opt; do
+       case $opt in
+               R) pkgroot="$OPTARG" ;;
+               f) pkgfile="$OPTARG" ;;
+               v) pkgver="$OPTARG" ;;
+               r) pkgrel="$OPTARG" ;;
+               d) description="$OPTARG" ;;
+               a) arch="$OPTARG" ;;
+               u) url="$OPTARG" ;;
+               l) licenses="$OPTARG" ;;
+               p) packager="$OPTARG" ;;
+               b) builddate="$OPTARG" ;;
+               P) prefix="$OPTARG" ;;
+       esac
+done
+
+set -e
+if [ -z "$pkgfile" ]; then
+       pkgfile="$package-$pkgver-$pkgrel.zpm"
+fi
+
+appid=$(sqlite3 $pkgfile 'pragma application_id;' | ( echo obase = 16; cat - ) | bc)
+if [ "$appid" != "5A504442" ]; then
+       echo $pkgfile does not appear to be a zpm package file
+       exit 1
+fi
+
+# check if package exists
+# run preinstall or preupgrade stage, in chroot
+
+# add package info and mark installing to local package database
+# each path: add to local db, extract, set mode/owner/mtime etc.
+# mark install done in local database
+
+for path in $*; do
+       mtime=$(stat -c '%Y' $path)
+       uid=$(stat -c '%u' $path)
+       gid=$(stat -c '%g' $path)
+       username=$(stat -c '%U' $path)
+       groupname=$(stat -c '%G' $path)
+       mode=$(stat -c '%a' $path)
+
+       # strip off leading slashes
+       rpath=$(echo "$path" | sed -e 's|^/*||')
+       # and a leading ./
+       rpath=${rpath#./}
+       rpath=$(echo "$rpath" | sed -e 's|^/*||')
+
+       if [ -z "$rpath" ] || [ "$rpath" = '.' ]; then
+               continue
+       fi
+
+       if [ ! -z "$prefix" ]; then
+               # trailing slashes on prefix
+               prefix=$(echo "$prefix" | sed -e 's|/*$||')
+               rpath="$prefix/$rpath"
+       fi
+
+       if [ -f "$path" ]; then
+
+               hash=$(./zpm-addfile $pkgfile $path)
+
+#if [ -z "$hash" ]; then continue; fi
+
+# TODO mtime, mode
+sqlite3 $pkgfile <<EOS
+PRAGMA foreign_keys = ON;
+insert or replace into packagefiles (package,version,release,path,mode,mtime,username,groupname,hash)
+values ('$package', '$pkgver', $pkgrel, '$rpath', '$mode',$mtime, '$username','$groupname','$hash')
+;
+EOS
+elif [ -d "$path" ]; then
+sqlite3 $pkgfile <<EOS
+PRAGMA foreign_keys = ON;
+insert or replace into packagefiles (package,version,release,path,mode,mtime,username,groupname)
+values ('$package', '$pkgver', $pkgrel, '$rpath', '$mode',$mtime, '$username','$groupname')
+;
+EOS
+elif [ -l "$path" ]; then
+       target=$(readlink $path)
+sqlite3 $pkgfile <<EOS
+PRAGMA foreign_keys = ON;
+insert or replace into packagefiles (package,version,release,path,mode,mtime,username,groupname,target)
+values ('$package', '$pkgver', $pkgrel, '$rpath', '$mode',$mtime, '$username','$groupname','$target')
+;
+EOS
+fi
+#printf "%s %s%s\n" $path $rpath ${target:+" -> $target"}
+printf "%s\n" $path
+done
diff --git a/zpm-ipkgfile b/zpm-ipkgfile
new file mode 100755 (executable)
index 0000000..0f9440c
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+pkgfile=$1
+shift
+
+for path in $*; do
+       mkdir -p tmp/$(dirname $path)
+       fhash=$(sqlite3 $pkgfile "select hash from packagefiles where path = '$path';")
+       ./zpm-extract $pkgfile $fhash ./tmp/$path
+done
diff --git a/zpm-newpackage b/zpm-newpackage
new file mode 100755 (executable)
index 0000000..fbd44b5
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+package=${1:-$ZPMPACKAGE}
+pkgver=${ZPMPACKAGEVER:-1.0}
+pkgrel=${ZPMPACKAGEREL:-1}
+
+builddate=$(date '+%s')
+
+while getopts :f:v:r:d:a:u:l:p:b: opt; do
+       case $opt in
+               f) pkgfile="$OPTARG" ;;
+               v) pkgver="$OPTARG" ;;
+               r) pkgrel="$OPTARG" ;;
+               d) description="$OPTARG" ;;
+               a) arch="$OPTARG" ;;
+               u) url="$OPTARG" ;;
+               l) licenses="$OPTARG" ;;
+               p) packager="$OPTARG" ;;
+               b) builddate="$OPTARG" ;;
+       esac
+done
+
+set -e
+
+if [ -z "$pkgfile" ]; then
+       pkgfile="$package-$pkgver-$pkgrel.zpm"
+fi
+
+if [ ! -e $pkgfile ]; then
+       sqlite3 $pkgfile < db.sql
+fi
+
+sqlite3 $pkgfile <<EOS
+insert or ignore into packages ("package", "version", "release", "build_time")
+values ('$package', '$pkgver', $pkgrel, $builddate)
+;
+EOS
diff --git a/zpm-pkg b/zpm-pkg
new file mode 100755 (executable)
index 0000000..28a652c
--- /dev/null
+++ b/zpm-pkg
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+package=${1:-$ZPMPACKAGE}
+pkgver=${ZPMPACKAGEVER:-1.0}
+pkgrel=${ZPMPACKAGEREL:-1}
+
+while getopts :n:v:r:d:a:u:l:p:b: opt; do
+       case $opt in
+               n) pkgfile="$OPTARG" ;;
+               v) pkgver="$OPTARG" ;;
+               r) pkgrel="$OPTARG" ;;
+               d) description="$OPTARG" ;;
+               a) arch="$OPTARG" ;;
+               u) url="$OPTARG" ;;
+               l) licenses="$OPTARG" ;;
+               p) packager="$OPTARG" ;;
+               b) builddate="$OPTARG" ;;
+       esac
+done
+
+pkgfile="$package-$pkgver-$pkgrel.zpm"
+
+if [ ! -f $pkgfile ]; then
+       echo $pkgfile missing 1>&2
+       exit 1
+fi
+
+for path in $*; do
+hash=$(./zpm-addfile $pkgfile $path)
+
+sqlite3 $pkgfile <<EOS
+create table if not exists packagefiles (
+        package text,
+        subpackage      text, -- libs, dev, client, server, whatever
+       hash    text,
+        path    text,
+        filetype        text -- e.g. config, etc?
+);
+insert into packagefiles
+values ('$package', nullif('$subpackage', ''), '$hash', '$path', NULL)
+;
+EOS
+done
diff --git a/zpm-pkginfo b/zpm-pkginfo
new file mode 100755 (executable)
index 0000000..93326e5
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+package=${1:-$ZPMPACKAGE}
+shift
+pkgver=${ZPMPACKAGEVER:-1.0}
+pkgrel=${ZPMPACKAGEREL:-1}
+
+pkgroot=/
+
+# option for "multipackage" just to let the system know that's what you meant
+# option to take filenames from stdin
+# parse package, version, release from file if not given
+while getopts :f:v:r:d:a:u:l:p:b:P: opt; do
+       case $opt in
+               R) pkgroot="$OPTARG" ;;
+               S) format=shell ;;
+               f) pkgfile="$OPTARG" ;;
+               v) pkgver="$OPTARG" ;;
+               r) pkgrel="$OPTARG" ;;
+               d) description="$OPTARG" ;;
+               a) arch="$OPTARG" ;;
+               u) url="$OPTARG" ;;
+               l) licenses="$OPTARG" ;;
+               p) packager="$OPTARG" ;;
+               b) builddate="$OPTARG" ;;
+               P) prefix="$OPTARG" ;;
+       esac
+done
+
+set -e
+if [ -z "$pkgfile" ]; then
+       pkgfile="$package-$pkgver-$pkgrel.zpm"
+fi
+
+appid=$(sqlite3 $pkgfile 'pragma application_id;' | ( echo obase = 16; cat - ) | bc)
+if [ "$appid" != "5A504442" ]; then
+       echo $pkgfile does not appear to be a zpm package file
+       exit 1
+fi
+
+{
+sqlite3 $pkgfile <<EOS
+.mode line
+select * from packages where package = '$package' and version = '$pkgver' and release = $pkgrel
+;
+EOS
+} | sed -e 's/ = /=/' -e 's/^ \+//'
+
+exit 0
index f3086160ad78b21d5184428bf7a9a9869517db76..1f0e1d014b5216f185097e48e446c0b8f86267e1 100755 (executable)
@@ -3,24 +3,51 @@
 # preserve a packages libraries
 
 verbose=0
+all=0
+script=0
+check=0
 
-if [ "$1" = '-v' ]; then
-       verbose=1
-       shift
-fi
+while getopts avs arg; do
+       case $arg in
+               a) all=1;;
+               v) verbose=1;;
+       c) check=1;;
+       s) script=1;;
+       ?) printf 'usage: zpm-preserve [-av] <pkgname>\n'
+               exit 1
+               ;;
+       esac
+done
 
 pkgname="$1"
 
 pacman -Qlq "$pkgname" | while read file; do
        if [ ! -f "$file" ]; then continue; fi
+       base=$(basename "$file")
+       if [ $all -eq 0 ] && [ "$base" = "${base#lib}" ]; then continue; fi
+
+       if [ $check -eq 1 ]; then
+               elftype -e "$file"
+               rv=$?
+               if [ $rv -ne 0 ]; then continue; fi
+               for lib in $(zpm getlibs $file | grep preserve); do
+                       echo $pkgname $file $lib
+               done
+               continue
+       fi
 
        soname=$(soname $file)
        if [ $? -eq 0 ] && [ -n "$soname" ]; then
                dir=$(dirname "$file")
-               mkdir -p "$dir/preserve"
-               if [ "$verbose" -gt 0 ]; then
-                       printf 'preserving %s\n' "$file"
+               if [ "$script" -ne 0 ]; then
+                       printf 'mkdir -p "%s"\n' "$dir/preserve"
+                       printf 'ln -f "%s" "%s"\n' "$file" "$dir/preserve/"
+               else
+                       mkdir -p "$dir/preserve"
+                       if [ "$verbose" -gt 0 ]; then
+                               printf '# preserving %s\n' "$file"
+                       fi
+                       ln -f "$file" "$dir/preserve/"
                fi
-               ln -f "$file" "$dir/preserve/"
        fi
 done
diff --git a/zpm-showpkg b/zpm-showpkg
new file mode 100755 (executable)
index 0000000..2322f6f
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+{
+for pkgfile in $*; do
+
+       sqlite3 $pkgfile <<EOS
+.separator "\t"
+select package, path || case when hash is null then '/' else '' end from packagefiles
+;
+EOS
+
+done
+} | column -t
diff --git a/zpm.h b/zpm.h
new file mode 100644 (file)
index 0000000..3149fe7
--- /dev/null
+++ b/zpm.h
@@ -0,0 +1,93 @@
+#include <stdint.h>
+#include <time.h>
+#include <sqlite3.h>
+
+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;
+       char *tags;
+       char *owner;
+       char *group;
+       time_t mtime;
+       struct zpm_file *next; /* so you can make a linked list */
+};
+
+/* NULL?  Create? */
+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 import file to a particular package */
+#define ZPM_NOPACKAGE 0x20
+
+int zpm_import(struct zpm *zp, char *path, uint32_t flags, char *hash);
+
+/* 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);
+void *compresslzma(void *buf, size_t bufsize, size_t *len);
+#define SQLERROR(x) fprintf(stderr, "%s %d: %s\n", __func__, __LINE__, (x))