packager text,
build_time integer default (strftime('%s', 'now')),
install_time integer,
- checksum text, -- checksum of package contents. null for incompleted packages
+ hash text, -- see integ.c for package hash
primary key (package,version,release),
check (typeof(package) = 'text'),
check (typeof(version) = 'text'),
packager = NEW.packager,
build_time = NEW.build_time,
install_time = NEW.install_time,
- checksum = NEW.checksum
+ hash = NEW.hash
where package = OLD.package
and version = OLD.version
and release = OLD.release
foreign key (package,version,release) references packages (package,version,release) on delete cascade on update cascade
);
--- packagefile hash is columns as text, joined with null bytes, then
--- sha256 sum of that
--- package checksum is package columns as text, joined with null bytes,
--- other than the checksum and install_time column
--- then that hashed. finally, that hash, plus the ascii sorted
--- hashes of the package files all joined with newlines, hashed.
--- really don't like this.
-
-- files contained in a package
create table packagefiles (
-- package id triple
--- /dev/null
+#define _POSIX_C_SOURCE 200809L
+#include <string.h>
+
+#include "zpm.h"
+#include "sqlite3.h"
+#include "sha256.h"
+
+static void hash_byte(struct sha256_state *h, int ch) {
+ unsigned char buf[1];
+
+ buf[0] = ch & 0xff;
+ sha256_process(h, buf, 1);
+}
+
+/* i will be positive, we are hashing column sizes */
+static void hash_int(struct sha256_state *h, int i) {
+ int n;
+ uint64_t z;
+
+ z = i;
+
+ n = sizeof i;
+ while (n--) {
+ hash_byte(h, (int)(z & 0xff));
+ z = z>>8;
+ }
+}
+
+/*
+ * Implementation of the sha3_query(SQL,SIZE) function.
+ *
+ * This function compiles and runs the SQL statement(s) given in the argument.
+ * The results are hashed using a SIZE-bit SHA3. The default size is 256.
+ *
+ * The format of the byte stream that is hashed is summarized as follows:
+ *
+ * R
+ * N
+ * I<int>
+ * F<ieee-float>
+ * B<size>:<bytes>
+ * T<size>:<text>
+ *
+ * <sql> is the original SQL text for each statement run and <n> is the size of
+ * that text. The SQL text is UTF-8. A single R character occurs before the
+ * start of each row. N means a NULL value. I mean an 8-byte little-endian
+ * integer <int>. F is a floating point number with an 8-byte little-endian
+ * IEEE floating point value <ieee-float>. B means blobs of <size> bytes. T
+ * means text rendered as <size> bytes of UTF-8. The <size> values are
+ * expressed as little endian 8 byte integers.
+ *
+ *
+ * There are zero or more R segments, one for each row in the
+ * result set. After each R, there are one or more N, I, F, B, or T segments,
+ * one for each column in the result set. Segments are concatentated directly
+ * with no delimiters of any kind.
+ */
+
+static void hash_query(struct zpm *zpm, const char *zSql, struct sha256_state *h) {
+ sqlite3 *db;
+ sqlite3_stmt *pStmt = 0;
+ int nCol; /* Number of columns in the result set */
+ int i, rc;
+
+ const unsigned char *data;
+ int bytes;
+
+ double r;
+ sqlite3_uint64 u;
+ sqlite3_int64 v;
+ int j;
+ unsigned char x[9];
+
+ if (!zSql) return;
+ if (!zpm) return;
+
+ db = zpm->db;
+
+ rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql);
+ if (rc) {
+ zpm->dberrmsg = strdup(sqlite3_errmsg(db));
+ sqlite3_finalize(pStmt);
+ return;
+ }
+
+ nCol = sqlite3_column_count(pStmt);
+
+ while (sqlite3_step(pStmt) == SQLITE_ROW) {
+ sha256_process(h, (const unsigned char *)"R", 1);
+ for (i = 0; i < nCol; i++) {
+ switch (sqlite3_column_type(pStmt, i)) {
+ case SQLITE_NULL:
+ hash_byte(h, 'N');
+ continue;
+ break;
+ case SQLITE_INTEGER:
+ v = sqlite3_column_int64(pStmt, i);
+ memcpy(&u, &v, 8);
+ for (j = 8; j >= 1; j--) {
+ x[j] = u & 0xff;
+ u >>= 8;
+ }
+ x[0] = 'I';
+ data = x;
+ bytes = 9;
+ break;
+ case SQLITE_FLOAT:
+ r = sqlite3_column_double(pStmt, i);
+ memcpy(&u, &r, 8);
+ for (j = 8; j >= 1; j--) {
+ x[j] = u & 0xff;
+ u >>= 8;
+ }
+ x[0] = 'F';
+ data = x;
+ bytes = 9;
+ break;
+ case SQLITE_TEXT:
+ bytes = sqlite3_column_bytes(pStmt, i);
+ data = sqlite3_column_text(pStmt, i);
+ hash_byte(h, 'T');
+ hash_int(h, bytes);
+ break;
+ case SQLITE_BLOB:
+ bytes = sqlite3_column_bytes(pStmt, i);
+ data = sqlite3_column_blob(pStmt, i);
+ hash_byte(h, 'B');
+ hash_int(h, bytes);
+ break;
+ }
+ sha256_process(h, data, bytes);
+ }
+ }
+ sqlite3_finalize(pStmt);
+}
+
+int zpm_package_hash(struct zpm *zpm, char *pkgid, char *hash) {
+ struct sha256_state d;
+ char *sql;
+ int i;
+ unsigned char tmp[32];
+
+ if (!hash) {
+ return 0;
+ }
+
+ sha256_init(&d);
+ /* find package */
+
+ sql = sqlite3_mprintf("select package,version,release,description,architecture,url,licenses,packager,build_time from packages_pkgid where pkgid = %Q", pkgid);
+
+ hash_query(zpm, sql, &d);
+ sqlite3_free(sql);
+
+ /* hash package files */
+
+ sql = sqlite3_mprintf("select path, mode, username, groupname, configuration, "
+ "filetype, target, devmajor, devminor, mtime, hash "
+ "from packagefiles_pkgid where pkgid = %Q order by path",
+ pkgid);
+ hash_query(zpm, sql, &d);
+ sqlite3_free(sql);
+
+ sha256_done(&d, tmp);
+ for (i=0; i<32; i++) {
+ sprintf(hash+i*2, "%02x", (unsigned)tmp[i]);
+ }
+ hash[64] = 0;
+
+ return 1;
+
+}
+
+int zpm_package_sethash(struct zpm *zpm, char *pkgid, char *hash) {
+ char buf[ZPM_HASH_STRLEN + 1];
+ char *sql;
+
+ if (!hash) {
+ hash = buf;
+ }
+
+ zpm_package_hash(zpm, pkgid, hash);
+
+ sql = sqlite3_mprintf("update packages_pkgid set hash = %Q where pkgid = %Q", hash, pkgid);
+
+ zpm_exec(zpm, sql, NULL, NULL, NULL);
+ sqlite3_free(sql);
+
+ return 1;
+
+}
--- /dev/null
+#define _POSIX_C_SOURCE 2
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include "zpm.h"
+
+static int found = 0;
+
+void usage(void) {
+ fprintf(stderr, "zpm-findpkg [-I] [-s <status> ...] [-S <status>] [package]\n");
+}
+
+int main(int ac, char **av){
+ int opt;
+ struct zpm pkg;
+ char *dbfile;
+
+ int set = 0;
+
+ dbfile = getenv("ZPMDB");
+ if (!dbfile) {
+ dbfile = "/var/lib/zpm/local.db";
+ }
+
+ while ((opt = getopt(ac, av, "f:s")) != -1) {
+ switch (opt) {
+ case 'f': dbfile = optarg; break;
+ case 's': set = 1; break;
+ default:
+ usage();
+ exit(EXIT_FAILURE);
+ break;
+ }
+ }
+ int argn = optind;
+
+ if (!dbfile) {
+ fprintf(stderr, "must specify db\n");
+ return 1;
+ }
+
+ char *pkgid = av[argn];
+ char hash[ZPM_HASH_STRLEN+1];
+
+ if (zpm_open(&pkg, dbfile)) {
+ if (set) {
+ found = zpm_package_sethash(&pkg, pkgid, hash);
+ } else {
+ found = zpm_package_hash(&pkg, pkgid, hash);
+ }
+ }
+ zpm_close(&pkg);
+ if (found) {
+ printf("%s\n", hash);
+ }
+ return found ? 0 : 1;
+}