elftype
+*.o
+*.a
+zpm-addfile
+zpm-extract
+uncompress
+*.zpm
+*.db
+soname
+compress
--- /dev/null
+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
--- /dev/null
+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.
-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 $@ $+
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
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
--- /dev/null
+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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+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;
-/*
- * add a file/files to an sqlite db
- * in the 'files' table.
- */
-
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#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;
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#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_ */
--- /dev/null
+#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);
+ }
+ }
+}
--- /dev/null
+#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
--- /dev/null
+#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;
+}
--- /dev/null
+#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;
+}
--- /dev/null
+#!/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
--- /dev/null
+#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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
# 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
--- /dev/null
+#!/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
--- /dev/null
+#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))