#define _POSIX_C_SOURCE 200809L #include #include #include "zpm.h" #include "sqlite3.h" #include "blake2.h" static void hash_byte(struct blake2b_state__ *h, int ch) { unsigned char buf[1]; buf[0] = ch & 0xff; blake2b_update(h, buf, 1); } /* i will be positive, we are hashing column sizes */ static void hash_int(struct blake2b_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 * F * B: * T: * * is the original SQL text for each statement run and 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 . F is a floating point number with an 8-byte little-endian * IEEE floating point value . B means blobs of bytes. T * means text rendered as bytes of UTF-8. The 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 int hash_query(struct zpm *zpm, const char *zSql, struct blake2b_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 0; if (!zpm) return 0; db = zpm->db; rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zSql); if (rc) { zpm->dberrmsg = strdup(sqlite3_errmsg(db)); sqlite3_finalize(pStmt); return rc; } nCol = sqlite3_column_count(pStmt); while (sqlite3_step(pStmt) == SQLITE_ROW) { blake2b_update(h, "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; default: hash_byte(h, 'U'); continue; break; } blake2b_update(h, data, bytes); } } return sqlite3_finalize(pStmt); } int zpm_package_hash(struct zpm *zpm, char *pkgid, char *hash) { struct blake2b_state__ d; char *sql; int i; unsigned char tmp[32]; if (!hash) { return 0; } /* find package */ blake2b_init(&d, 32); sql = sqlite3_mprintf("select package,version,release,description,architecture,url,licenses,packager,build_time from packages_pkgid where pkgid = %Q", pkgid); i = hash_query(zpm, sql, &d); sqlite3_free(sql); if (i) { return i; } /* hash package files */ sql = sqlite3_mprintf("select path, mode, username, groupname, configuration, " "filetype, target, device, mtime, hash " "from packagefiles_pkgid where pkgid = %Q order by path", pkgid); i = hash_query(zpm, sql, &d); sqlite3_free(sql); if (i) { return i; } /* package dependencies */ sql = sqlite3_mprintf("select requires from packagedeps" " where printf('%%q-%%q-%%d',package,version,release) = %Q" " order by requires", pkgid); i = hash_query(zpm, sql, &d); sqlite3_free(sql); if (i) { return i; } /* package scripts */ sql = sqlite3_mprintf("select stage,hash from scripts" " where printf('%%q-%%q-%%d',package,version,release) = %Q" " order by stage,hash", pkgid); i = hash_query(zpm, sql, &d); sqlite3_free(sql); if (i) { return i; } blake2b_final(&d, tmp, sizeof tmp); for (i=0; i<32; i++) { sprintf(hash+i*2, "%02x", (unsigned)tmp[i]); } return 0; } int zpm_package_sethash(struct zpm *zpm, char *pkgid, char *hash) { char buf[ZPM_HASH_STRLEN + 1]; if (!hash) { hash = buf; } zpm_package_hash(zpm, pkgid, hash); hash[ZPM_HASH_STRLEN] = 0; return zpm_db_run(zpm, "update packages_pkgid set hash = %Q where pkgid = %Q", hash, pkgid); } int zpm_package_clearhash(struct zpm *zpm, char *pkgid) { if (!zpm || !zpm->db) { return 1; } return zpm_db_run(zpm, "update packages_pkgid set hash = NULL where pkgid = %Q", pkgid); } char *zpm_package_gethash(struct zpm *zpm, char *pkgid, char *hash) { char *saved; if (!zpm || !zpm->db || !pkgid) { return 0; } saved = zpm_db_string(zpm, "select hash from packages_pkgid where pkgid = %Q", pkgid); if (saved && hash) { strncpy(hash, saved, ZPM_HASH_STRLEN); free(saved); return hash; } return saved; } /* calc vs stored (hash == 0) * * otherwise check both. * 0 == match both * 1 = no match stored * 2 = no match calc * 3 = no match either * 4 = no such pkgid */ int zpm_package_checkhash(struct zpm *zpm, char *pkgid, char *hash) { /* check calculated hash against stored hash */ char calc[ZPM_HASH_STRLEN]; char set[ZPM_HASH_STRLEN]; char *current; int rv = 0; zpm_package_hash(zpm, pkgid, calc); current = zpm_package_gethash(zpm, pkgid, set); if (!current) { rv |= 1; } if (hash) { if (current && memcmp(hash, current, ZPM_HASH_STRLEN)) { rv |= 1; } if (memcmp(hash, calc, ZPM_HASH_STRLEN)) { rv |= 2; } } else { if (current) { rv = memcmp(calc, current, ZPM_HASH_STRLEN) ? 1 : 0; } } return rv; }