From ebf6f0b112dd1a1a57b1680bbe447b545671cf64 Mon Sep 17 00:00:00 2001 From: forrest_ly Date: Thu, 29 Jun 2023 13:17:31 +0800 Subject: [PATCH] support and switch to sqlite backend rpmdb --- 0001-support-sqlite-backend-rpmdb.patch | 2094 +++++++++++++++++++++++ rpm.spec | 22 +- 2 files changed, 2115 insertions(+), 1 deletion(-) create mode 100644 0001-support-sqlite-backend-rpmdb.patch diff --git a/0001-support-sqlite-backend-rpmdb.patch b/0001-support-sqlite-backend-rpmdb.patch new file mode 100644 index 0000000..395badd --- /dev/null +++ b/0001-support-sqlite-backend-rpmdb.patch @@ -0,0 +1,2094 @@ +From 955c0f001792deaa95913f5cd7ccaca2d8b0298e Mon Sep 17 00:00:00 2001 +From: forrest_ly +Date: Wed, 28 Jun 2023 16:50:14 +0800 +Subject: [PATCH] support sqlite backend rpmdb + +--- + configure.ac | 14 + + lib/Makefile.am | 4 + + lib/backend/bdb_ro.c | 816 +++++++++++++++++++++++++++++++++++++++++++ + lib/backend/db3.c | 69 ++-- + lib/backend/dbi.c | 139 ++++---- + lib/backend/dbi.h | 47 ++- + lib/backend/sqlite.c | 105 ++++-- + lib/rpmdb.c | 150 +++++--- + lib/rpmdb.h | 33 ++ + lib/rpmdb_internal.h | 10 +- + lib/rpmts.c | 8 +- + macros.in | 2 +- + rpmdb.c | 6 + + 13 files changed, 1212 insertions(+), 191 deletions(-) + create mode 100644 lib/backend/bdb_ro.c + +diff --git a/configure.ac b/configure.ac +index 3bd6382..a47aa46 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -610,6 +610,20 @@ AS_IF([test "$enable_ndb" = yes],[ + ]) + AM_CONDITIONAL([NDB], [test "$enable_ndb" = yes]) + ++#================= ++# Process --enable-bdb-ro ++AC_ARG_ENABLE([bdb-ro], [AS_HELP_STRING([--enable-bdb-ro (EXPERIMENTAL)],[enable the read-only Berkeley DB code])], ++[case "$enable_bdb_ro" in ++yes|no) ;; ++*) AC_MSG_ERROR([invalid argument to --enable-bdb-ro]) ++ ;; ++esac], ++[enable_bdb_ro=no]) ++AS_IF([test "$enable_bdb_ro" = yes],[ ++ AC_DEFINE(WITH_BDB_RO, 1, [Build with read-only Berkeley DB]) ++]) ++AM_CONDITIONAL([BDB_RO], [test "$enable_bdb_ro" = yes]) ++ + # Check for SQLITE support + AC_ARG_ENABLE([sqlite], + [AS_HELP_STRING([--enable-sqlite=@<:@yes/no/auto@:>@)], +diff --git a/lib/Makefile.am b/lib/Makefile.am +index 07c681e..b121f3d 100644 +--- a/lib/Makefile.am ++++ b/lib/Makefile.am +@@ -66,6 +66,10 @@ else + librpm_la_LIBADD += @WITH_DB_LIB@ + endif + ++if BDB_RO ++librpm_la_SOURCES += backend/bdb_ro.c ++endif ++ + if NDB + librpm_la_SOURCES += \ + backend/ndb/glue.c \ +diff --git a/lib/backend/bdb_ro.c b/lib/backend/bdb_ro.c +new file mode 100644 +index 0000000..2667ec8 +--- /dev/null ++++ b/lib/backend/bdb_ro.c +@@ -0,0 +1,816 @@ ++#include "system.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "lib/rpmdb_internal.h" ++#include ++#include ++ ++#define BDB_HASH 0 ++#define BDB_BTREE 1 ++ ++struct dbiCursor_s; ++ ++struct bdb_kv { ++ unsigned char *kv; ++ unsigned int len; ++}; ++ ++struct bdb_db { ++ int fd; /* file descriptor of database */ ++ int type; /* BDB_HASH / BDB_BTREE */ ++ unsigned int pagesize; ++ unsigned int lastpage; ++ int swapped; /* different endianess? */ ++ /* btree */ ++ unsigned int root; /* root page of the b-tree */ ++ /* hash */ ++ unsigned int maxbucket; ++ unsigned int highmask; ++ unsigned int lowmask; ++ unsigned int spares[32]; /* spare pages for each splitpoint */ ++}; ++ ++struct bdb_cur { ++ struct bdb_db *db; ++ ++ struct bdb_kv key; /* key and value from the db entry */ ++ struct bdb_kv val; ++ ++ unsigned char *page; /* the page we're looking at */ ++ ++ unsigned char *ovpage; ++ struct bdb_kv keyov; /* space to store oversized keys/values */ ++ struct bdb_kv valov; ++ ++ int state; /* 1: onpage, -1: error */ ++ int idx; /* entry index */ ++ int numidx; /* number of entries on the page */ ++ int islookup; /* we're doing a lookup operation */ ++ ++ /* hash */ ++ unsigned int bucket; /* current bucket */ ++}; ++ ++ ++static void swap16(unsigned char *p) ++{ ++ int a = p[0]; ++ p[0] = p[1]; ++ p[1] = a; ++} ++ ++static void swap32(unsigned char *p) ++{ ++ int a = p[0]; ++ p[0] = p[3]; ++ p[3] = a; ++ a = p[1]; ++ p[1] = p[2]; ++ p[2] = a; ++} ++ ++static void swap32_2(unsigned char *p) ++{ ++ swap32(p); ++ swap32(p + 4); ++} ++ ++static void bdb_swapmetapage(struct bdb_db *db, unsigned char *page) ++{ ++ int i, maxi = db->type == BDB_HASH ? 224 : 92; ++ for (i = 8; i < maxi; i += 4) ++ swap32((unsigned char *)(page + i)); ++ swap32((unsigned char *)(page + 24)); ++} ++ ++static void bdb_swappage(struct bdb_db *db, unsigned char *page) ++{ ++ unsigned int pagesize = db->pagesize; ++ int type, i, nent, off; ++ swap32(page + 8); /* page number */ ++ swap32_2(page + 12); /* prev/next page */ ++ swap16(page + 20); /* nitems */ ++ swap16(page + 22); /* highfree */ ++ ++ type = page[25]; ++ if (type != 2 && type != 13 && type != 3 && type != 5) ++ return; ++ nent = *(uint16_t *)(page + 20); ++ if (nent > (pagesize - 26) / 2) ++ nent = (pagesize - 26) / 2; ++ for (i = 0; i < nent; i++) { ++ int minoff = 26 + nent * 2; ++ swap16(page + 26 + i * 2); /* offset */ ++ off = *(uint16_t *)(page + 26 + i * 2); ++ if (off < minoff || off >= pagesize) ++ continue; ++ if (type == 2 || type == 13) { /* hash */ ++ if (page[off] == 3 && off + 12 <= pagesize) ++ swap32_2(page + off + 4); /* page no/length */ ++ } else if (type == 3) { /* btree internal */ ++ if (off + 12 > pagesize) ++ continue; ++ swap16(page + off); /* length */ ++ swap32_2(page + off + 4); /* page no/num recs */ ++ if (page[off + 2] == 3 && off + 24 <= pagesize) ++ swap32_2(page + off + 16); /* with overflow page/length */ ++ } else if (type == 5) { /* btree leaf */ ++ if (off + 3 <= pagesize && page[off + 2] == 1) ++ swap16(page + off); /* length */ ++ else if (off + 12 <= pagesize && page[off + 2] == 3) ++ swap32_2(page + off + 4); /* overflow page/length */ ++ } ++ } ++} ++ ++static int bdb_getpage(struct bdb_db *db, unsigned char *page, unsigned int pageno) ++{ ++ if (!pageno || pageno > db->lastpage) ++ return -1; ++ if (pread(db->fd, page, db->pagesize, (off_t)pageno * db->pagesize) != db->pagesize) { ++ rpmlog(RPMLOG_ERR, "pread: %s\n", strerror(errno)); ++ return -1; ++ } ++ if (db->swapped) ++ bdb_swappage(db, page); ++ if (pageno != *(uint32_t *)(page + 8)) ++ return -1; ++ return 0; ++} ++ ++static void bdb_close(struct bdb_db *db) ++{ ++ if (db->fd >= 0) ++ close(db->fd); ++ free(db); ++} ++ ++static struct bdb_db *bdb_open(const char *name) ++{ ++ uint32_t meta[512 / 4]; ++ int i, fd; ++ struct bdb_db *db; ++ ++ fd = open(name, O_RDONLY); ++ if (fd == -1) { ++ return NULL; ++ } ++ db = xcalloc(1, sizeof(*db)); ++ db->fd = fd; ++ if (pread(fd, meta, 512, 0) != 512) { ++ rpmlog(RPMLOG_ERR, "%s: pread: %s\n", name, strerror(errno)); ++ bdb_close(db); ++ return NULL; ++ } ++ if (meta[3] == 0x00061561 || meta[3] == 0x61150600) { ++ db->type = BDB_HASH; ++ db->swapped = meta[3] == 0x61150600; ++ } else if (meta[3] == 0x00053162 || meta[3] == 0x62310500) { ++ db->type = BDB_BTREE; ++ db->swapped = meta[3] == 0x62310500; ++ } else { ++ rpmlog(RPMLOG_ERR, "%s: not a berkeley db hash/btree database\n", name); ++ bdb_close(db); ++ return NULL; ++ } ++ if (db->swapped) ++ bdb_swapmetapage(db, (unsigned char *)meta); ++ db->pagesize = meta[5]; ++ db->lastpage = meta[8]; ++ if (db->type == BDB_HASH) { ++ if (meta[4] < 8 || meta[4] > 10) { ++ rpmlog(RPMLOG_ERR, "%s: unsupported hash version %d\n", name, meta[4]); ++ bdb_close(db); ++ return NULL; ++ } ++ db->maxbucket = meta[18]; ++ db->highmask = meta[19]; ++ db->lowmask = meta[20]; ++ for (i = 0; i < 32; i++) ++ db->spares[i] = meta[24 + i]; ++ } ++ if (db->type == BDB_BTREE) { ++ if (meta[4] < 9 || meta[4] > 10) { ++ rpmlog(RPMLOG_ERR, "%s: unsupported btree version %d\n", name, meta[4]); ++ bdb_close(db); ++ return NULL; ++ } ++ db->root = meta[22]; ++ } ++ return db; ++} ++ ++ ++/****** overflow handling ******/ ++ ++static int ovfl_get(struct bdb_cur *cur, struct bdb_kv *kv, struct bdb_kv *ov, uint32_t *pagenolen) ++{ ++ unsigned int pageno = pagenolen[0]; ++ unsigned int len = pagenolen[1]; ++ unsigned int plen; ++ unsigned char *p; ++ ++ if (len == 0) ++ return -1; ++ if (len > ov->len) { ++ if (ov->kv) ++ ov->kv = xrealloc(ov->kv, len); ++ else ++ ov->kv = xmalloc(len); ++ ov->len = len; ++ } ++ if (!cur->ovpage) ++ cur->ovpage = xmalloc(cur->db->pagesize); ++ p = ov->kv; ++ while (len > 0) { ++ if (bdb_getpage(cur->db, cur->ovpage, pageno)) ++ return -1; ++ if (cur->ovpage[25] != 7) ++ return -1; ++ plen = *(uint16_t *)(cur->ovpage + 22); ++ if (plen + 26 > cur->db->pagesize || plen > len) ++ return -1; ++ memcpy(p, cur->ovpage + 26, plen); ++ p += plen; ++ len -= plen; ++ pageno = *(uint32_t *)(cur->ovpage + 16); ++ } ++ if (kv) { ++ kv->kv = ov->kv; ++ kv->len = pagenolen[1]; ++ } ++ return 0; ++} ++ ++ ++/****** hash implementation ******/ ++ ++static int hash_bucket_to_page(struct bdb_db *db, unsigned int bucket) ++{ ++ unsigned int b; ++ int i = 0; ++ for (b = bucket; b; b >>= 1) ++ i++; ++ return bucket + db->spares[i]; ++} ++ ++static int hash_lookup(struct bdb_cur *cur, const unsigned char *key, unsigned int keyl) ++{ ++ uint32_t bucket; ++ unsigned int pg, i; ++ cur->state = -1; ++ for (bucket = 0, i = 0; i < keyl; i++) ++ bucket = (bucket * 16777619) ^ key[i]; ++ bucket &= cur->db->highmask; ++ if (bucket > cur->db->maxbucket) ++ bucket &= cur->db->lowmask; ++ cur->bucket = bucket; ++ pg = hash_bucket_to_page(cur->db, bucket); ++ if (bdb_getpage(cur->db, cur->page, pg)) ++ return -1; ++ if (cur->page[25] != 8 && cur->page[25] != 13) ++ return -1; ++ cur->idx = (unsigned int)-2; ++ cur->numidx = *(uint16_t *)(cur->page + 20); ++ cur->state = 1; ++ return 0; ++} ++ ++static int hash_getkv(struct bdb_cur *cur, struct bdb_kv *kv, struct bdb_kv *ov, int off, int len) ++{ ++ if (len <= 0 || off + len > cur->db->pagesize) ++ return -1; ++ if (cur->page[off] == 1) { ++ kv->kv = cur->page + off + 1; ++ kv->len = len - 1; ++ } else if (cur->page[off] == 3) { ++ uint32_t ovlpage[2]; ++ if (len != 12) ++ return -1; ++ memcpy(ovlpage, cur->page + off + 4, 8); /* off is unaligned */ ++ if (ovfl_get(cur, kv, ov, ovlpage)) ++ return -1; ++ } else { ++ return -1; ++ } ++ return 0; ++} ++ ++static int hash_next(struct bdb_cur *cur) ++{ ++ int pagesize = cur->db->pagesize; ++ int koff, klen, voff, vlen; ++ if (!cur->state && hash_lookup(cur, 0, 0)) ++ return -1; ++ cur->idx += 2; ++ for (;;) { ++ if (cur->idx + 1 >= cur->numidx) { ++ unsigned int pg; ++ cur->idx = cur->numidx = 0; ++ pg = *(uint32_t *)(cur->page + 16); ++ if (!pg) { ++ if (cur->islookup || cur->bucket >= cur->db->maxbucket) ++ return 1; ++ pg = hash_bucket_to_page(cur->db, ++cur->bucket); ++ } ++ if (bdb_getpage(cur->db, cur->page, pg)) ++ return -1; ++ if (cur->page[25] != 8 && cur->page[25] != 13) ++ return -1; ++ cur->numidx = *(uint16_t *)(cur->page + 20); ++ continue; ++ } ++ koff = *(uint16_t *)(cur->page + 26 + 2 * cur->idx); ++ voff = *(uint16_t *)(cur->page + 28 + 2 * cur->idx); ++ if (koff >= pagesize || voff >= pagesize) ++ return -1; ++ if (cur->idx == 0) ++ klen = pagesize - koff; ++ else ++ klen = *(uint16_t *)(cur->page + 24 + 2 * cur->idx) - koff; ++ vlen = koff - voff; ++ if (hash_getkv(cur, &cur->key, &cur->keyov, koff, klen)) ++ return -1; ++ if (!cur->islookup && hash_getkv(cur, &cur->val, &cur->valov, voff, vlen)) ++ return -1; ++ return 0; ++ } ++} ++ ++static int hash_getval(struct bdb_cur *cur) ++{ ++ int koff, voff; ++ if (cur->state != 1 || cur->idx + 1 >= cur->numidx) ++ return -1; ++ koff = *(uint16_t *)(cur->page + 26 + 2 * cur->idx); ++ voff = *(uint16_t *)(cur->page + 28 + 2 * cur->idx); ++ return hash_getkv(cur, &cur->val, &cur->valov, voff, koff - voff); ++} ++ ++ ++/****** btree implementation ******/ ++ ++static int btree_lookup(struct bdb_cur *cur, const unsigned char *key, unsigned int keylen) ++{ ++ int pagesize = cur->db->pagesize; ++ int off, lastoff, idx, numidx; ++ unsigned int pg; ++ unsigned char *ekey; ++ unsigned int ekeylen; ++ int cmp; ++ ++ cur->state = -1; ++ pg = cur->db->root; ++ for (;;) { ++ if (bdb_getpage(cur->db, cur->page, pg)) ++ return -1; ++ if (cur->page[25] == 5) ++ break; /* found leaf page */ ++ if (cur->page[25] != 3) ++ return -1; ++ numidx = *(uint16_t *)(cur->page + 20); ++ if (!numidx) ++ return -1; ++ for (lastoff = 0, idx = 0; idx < numidx; idx++, lastoff = off) { ++ off = *(uint16_t *)(cur->page + 26 + 2 * idx); ++ if ((off & 3) != 0 || off + 3 > pagesize) ++ return -1; ++ ekeylen = *(uint16_t *)(cur->page + off); ++ if (off + 12 + ekeylen > pagesize) ++ return -1; ++ if (!keylen) { ++ lastoff = off; ++ break; ++ } ++ if (idx == 0) ++ continue; ++ ekey = cur->page + off + 12; ++ if ((cur->page[off + 2] & 0x7f) == 3) { ++ if (ekeylen != 12) ++ return -1; ++ if (ovfl_get(cur, 0, &cur->keyov, (uint32_t *)(ekey + 4))) ++ return -1; ++ ekeylen = *(uint32_t *)(ekey + 8); ++ ekey = cur->keyov.kv; ++ } else if ((cur->page[off + 2] & 0x7f) != 1) { ++ return -1; ++ } ++ cmp = memcmp(ekey, key, keylen < ekeylen ? keylen : ekeylen); ++ if (cmp > 0 || (cmp == 0 && ekeylen > keylen)) ++ break; ++ } ++ pg = *(uint32_t *)(cur->page + lastoff + 4); ++ } ++ cur->idx = (unsigned int)-2; ++ cur->numidx = *(uint16_t *)(cur->page + 20); ++ cur->state = 1; ++ return 0; ++} ++ ++static int btree_getkv(struct bdb_cur *cur, struct bdb_kv *kv, struct bdb_kv *ov, int off) ++{ ++ if ((off & 3) != 0) ++ return -1; ++ if (cur->page[off + 2] == 1) { ++ int len = *(uint16_t *)(cur->page + off); ++ if (off + 3 + len > cur->db->pagesize) ++ return -1; ++ kv->kv = cur->page + off + 3; ++ kv->len = len; ++ } else if (cur->page[off + 2] == 3) { ++ if (off + 12 > cur->db->pagesize) ++ return -1; ++ if (ovfl_get(cur, kv, ov, (uint32_t *)(cur->page + off + 4))) ++ return -1; ++ } else { ++ return -1; ++ } ++ return 0; ++} ++ ++static int btree_next(struct bdb_cur *cur) ++{ ++ int pagesize = cur->db->pagesize; ++ int koff, voff; ++ if (!cur->state && btree_lookup(cur, 0, 0)) ++ return -1; ++ cur->idx += 2; ++ for (;;) { ++ if (cur->idx + 1 >= cur->numidx) { ++ unsigned int pg; ++ cur->idx = cur->numidx = 0; ++ pg = *(uint32_t *)(cur->page + 16); ++ if (cur->islookup || !pg) ++ return 1; ++ if (bdb_getpage(cur->db, cur->page, pg)) ++ return -1; ++ if (cur->page[25] != 5) ++ return -1; ++ cur->numidx = *(uint16_t *)(cur->page + 20); ++ continue; ++ } ++ koff = *(uint16_t *)(cur->page + 26 + 2 * cur->idx); ++ voff = *(uint16_t *)(cur->page + 28 + 2 * cur->idx); ++ if (koff + 3 > pagesize || voff + 3 > pagesize) ++ return -1; ++ if ((cur->page[koff + 2] & 0x80) != 0 || (cur->page[voff + 2] & 0x80) != 0) ++ continue; /* ignore deleted */ ++ if (btree_getkv(cur, &cur->key, &cur->keyov, koff)) ++ return -1; ++ if (!cur->islookup && btree_getkv(cur, &cur->val, &cur->valov, voff)) ++ return -1; ++ return 0; ++ } ++} ++ ++static int btree_getval(struct bdb_cur *cur) ++{ ++ int voff; ++ if (cur->state != 1 || cur->idx + 1 >= cur->numidx) ++ return -1; ++ voff = *(uint16_t *)(cur->page + 28 + 2 * cur->idx); ++ return btree_getkv(cur, &cur->val, &cur->valov, voff); ++} ++ ++ ++/****** cursor functions ******/ ++ ++static struct bdb_cur *cur_open(struct bdb_db *db) ++{ ++ struct bdb_cur *cur = xcalloc(1, sizeof(*cur)); ++ cur->db = db; ++ cur->page = xmalloc(db->pagesize); ++ return cur; ++} ++ ++static void cur_close(struct bdb_cur *cur) ++{ ++ if (cur->page) ++ free(cur->page); ++ if (cur->ovpage) ++ free(cur->ovpage); ++ if (cur->keyov.kv) ++ free(cur->keyov.kv); ++ if (cur->valov.kv) ++ free(cur->valov.kv); ++ free(cur); ++} ++ ++static int cur_next(struct bdb_cur *cur) ++{ ++ if (cur->state < 0) ++ return -1; ++ if (cur->db->type == BDB_HASH) ++ return hash_next(cur); ++ if (cur->db->type == BDB_BTREE) ++ return btree_next(cur); ++ return -1; ++} ++ ++static int cur_getval(struct bdb_cur *cur) ++{ ++ if (cur->state < 0) ++ return -1; ++ if (cur->db->type == BDB_HASH) ++ return hash_getval(cur); ++ if (cur->db->type == BDB_BTREE) ++ return btree_getval(cur); ++ return -1; ++} ++ ++static int cur_lookup(struct bdb_cur *cur, const unsigned char *key, unsigned int keyl) ++{ ++ int r = -1; ++ if (cur->db->type == BDB_HASH) ++ r = hash_lookup(cur, key, keyl); ++ if (cur->db->type == BDB_BTREE) ++ r = btree_lookup(cur, key, keyl); ++ if (r != 0) ++ return r; ++ cur->islookup = 1; ++ while ((r = cur_next(cur)) == 0) ++ if (keyl == cur->key.len && !memcmp(key, cur->key.kv, keyl)) ++ break; ++ cur->islookup = 0; ++ if (r == 0) ++ r = cur_getval(cur); ++ return r; ++} ++ ++static int cur_lookup_ge(struct bdb_cur *cur, const unsigned char *key, unsigned int keyl) ++{ ++ int r = -1; ++ if (cur->db->type == BDB_BTREE) ++ r = btree_lookup(cur, key, keyl); ++ if (r != 0) ++ return r; ++ cur->islookup = 1; ++ while ((r = cur_next(cur)) == 0) { ++ unsigned int ekeyl = cur->key.len; ++ int cmp = memcmp(cur->key.kv, key, keyl < ekeyl ? keyl : ekeyl); ++ if (cmp > 0 || (cmp == 0 && ekeyl >= keyl)) ++ break; ++ } ++ cur->islookup = 0; ++ if (r == 0) ++ r = cur_getval(cur); ++ else if (r == 1) ++ r = cur_next(cur); ++ return r; ++} ++ ++/****** glue code ******/ ++ ++static unsigned int getui32(unsigned char *x, int swapped) ++{ ++ union _dbswap bs; ++ memcpy(bs.uc, x, 4); ++ if (swapped) ++ _DBSWAP(bs); ++ return bs.ui; ++} ++ ++static void setui32(unsigned char *x, uint32_t v, int swapped) ++{ ++ union _dbswap bs; ++ bs.ui = v; ++ if (swapped) ++ _DBSWAP(bs); ++ memcpy(x, bs.uc, 4); ++} ++ ++static void log_error(dbiIndex dbi) ++{ ++ rpmlog(RPMLOG_ERR, "bdb_ro error reading %s database\n", dbi->dbi_file); ++} ++ ++static int bdbro_Open(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags) ++{ ++ const char *dbhome = rpmdbHome(rdb); ++ dbiIndex dbi = NULL; ++ char *path; ++ ++ if (dbip) ++ *dbip = NULL; ++ if ((rdb->db_mode & O_ACCMODE) != O_RDONLY) ++ return EPERM; ++ if ((dbi = dbiNew(rdb, rpmtag)) == NULL) ++ return 1; ++ path = rstrscat(NULL, dbhome, "/", dbi->dbi_file, NULL); ++ rpmlog(RPMLOG_DEBUG, "opening db index %s\n", path); ++ dbi->dbi_db = bdb_open(path); ++ if (!dbi->dbi_db) { ++ int lvl = (dbi->dbi_type == DBI_PRIMARY) ? RPMLOG_ERR : RPMLOG_WARNING; ++ rpmlog(lvl, "could not open %s: %s\n", path, strerror(errno)); ++ if (dbi->dbi_type == DBI_PRIMARY) { ++ free(path); ++ dbiFree(dbi); ++ return 1; ++ } ++ } ++ free(path); ++ dbi->dbi_flags |= DBI_RDONLY; ++ if (dbip) ++ *dbip = dbi; ++ else ++ (void) dbiClose(dbi, 0); ++ return 0; ++} ++ ++static int bdbro_Close(dbiIndex dbi, unsigned int flags) ++{ ++ if (dbi->dbi_db) ++ bdb_close(dbi->dbi_db); ++ dbiFree(dbi); ++ return 0; ++} ++ ++static int bdbro_Verify(dbiIndex dbi, unsigned int flags) ++{ ++ return 0; ++} ++ ++static void bdbro_SetFSync(rpmdb rdb, int enable) ++{ ++} ++ ++static int bdbro_Ctrl(rpmdb rdb, dbCtrlOp ctrl) ++{ ++ return 0; ++} ++ ++static dbiCursor bdbro_CursorInit(dbiIndex dbi, unsigned int flags) ++{ ++ /* Secondary indexes may be missing */ ++ return (dbi && dbi->dbi_db) ? (void *)cur_open(dbi->dbi_db) : NULL; ++} ++ ++static dbiCursor bdbro_CursorFree(dbiIndex dbi, dbiCursor dbc) ++{ ++ if (dbc) ++ cur_close((void *)dbc); ++ return NULL; ++} ++ ++static void appenddbt(dbiCursor dbc, unsigned char *val, unsigned int vallen, dbiIndexSet *setp) ++{ ++ struct bdb_cur *cur = (void *)dbc; ++ dbiIndexSet set; ++ unsigned int i; ++ ++ set = dbiIndexSetNew(vallen / (2 * sizeof(uint32_t))); ++ set->count = vallen / (2 * sizeof(uint32_t)); ++ ++ for (i = 0; i < set->count; i++, val += 8) { ++ set->recs[i].hdrNum = getui32(val, cur->db->swapped); ++ set->recs[i].tagNum = getui32(val + 4, cur->db->swapped); ++ } ++ if (*setp == NULL) { ++ *setp = set; ++ } else { ++ dbiIndexSetAppendSet(*setp, set, 0); ++ dbiIndexSetFree(set); ++ } ++} ++ ++static rpmRC bdbro_idxdbPut(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h) ++{ ++ return RPMRC_FAIL; ++} ++ ++static rpmRC bdbro_idxdbDel(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h) ++{ ++ return RPMRC_FAIL; ++} ++ ++static rpmRC bdbro_idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, ++ dbiIndexSet *set, int searchType) ++{ ++ struct bdb_cur *cur = (void *)dbc; ++ int r; ++ ++ if (!cur) ++ return RPMRC_FAIL; ++ if (searchType == DBC_PREFIX_SEARCH) { ++ rpmRC rc = RPMRC_NOTFOUND; ++ if (!keyp) ++ return RPMRC_FAIL; ++ r = cur_lookup_ge(cur, (const unsigned char *)keyp, keylen); ++ for (; r == 0; r = cur_next(cur)) { ++ if (cur->key.len < keylen || memcmp(cur->key.kv, keyp, keylen) != 0) ++ break; ++ if (set) ++ appenddbt(dbc, cur->val.kv, cur->val.len, set); ++ rc = RPMRC_OK; ++ } ++ if (r == -1) ++ log_error(dbi); ++ cur->key.kv = 0; ++ return r == -1 ? RPMRC_FAIL : rc; ++ } ++ if (keyp) { ++ if (keylen == 0) { ++ keyp = ""; ++ keylen = 1; ++ } ++ r = cur_lookup(cur, (const unsigned char *)keyp, keylen); ++ } else { ++ r = cur_next(cur); ++ } ++ if (r == 0) { ++ if (set) ++ appenddbt(dbc, cur->val.kv, cur->val.len, set); ++ return RPMRC_OK; ++ } ++ if (r == -1) ++ log_error(dbi); ++ cur->key.kv = 0; ++ return r == 1 ? RPMRC_NOTFOUND : RPMRC_FAIL; ++} ++ ++static const void *bdbro_idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen) ++{ ++ struct bdb_cur *cur = (void *)dbc; ++ if (!cur || !cur->key.kv) ++ return 0; ++ if (keylen) ++ *keylen = cur->key.len; ++ return cur->key.kv; ++} ++ ++static rpmRC bdbro_pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum, ++ unsigned char *hdrBlob, unsigned int hdrLen) ++{ ++ return RPMRC_FAIL; ++} ++ ++static rpmRC bdbro_pkgdbDel(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum) ++{ ++ return RPMRC_FAIL; ++} ++ ++static rpmRC bdbro_pkgdbGet(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, ++ unsigned char **hdrBlob, unsigned int *hdrLen) ++{ ++ struct bdb_cur *cur = (void *)dbc; ++ int r; ++ if (hdrNum) { ++ unsigned char hdrkey[4]; ++ setui32(hdrkey, hdrNum, cur->db->swapped); ++ r = cur_lookup(cur, hdrkey, 4); ++ } else { ++ r = cur_next(cur); ++ } ++ if (r == 0) { ++ if (hdrBlob) ++ *hdrBlob = cur->val.kv; ++ if (hdrLen) ++ *hdrLen = cur->val.len; ++ return RPMRC_OK; ++ } ++ if (r == -1) ++ log_error(dbi); ++ cur->key.kv = 0; ++ return r == 1 ? RPMRC_NOTFOUND : RPMRC_FAIL; ++} ++ ++static unsigned int bdbro_pkgdbKey(dbiIndex dbi, dbiCursor dbc) ++{ ++ struct bdb_cur *cur = (void *)dbc; ++ if (!cur || !cur->key.kv || cur->key.len != 4) ++ return 0; ++ return getui32(cur->key.kv, cur->db->swapped); ++} ++ ++struct rpmdbOps_s bdbro_dbops = { ++ .name = "bdb_ro", ++ .path = "Packages", ++ ++ .open = bdbro_Open, ++ .close = bdbro_Close, ++ .verify = bdbro_Verify, ++ .setFSync = bdbro_SetFSync, ++ .ctrl = bdbro_Ctrl, ++ ++ .cursorInit = bdbro_CursorInit, ++ .cursorFree = bdbro_CursorFree, ++ ++ .pkgdbPut = bdbro_pkgdbPut, ++ .pkgdbDel = bdbro_pkgdbDel, ++ .pkgdbGet = bdbro_pkgdbGet, ++ .pkgdbKey = bdbro_pkgdbKey, ++ ++ .idxdbGet = bdbro_idxdbGet, ++ .idxdbPut = bdbro_idxdbPut, ++ .idxdbDel = bdbro_idxdbDel, ++ .idxdbKey = bdbro_idxdbKey ++}; ++ +diff --git a/lib/backend/db3.c b/lib/backend/db3.c +index da50dfd..68cfa6f 100644 +--- a/lib/backend/db3.c ++++ b/lib/backend/db3.c +@@ -11,6 +11,7 @@ static int _debug = 1; /* XXX if < 0 debugging, > 0 unusual error returns */ + #include + #include + #include ++#include + + #include + #include +@@ -415,10 +416,6 @@ static int db_init(rpmdb rdb, const char * dbhome) + if (rdb->db_dbenv != NULL) { + rdb->db_opens++; + return 0; +- } else { +- /* On first call, set backend description to something... */ +- free(rdb->db_descr); +- rasprintf(&rdb->db_descr, "db%u", DB_VERSION_MAJOR); + } + + /* +@@ -1012,7 +1009,7 @@ static rpmRC db3_idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t + dbiIndexSet *set, int searchType) + { + rpmRC rc = RPMRC_FAIL; /* assume failure */ +- if (dbi != NULL && dbc != NULL && set != NULL) { ++ if (dbi != NULL && dbc != NULL) { + int cflags = DB_NEXT; + int dbrc; + DBT data, key; +@@ -1037,12 +1034,14 @@ static rpmRC db3_idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t + if (searchType == DBC_PREFIX_SEARCH && + (key.size < keylen || memcmp(key.data, keyp, keylen) != 0)) + break; +- dbt2set(dbi, &data, &newset); +- if (*set == NULL) { +- *set = newset; +- } else { +- dbiIndexSetAppendSet(*set, newset, 0); +- dbiIndexSetFree(newset); ++ if (set) { ++ dbt2set(dbi, &data, &newset); ++ if (*set == NULL) { ++ *set = newset; ++ } else { ++ dbiIndexSetAppendSet(*set, newset, 0); ++ dbiIndexSetFree(newset); ++ } + } + if (searchType != DBC_PREFIX_SEARCH) + break; +@@ -1052,7 +1051,7 @@ static rpmRC db3_idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t + } + + /* fixup result status for prefix search */ +- if (searchType == DBC_PREFIX_SEARCH) { ++ if (searchType == DBC_PREFIX_SEARCH && set) { + if (dbrc == DB_NOTFOUND && *set != NULL && (*set)->count > 0) + dbrc = 0; + else if (dbrc == 0 && (*set == NULL || (*set)->count == 0)) +@@ -1115,7 +1114,7 @@ static rpmRC updateIndex(dbiCursor dbc, const char *keyp, unsigned int keylen, + return rc; + } + +-static rpmRC db3_idxdbPut(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, ++static rpmRC db3_idxdbPutOne(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, + dbiIndexItem rec) + { + dbiIndexSet set = NULL; +@@ -1141,7 +1140,12 @@ static rpmRC db3_idxdbPut(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t + return rc; + } + +-static rpmRC db3_idxdbDel(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, ++static rpmRC db3_idxdbPut(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h) ++{ ++ return tag2index(dbi, rpmtag, hdrNum, h, db3_idxdbPutOne); ++} ++ ++static rpmRC db3_idxdbDelOne(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, + dbiIndexItem rec) + { + dbiIndexSet set = NULL; +@@ -1171,6 +1175,11 @@ static rpmRC db3_idxdbDel(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t + return rc; + } + ++static rpmRC db3_idxdbDel(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h) ++{ ++ return tag2index(dbi, rpmtag, hdrNum, h, db3_idxdbDelOne); ++} ++ + static const void * db3_idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen) + { + const void *key = NULL; +@@ -1278,14 +1287,26 @@ static unsigned int pkgInstance(dbiIndex dbi, int alloc) + return hdrNum; + } + +-static rpmRC db3_pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, ++static rpmRC db3_pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum, + unsigned char *hdrBlob, unsigned int hdrLen) + { ++ rpmRC rc = RPMRC_FAIL; ++ unsigned int hnum = *hdrNum; + DBT hdr; + memset(&hdr, 0, sizeof(hdr)); + hdr.data = hdrBlob; + hdr.size = hdrLen; +- return updatePackages(dbc, hdrNum, &hdr); ++ ++ if (hnum == 0) ++ hnum = pkgInstance(dbi, 1); ++ ++ if (hnum) ++ rc = updatePackages(dbc, hnum, &hdr); ++ ++ if (rc == RPMRC_OK) ++ *hdrNum = hnum; ++ ++ return rc; + } + + static rpmRC db3_pkgdbDel(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum) +@@ -1342,19 +1363,10 @@ static unsigned int db3_pkgdbKey(dbiIndex dbi, dbiCursor dbc) + return mi_offset.ui; + } + +-static rpmRC db3_pkgdbNew(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum) +-{ +- unsigned int num; +- if (dbc == NULL) +- return RPMRC_FAIL; +- num = pkgInstance(dbc->dbi, 1); +- if (!num) +- return RPMRC_FAIL; +- *hdrNum = num; +- return RPMRC_OK; +-} +- + struct rpmdbOps_s db3_dbops = { ++ .name = "bdb", ++ .path = "Packages", ++ + .open = db3_dbiOpen, + .close = db3_dbiClose, + .verify = db3_dbiVerify, +@@ -1368,7 +1380,6 @@ struct rpmdbOps_s db3_dbops = { + .pkgdbGet = db3_pkgdbGet, + .pkgdbPut = db3_pkgdbPut, + .pkgdbDel = db3_pkgdbDel, +- .pkgdbNew = db3_pkgdbNew, + .pkgdbKey = db3_pkgdbKey, + + .idxdbGet = db3_idxdbGet, +diff --git a/lib/backend/dbi.c b/lib/backend/dbi.c +index dc3587f..9e714d7 100644 +--- a/lib/backend/dbi.c ++++ b/lib/backend/dbi.c +@@ -5,6 +5,7 @@ + #include "system.h" + + #include ++#include + #include + #include + #include +@@ -12,6 +13,21 @@ + #include "lib/rpmdb_internal.h" + #include "debug.h" + ++const struct rpmdbOps_s *backends[] = { ++#if defined(WITH_SQLITE) ++ &sqlite_dbops, ++#endif ++#ifdef ENABLE_NDB ++ &ndb_dbops, ++#endif ++#if defined(WITH_BDB) ++ &db3_dbops, ++#endif ++#if defined(WITH_BDB_RO) ++ &bdbro_dbops, ++#endif ++ NULL ++}; + + dbiIndex dbiFree(dbiIndex dbi) + { +@@ -32,69 +48,77 @@ dbiIndex dbiNew(rpmdb rdb, rpmDbiTagVal rpmtag) + return dbi; + } + ++/* Test whether there's a database for this backend, return true/false */ ++static int tryBackend(const char *dbhome, const struct rpmdbOps_s *be) ++{ ++ int rc = 0; ++ if (be && be->path) { ++ char *path = rstrscat(NULL, dbhome, "/", be->path, NULL); ++ rc = (access(path, F_OK) == 0); ++ free(path); ++ } ++ return rc; ++} ++ + static void + dbDetectBackend(rpmdb rdb) + { + const char *dbhome = rpmdbHome(rdb); + char *db_backend = rpmExpand("%{?_db_backend}", NULL); +- char *path = NULL; +- +-#if defined(WITH_LMDB) +- if (!strcmp(db_backend, "lmdb")) { +- rdb->db_ops = &lmdb_dbops; +- } else +-#endif +-#ifdef ENABLE_NDB +- if (!strcmp(db_backend, "ndb")) { +- rdb->db_ops = &ndb_dbops; +- } else +-#endif +-#ifdef WITH_SQLITE +- if (!strcmp(db_backend, "sqlite")) { +- rdb->db_ops = &sqlite_dbops; +- } else +-#endif +- { +- rdb->db_ops = &db3_dbops; +- if (*db_backend == '\0') { +- free(db_backend); +- db_backend = xstrdup("bdb"); ++ const struct rpmdbOps_s **ops; ++ const struct rpmdbOps_s *cfg = NULL; ++ const struct rpmdbOps_s *ondisk = NULL; ++ ++ /* Find configured backend */ ++ for (ops = backends; ops && *ops; ops++) { ++ if (rstreq(db_backend, (*ops)->name)) { ++ cfg = *ops; ++ break; + } + } + +-#if defined(WITH_LMDB) +- path = rstrscat(NULL, dbhome, "/data.mdb", NULL); +- if (access(path, F_OK) == 0 && rdb->db_ops != &lmdb_dbops) { +- rdb->db_ops = &lmdb_dbops; +- rpmlog(RPMLOG_WARNING, _("Found LMDB data.mdb database while attempting %s backend: using lmdb backend.\n"), db_backend); ++ if (!cfg && ((rdb->db_mode & O_ACCMODE) != O_RDONLY || (rdb->db_flags & RPMDB_FLAG_REBUILD) != 0)) { ++ rpmlog(RPMLOG_WARNING, _("invalid %%_db_backend: %s\n"), db_backend); ++ goto exit; + } +- free(path); +-#endif + +-#ifdef ENABLE_NDB +- path = rstrscat(NULL, dbhome, "/Packages.db", NULL); +- if (access(path, F_OK) == 0 && rdb->db_ops != &ndb_dbops) { +- rdb->db_ops = &ndb_dbops; +- rpmlog(RPMLOG_WARNING, _("Found NDB Packages.db database while attempting %s backend: using ndb backend.\n"), db_backend); +- } +- free(path); +-#endif ++ /* If configured database doesn't exist, try autodetection */ ++ if (!tryBackend(dbhome, cfg)) { ++ for (ops = backends; ops && *ops; ops++) { ++ if (tryBackend(dbhome, *ops)) { ++ ondisk = *ops; ++ break; ++ } ++ } + +-#ifdef WITH_SQLITE +- path = rstrscat(NULL, dbhome, "/rpmdb.sqlite", NULL); +- if (access(path, F_OK) == 0 && rdb->db_ops != &sqlite_dbops) { +- rdb->db_ops = &sqlite_dbops; +- rpmlog(RPMLOG_WARNING, _("Found SQLITE rpmdb.sqlite database while attempting %s backend: using sqlite backend.\n"), db_backend); ++ /* On-disk database differs from configuration */ ++ if (ondisk && ondisk != cfg) { ++ if (*db_backend) { ++ if (rdb->db_flags & RPMDB_FLAG_REBUILD) { ++ rpmlog(RPMLOG_WARNING, ++ _("Converting database from %s to %s backend\n"), ++ ondisk->name, db_backend); ++ } else { ++ rpmlog(RPMLOG_WARNING, ++ _("Found %s %s database while attempting %s backend: " ++ "using %s backend.\n"), ++ ondisk->name, ondisk->path, db_backend, ondisk->name); ++ } ++ } else { ++ rpmlog(RPMLOG_DEBUG, "Found %s %s database: using %s backend.\n", ++ ondisk->name, ondisk->path, ondisk->name); ++ } ++ rdb->db_ops = ondisk; ++ } + } +- free(path); +-#endif + +- path = rstrscat(NULL, dbhome, "/Packages", NULL); +- if (access(path, F_OK) == 0 && rdb->db_ops != &db3_dbops) { +- rdb->db_ops = &db3_dbops; +- rpmlog(RPMLOG_WARNING, _("Found BDB Packages database while attempting %s backend: using bdb backend.\n"), db_backend); +- } +- free(path); ++ /* Newly created database, use configured backend */ ++ if (rdb->db_ops == NULL && cfg) ++ rdb->db_ops = cfg; ++ ++exit: ++ ++ rdb->db_descr = rdb->db_ops->name; + + if (db_backend) + free(db_backend); +@@ -151,7 +175,7 @@ dbiCursor dbiCursorFree(dbiIndex dbi, dbiCursor dbc) + return dbi->dbi_rpmdb->db_ops->cursorFree(dbi, dbc); + } + +-rpmRC pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, unsigned char *hdrBlob, unsigned int hdrLen) ++rpmRC pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum, unsigned char *hdrBlob, unsigned int hdrLen) + { + return dbi->dbi_rpmdb->db_ops->pkgdbPut(dbi, dbc, hdrNum, hdrBlob, hdrLen); + } +@@ -166,11 +190,6 @@ rpmRC pkgdbGet(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, unsigned char * + return dbi->dbi_rpmdb->db_ops->pkgdbGet(dbi, dbc, hdrNum, hdrBlob, hdrLen); + } + +-rpmRC pkgdbNew(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum) +-{ +- return dbi->dbi_rpmdb->db_ops->pkgdbNew(dbi, dbc, hdrNum); +-} +- + unsigned int pkgdbKey(dbiIndex dbi, dbiCursor dbc) + { + return dbi->dbi_rpmdb->db_ops->pkgdbKey(dbi, dbc); +@@ -181,14 +200,14 @@ rpmRC idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbi + return dbi->dbi_rpmdb->db_ops->idxdbGet(dbi, dbc, keyp, keylen, set, curFlags); + } + +-rpmRC idxdbPut(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexItem rec) ++rpmRC idxdbPut(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h) + { +- return dbi->dbi_rpmdb->db_ops->idxdbPut(dbi, dbc, keyp, keylen, rec); ++ return dbi->dbi_rpmdb->db_ops->idxdbPut(dbi, rpmtag, hdrNum, h); + } + +-rpmRC idxdbDel(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexItem rec) ++rpmRC idxdbDel(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h) + { +- return dbi->dbi_rpmdb->db_ops->idxdbDel(dbi, dbc, keyp, keylen, rec); ++ return dbi->dbi_rpmdb->db_ops->idxdbDel(dbi, rpmtag, hdrNum, h); + } + + const void * idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen) +diff --git a/lib/backend/dbi.h b/lib/backend/dbi.h +index ff2b4f9..fba2f04 100644 +--- a/lib/backend/dbi.h ++++ b/lib/backend/dbi.h +@@ -10,6 +10,7 @@ enum rpmdbFlags { + RPMDB_FLAG_JUSTCHECK = (1 << 0), + RPMDB_FLAG_REBUILD = (1 << 1), + RPMDB_FLAG_VERIFYONLY = (1 << 2), ++ RPMDB_FLAG_SALVAGE = (1 << 3), + }; + + typedef enum dbCtrlOp_e { +@@ -49,7 +50,7 @@ struct rpmdb_s { + int db_flags; + int db_mode; /*!< open mode */ + int db_perms; /*!< open permissions */ +- char * db_descr; /*!< db backend description (for error msgs) */ ++ const char * db_descr; /*!< db backend description (for error msgs) */ + struct dbChk_s * db_checked;/*!< headerCheck()'ed package instances */ + rpmdb db_next; + int db_opens; +@@ -59,10 +60,11 @@ struct rpmdb_s { + dbiIndex * db_indexes; /*!< Tag indices. */ + int db_buildindex; /*!< Index rebuild indicator */ + +- struct rpmdbOps_s * db_ops; /*!< backend ops */ ++ const struct rpmdbOps_s * db_ops; /*!< backend ops */ + + /* dbenv and related parameters */ + void * db_dbenv; /*!< Backend private handle */ ++ void * db_cache; /*!< Backend private cache handle */ + struct dbConfig_s cfg; + int db_remove_env; + +@@ -123,10 +125,16 @@ union _dbswap { + \ + } + ++typedef rpmRC (*idxfunc)(dbiIndex dbi, dbiCursor dbc, ++ const char *keyp, size_t keylen, dbiIndexItem rec); ++ + #ifdef __cplusplus + extern "C" { + #endif + ++RPM_GNUC_INTERNAL ++rpmRC tag2index(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h, ++ idxfunc idxupdate); + + RPM_GNUC_INTERNAL + /* Globally enable/disable fsync in the backend */ +@@ -216,7 +224,7 @@ dbiCursor dbiCursorFree(dbiIndex dbi, dbiCursor dbc); + + + RPM_GNUC_INTERNAL +-rpmRC pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, ++rpmRC pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum, + unsigned char *hdrBlob, unsigned int hdrLen); + RPM_GNUC_INTERNAL + rpmRC pkgdbDel(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum); +@@ -224,23 +232,24 @@ RPM_GNUC_INTERNAL + rpmRC pkgdbGet(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, + unsigned char **hdrBlob, unsigned int *hdrLen); + RPM_GNUC_INTERNAL +-rpmRC pkgdbNew(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum); +-RPM_GNUC_INTERNAL + unsigned int pkgdbKey(dbiIndex dbi, dbiCursor dbc); + + RPM_GNUC_INTERNAL + rpmRC idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, + dbiIndexSet *set, int curFlags); + RPM_GNUC_INTERNAL +-rpmRC idxdbPut(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, +- dbiIndexItem rec); ++rpmRC idxdbPut(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h); ++ + RPM_GNUC_INTERNAL +-rpmRC idxdbDel(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, +- dbiIndexItem rec); ++rpmRC idxdbDel(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h); ++ + RPM_GNUC_INTERNAL + const void * idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen); + + struct rpmdbOps_s { ++ const char *name; /* backend name */ ++ const char *path; /* main database name */ ++ + int (*open)(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags); + int (*close)(dbiIndex dbi, unsigned int flags); + int (*verify)(dbiIndex dbi, unsigned int flags); +@@ -251,28 +260,29 @@ struct rpmdbOps_s { + dbiCursor (*cursorFree)(dbiIndex dbi, dbiCursor dbc); + + rpmRC (*pkgdbGet)(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, unsigned char **hdrBlob, unsigned int *hdrLen); +- rpmRC (*pkgdbPut)(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, unsigned char *hdrBlob, unsigned int hdrLen); ++ rpmRC (*pkgdbPut)(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum, unsigned char *hdrBlob, unsigned int hdrLen); + rpmRC (*pkgdbDel)(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum); +- rpmRC (*pkgdbNew)(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum); + unsigned int (*pkgdbKey)(dbiIndex dbi, dbiCursor dbc); + + rpmRC (*idxdbGet)(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexSet *set, int curFlags); +- rpmRC (*idxdbPut)(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexItem rec); +- rpmRC (*idxdbDel)(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexItem rec); ++ rpmRC (*idxdbPut)(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h); ++ rpmRC (*idxdbDel)(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h); + const void * (*idxdbKey)(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen); + }; + ++#if defined(WITH_BDB) + RPM_GNUC_INTERNAL + extern struct rpmdbOps_s db3_dbops; ++#endif + +-#ifdef ENABLE_NDB ++#if defined(WITH_BDB_RO) + RPM_GNUC_INTERNAL +-extern struct rpmdbOps_s ndb_dbops; ++extern struct rpmdbOps_s bdbro_dbops; + #endif + +-#if defined(WITH_LMDB) ++#ifdef ENABLE_NDB + RPM_GNUC_INTERNAL +-extern struct rpmdbOps_s lmdb_dbops; ++extern struct rpmdbOps_s ndb_dbops; + #endif + + #if defined(WITH_SQLITE) +@@ -280,6 +290,9 @@ RPM_GNUC_INTERNAL + extern struct rpmdbOps_s sqlite_dbops; + #endif + ++RPM_GNUC_INTERNAL ++extern struct rpmdbOps_s dummydb_dbops; ++ + #ifdef __cplusplus + } + #endif +diff --git a/lib/backend/sqlite.c b/lib/backend/sqlite.c +index 297f97f..7c2de45 100644 +--- a/lib/backend/sqlite.c ++++ b/lib/backend/sqlite.c +@@ -132,22 +132,14 @@ static int sqlite_init(rpmdb rdb, const char * dbhome) + char *dbfile = NULL; + + if (rdb->db_dbenv == NULL) { +- dbfile = rpmGenPath(dbhome, "rpmdb.sqlite", NULL); ++ dbfile = rpmGenPath(dbhome, rdb->db_ops->path, NULL); + sqlite3 *sdb = NULL; + int xx, flags = 0; + int retry_open = 1; +- +- free(rdb->db_descr); +- rdb->db_descr = xstrdup("sqlite"); +- + if ((rdb->db_mode & O_ACCMODE) == O_RDONLY) + flags |= SQLITE_OPEN_READONLY; +- else { +- rpmlog(RPMLOG_ERR, +- _("unable to open sqlite database %s for writing, sqlite support is read-only\n"), dbfile); +- rc = RPMRC_FAIL; +- goto exit; +- } ++ else ++ flags |= (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE); + + while (retry_open--) { + xx = sqlite3_open_v2(dbfile, &sdb, flags, NULL); +@@ -241,7 +233,6 @@ static int sqlexec(sqlite3 *sdb, const char *fmt, ...) + rpmlog(RPMLOG_DEBUG, "%s: %d\n", cmd, rc); + + sqlite3_free(cmd); +- sqlite3_free(err); + + return rc ? RPMRC_FAIL : RPMRC_OK; + } +@@ -395,10 +386,8 @@ exit: + + static void sqlite_SetFSync(rpmdb rdb, int enable) + { +- if (rdb->db_dbenv) { +- sqlexec(rdb->db_dbenv, ++ sqlexec(rdb->db_dbenv, + "PRAGMA synchronous = %s", enable ? "FULL" : "OFF"); +- } + } + + static int sqlite_Ctrl(rpmdb rdb, dbCtrlOp ctrl) +@@ -448,19 +437,52 @@ static dbiCursor sqlite_CursorFree(dbiIndex dbi, dbiCursor dbc) + return NULL; + } + +-static rpmRC sqlite_pkgdbNew(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum) ++static rpmRC sqlite_pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum, unsigned char *hdrBlob, unsigned int hdrLen) + { +- return RPMRC_FAIL; +-} ++ int rc = 0; ++ dbiCursor dbwc = NULL; + +-static rpmRC sqlite_pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum, unsigned char *hdrBlob, unsigned int hdrLen) +-{ +- return RPMRC_FAIL; ++ /* Avoid trashing existing query cursor on header rewrite */ ++ if (hdrNum && *hdrNum) { ++ dbwc = dbiCursorInit(dbi, DBC_WRITE); ++ dbc = dbwc; ++ } ++ ++ if (!rc) { ++ rc = dbiCursorPrep(dbc, "INSERT OR REPLACE INTO '%q' VALUES(?, ?)", ++ dbi->dbi_file); ++ } ++ ++ if (!rc) ++ rc = dbiCursorBindPkg(dbc, *hdrNum, hdrBlob, hdrLen); ++ ++ if (!rc) ++ while ((rc = sqlite3_step(dbc->stmt)) == SQLITE_ROW) {}; ++ ++ /* XXX rowid is a 64bit integer and could overflow hdrnum */ ++ if (rc == SQLITE_DONE && *hdrNum == 0) ++ *hdrNum = sqlite3_last_insert_rowid(dbc->sdb); ++ ++ rc = dbiCursorResult(dbc); ++ ++ if (dbwc) ++ dbiCursorFree(dbi, dbwc); ++ ++ return rc; + } + + static rpmRC sqlite_pkgdbDel(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum) + { +- return RPMRC_FAIL; ++ int rc = dbiCursorPrep(dbc, "DELETE FROM '%q' WHERE hnum=?;", ++ dbi->dbi_file); ++ ++ if (!rc) ++ rc = dbiCursorBindPkg(dbc, hdrNum, NULL, 0); ++ ++ if (!rc) ++ while ((rc = sqlite3_step(dbc->stmt)) == SQLITE_ROW) {}; ++ ++ return dbiCursorResult(dbc); + } + + static rpmRC sqlite_stepPkg(dbiCursor dbc, unsigned char **hdrBlob, unsigned int *hdrLen) +@@ -615,14 +637,39 @@ static rpmRC sqlite_idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size + return rc; + } + +-static rpmRC sqlite_idxdbPut(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexItem rec) ++static rpmRC sqlite_idxdbPutOne(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexItem rec) ++{ ++ int rc = dbiCursorPrep(dbc, "INSERT INTO '%q' VALUES(?, ?, ?)", ++ dbi->dbi_file); ++ ++ if (!rc) ++ rc = dbiCursorBindIdx(dbc, keyp, keylen, rec); ++ ++ if (!rc) ++ while ((rc = sqlite3_step(dbc->stmt)) == SQLITE_ROW) {}; ++ ++ return dbiCursorResult(dbc); ++} ++ ++static rpmRC sqlite_idxdbPut(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h) + { +- return RPMRC_FAIL; ++ return tag2index(dbi, rpmtag, hdrNum, h, sqlite_idxdbPutOne); + } + +-static rpmRC sqlite_idxdbDel(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, dbiIndexItem rec) ++static rpmRC sqlite_idxdbDel(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h) + { +- return RPMRC_FAIL; ++ dbiCursor dbc = dbiCursorInit(dbi, DBC_WRITE); ++ int rc = dbiCursorPrep(dbc, "DELETE FROM '%q' WHERE hnum=?", dbi->dbi_file); ++ ++ if (!rc) ++ rc = dbiCursorBindPkg(dbc, hdrNum, NULL, 0); ++ ++ if (!rc) ++ while ((rc = sqlite3_step(dbc->stmt)) == SQLITE_ROW) {}; ++ ++ rc = dbiCursorResult(dbc); ++ dbiCursorFree(dbi, dbc); ++ return rc; + } + + static const void * sqlite_idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen) +@@ -636,7 +683,12 @@ static const void * sqlite_idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *k + return key; + } + ++ ++ + struct rpmdbOps_s sqlite_dbops = { ++ .name = "sqlite", ++ .path = "rpmdb.sqlite", ++ + .open = sqlite_Open, + .close = sqlite_Close, + .verify = sqlite_Verify, +@@ -650,7 +702,6 @@ struct rpmdbOps_s sqlite_dbops = { + .pkgdbDel = sqlite_pkgdbDel, + .pkgdbGet = sqlite_pkgdbGet, + .pkgdbKey = sqlite_pkgdbKey, +- .pkgdbNew = sqlite_pkgdbNew, + + .idxdbGet = sqlite_idxdbGet, + .idxdbPut = sqlite_idxdbPut, +diff --git a/lib/rpmdb.c b/lib/rpmdb.c +index 355cb08..cf5e6a7 100644 +--- a/lib/rpmdb.c ++++ b/lib/rpmdb.c +@@ -7,6 +7,8 @@ + #include + #include + #include ++#include ++#include + + #ifndef DYING /* XXX already in "system.h" */ + #include +@@ -32,6 +34,7 @@ + #include "lib/rpmdb_internal.h" + #include "lib/fprint.h" + #include "lib/header_internal.h" /* XXX for headerSetInstance() */ ++#include "lib/backend/dbi.h" + #include "lib/backend/dbiset.h" + #include "lib/misc.h" + #include "debug.h" +@@ -48,14 +51,6 @@ + #undef HTKEYTYPE + #undef HTDATATYPE + +-typedef rpmRC (*idxfunc)(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen, +- dbiIndexItem rec); +- +-static rpmRC tag2index(dbiIndex dbi, rpmTagVal rpmtag, +- unsigned int hdrNum, Header h, +- idxfunc idxupdate); +- +-static rpmRC indexPut(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h); + static rpmdb rpmdbUnlink(rpmdb db); + + static int buildIndexes(rpmdb db) +@@ -87,7 +82,7 @@ static int buildIndexes(rpmdb db) + for (int dbix = 0; dbix < db->db_ndbi; dbix++) { + dbiIndex dbi = db->db_indexes[dbix]; + if (dbi && (dbiFlags(dbi) & DBI_CREATED)) { +- rc += indexPut(dbi, db->db_tags[dbix], hdrNum, h); ++ rc += idxdbPut(dbi, db->db_tags[dbix], hdrNum, h); + } + } + } +@@ -305,6 +300,7 @@ struct rpmdbIndexIterator_s { + dbiCursor ii_dbc; + dbiIndexSet ii_set; + unsigned int *ii_hdrNums; ++ int ii_skipdata; + }; + + static rpmdb rpmdbRock; +@@ -355,17 +351,22 @@ const char *rpmdbHome(rpmdb db) + return dbdir; + } + +-int rpmdbOpenAll(rpmdb db) ++static int doOpen(rpmdb db, int justPkgs) + { +- int rc = 0; ++ int rc = pkgdbOpen(db, db->db_flags, NULL); ++ if (!justPkgs) { ++ for (int dbix = 0; rc == 0 && dbix < db->db_ndbi; dbix++) { ++ rc += indexOpen(db, db->db_tags[dbix], db->db_flags, NULL); ++ } ++ } ++ return rc; ++} + ++int rpmdbOpenAll(rpmdb db) ++{ + if (db == NULL) return -2; + +- rc = pkgdbOpen(db, db->db_flags, NULL); +- for (int dbix = 0; dbix < db->db_ndbi; dbix++) { +- rc += indexOpen(db, db->db_tags[dbix], db->db_flags, NULL); +- } +- return rc; ++ return doOpen(db, 0); + } + + static int dbiForeach(dbiIndex *dbis, int ndbi, +@@ -415,7 +416,6 @@ int rpmdbClose(rpmdb db) + db->db_fullpath = _free(db->db_fullpath); + db->db_checked = dbChkFree(db->db_checked); + db->db_indexes = _free(db->db_indexes); +- db->db_descr = _free(db->db_descr); + + if (next) { + *prev = next->db_next; +@@ -480,7 +480,6 @@ static rpmdb newRpmdb(const char * root, const char * home, + db->db_tags = dbiTags; + db->db_ndbi = sizeof(dbiTags) / sizeof(rpmDbiTag); + db->db_indexes = xcalloc(db->db_ndbi, sizeof(*db->db_indexes)); +- db->db_descr = xstrdup("unknown db"); + db->nrefs = 0; + return rpmdbLink(db); + } +@@ -508,13 +507,18 @@ static int openDatabase(const char * prefix, + /* Try to ensure db home exists, error out if we can't even create */ + rc = rpmioMkpath(rpmdbHome(db), 0755, getuid(), getgid()); + if (rc == 0) { ++ /* Open just bare minimum when rebuilding a potentially damaged db */ ++ int justPkgs = (db->db_flags & RPMDB_FLAG_REBUILD) && ++ ((db->db_mode & O_ACCMODE) == O_RDONLY); + /* Enable signal queue on the first db open */ + if (db->db_next == NULL) { + rpmsqActivate(1); + } + +- /* Just the primary Packages database opened here */ +- rc = pkgdbOpen(db, db->db_flags, NULL); ++ rc = doOpen(db, justPkgs); ++ ++ if (!db->db_descr) ++ db->db_descr = "unknown db"; + } + + if (rc || justCheck || dbp == NULL) +@@ -552,10 +556,7 @@ int rpmdbInit (const char * prefix, int perms) + + rc = openDatabase(prefix, NULL, &db, (O_CREAT | O_RDWR), perms, 0); + if (db != NULL) { +- int xx; +- xx = rpmdbOpenAll(db); +- if (xx && rc == 0) rc = xx; +- xx = rpmdbClose(db); ++ int xx = rpmdbClose(db); + if (xx && rc == 0) rc = xx; + db = NULL; + } +@@ -571,8 +572,6 @@ int rpmdbVerify(const char * prefix) + + if (db != NULL) { + int xx; +- rc = rpmdbOpenAll(db); +- + + if (db->db_pkgs) + rc += dbiVerify(db->db_pkgs, 0); +@@ -979,7 +978,7 @@ static int miFreeHeader(rpmdbMatchIterator mi, dbiIndex dbi) + if (hdrBlob != NULL && rpmrc != RPMRC_FAIL) { + rpmsqBlock(SIG_BLOCK); + dbCtrl(mi->mi_db, DB_CTRL_LOCK_RW); +- rc = pkgdbPut(dbi, mi->mi_dbc, mi->mi_prevoffset, ++ rc = pkgdbPut(dbi, mi->mi_dbc, &mi->mi_prevoffset, + hdrBlob, hdrLen); + dbCtrl(mi->mi_db, DB_CTRL_INDEXSYNC); + dbCtrl(mi->mi_db, DB_CTRL_UNLOCK_RW); +@@ -1588,6 +1587,7 @@ int rpmdbExtendIterator(rpmdbMatchIterator mi, + dbiIndexSetAppendSet(mi->mi_set, set, 0); + dbiIndexSetFree(set); + } ++ mi->mi_sorted = 0; + rc = 0; + } + +@@ -1649,6 +1649,7 @@ int rpmdbAppendIterator(rpmdbMatchIterator mi, + + for (unsigned int i = 0; i < nHdrNums; i++) + dbiIndexSetAppendOne(mi->mi_set, hdrNums[i], 0, 0); ++ mi->mi_sorted = 0; + return 0; + } + +@@ -1891,6 +1892,13 @@ rpmdbIndexIterator rpmdbIndexIteratorInit(rpmdb db, rpmDbiTag rpmtag) + return ii; + } + ++rpmdbIndexIterator rpmdbIndexKeyIteratorInit(rpmdb db, rpmDbiTag rpmtag) ++{ ++ rpmdbIndexIterator ki = rpmdbIndexIteratorInit(db, rpmtag); ++ ki->ii_skipdata = 1; ++ return ki; ++} ++ + int rpmdbIndexIteratorNext(rpmdbIndexIterator ii, const void ** key, size_t * keylen) + { + int rc; +@@ -1905,7 +1913,8 @@ int rpmdbIndexIteratorNext(rpmdbIndexIterator ii, const void ** key, size_t * ke + /* free old data */ + ii->ii_set = dbiIndexSetFree(ii->ii_set); + +- rc = idxdbGet(ii->ii_dbi, ii->ii_dbc, NULL, 0, &ii->ii_set, DBC_NORMAL_SEARCH); ++ rc = idxdbGet(ii->ii_dbi, ii->ii_dbc, NULL, 0, ++ ii->ii_skipdata ? NULL : &ii->ii_set, DBC_NORMAL_SEARCH); + + *key = idxdbKey(ii->ii_dbi, ii->ii_dbc, &iikeylen); + *keylen = iikeylen; +@@ -1973,7 +1982,7 @@ unsigned int rpmdbIndexIteratorPkgOffset(rpmdbIndexIterator ii, unsigned int nr) + return dbiIndexRecordOffset(ii->ii_set, nr); + } + +-unsigned int *rpmdbIndexIteratorPkgOffsets(rpmdbIndexIterator ii) ++const unsigned int *rpmdbIndexIteratorPkgOffsets(rpmdbIndexIterator ii) + { + int i; + +@@ -2042,11 +2051,6 @@ static void logAddRemove(const char *dbiname, int removing, rpmtd tagdata) + } + } + +-static rpmRC indexDel(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h) +-{ +- return tag2index(dbi, rpmtag, hdrNum, h, idxdbDel); +-} +- + int rpmdbRemove(rpmdb db, unsigned int hdrNum) + { + dbiIndex dbi = NULL; +@@ -2088,7 +2092,7 @@ int rpmdbRemove(rpmdb db, unsigned int hdrNum) + if (indexOpen(db, rpmtag, 0, &dbi)) + continue; + +- ret += indexDel(dbi, rpmtag, hdrNum, h); ++ ret += idxdbDel(dbi, rpmtag, hdrNum, h); + } + } + +@@ -2185,7 +2189,7 @@ static rpmRC updateRichDep(dbiIndex dbi, dbiCursor dbc, const char *str, + return rc; + } + +-static rpmRC tag2index(dbiIndex dbi, rpmTagVal rpmtag, ++rpmRC tag2index(dbiIndex dbi, rpmTagVal rpmtag, + unsigned int hdrNum, Header h, + idxfunc idxupdate) + { +@@ -2244,8 +2248,8 @@ static rpmRC tag2index(dbiIndex dbi, rpmTagVal rpmtag, + case RPMTAG_REQUIRENAME: { + /* Filter out install prerequisites. */ + rpm_flag_t *rflag = rpmtdNextUint32(&reqflags); +- if (rflag && isInstallPreReq(*rflag) && +- !isErasePreReq(*rflag)) ++ if (rflag && isInstallPreReq(*rflag) && ++ !isErasePreReq(*rflag)) + continue; + break; + } +@@ -2295,11 +2299,6 @@ exit: + return (rc == 0) ? RPMRC_OK : RPMRC_FAIL; + } + +-static rpmRC indexPut(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h) +-{ +- return tag2index(dbi, rpmtag, hdrNum, h, idxdbPut); +-} +- + int rpmdbAdd(rpmdb db, Header h) + { + dbiIndex dbi = NULL; +@@ -2327,9 +2326,7 @@ int rpmdbAdd(rpmdb db, Header h) + + /* Add header to primary index */ + dbc = dbiCursorInit(dbi, DBC_WRITE); +- ret = pkgdbNew(dbi, dbc, &hdrNum); +- if (ret == 0) +- ret = pkgdbPut(dbi, dbc, hdrNum, hdrBlob, hdrLen); ++ ret = pkgdbPut(dbi, dbc, &hdrNum, hdrBlob, hdrLen); + dbiCursorFree(dbi, dbc); + + /* Add associated data to secondary indexes */ +@@ -2340,7 +2337,7 @@ int rpmdbAdd(rpmdb db, Header h) + if (indexOpen(db, rpmtag, 0, &dbi)) + continue; + +- ret += indexPut(dbi, rpmtag, hdrNum, h); ++ ret += idxdbPut(dbi, rpmtag, hdrNum, h); + } + } + +@@ -2487,7 +2484,8 @@ static int rpmdbSetPermissions(char * src, char * dest) + } + + int rpmdbRebuild(const char * prefix, rpmts ts, +- rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg)) ++ rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg), ++ int rebuildflags) + { + rpmdb olddb; + char * dbpath = NULL; +@@ -2527,7 +2525,9 @@ int rpmdbRebuild(const char * prefix, rpmts ts, + } + + if (openDatabase(prefix, dbpath, &olddb, +- O_RDONLY, 0644, RPMDB_FLAG_REBUILD)) { ++ O_RDONLY, 0644, RPMDB_FLAG_REBUILD | ++ (rebuildflags & RPMDB_REBUILD_FLAG_SALVAGE ? ++ RPMDB_FLAG_SALVAGE : 0))) { + rc = 1; + goto exit; + } +@@ -2536,10 +2536,6 @@ int rpmdbRebuild(const char * prefix, rpmts ts, + rc = 1; + goto exit; + } +- if (rpmdbOpenAll(newdb)) { +- rc = 1; +- goto exit; +- } + + { Header h = NULL; + rpmdbMatchIterator mi; +@@ -2584,6 +2580,7 @@ int rpmdbRebuild(const char * prefix, rpmts ts, + } + + rpmdbClose(olddb); ++ dbCtrl(newdb, DB_CTRL_INDEXSYNC); + rpmdbClose(newdb); + + if (failed) { +@@ -2644,3 +2641,50 @@ int rpmdbCtrl(rpmdb db, rpmdbCtrlOp ctrl) + return dbctrl ? dbCtrl(db, dbctrl) : 1; + } + ++char *rpmdbCookie(rpmdb db) ++{ ++ void *cookie = NULL; ++ rpmdbIndexIterator ii = rpmdbIndexIteratorInit(db, RPMDBI_NAME); ++ ++ if (ii) { ++ DIGEST_CTX ctx = rpmDigestInit(PGPHASHALGO_SHA1, RPMDIGEST_NONE); ++ const void *key = 0; ++ size_t keylen = 0; ++ while ((rpmdbIndexIteratorNext(ii, &key, &keylen)) == 0) { ++ const unsigned int *offsets = rpmdbIndexIteratorPkgOffsets(ii); ++ unsigned int npkgs = rpmdbIndexIteratorNumPkgs(ii); ++ rpmDigestUpdate(ctx, key, keylen); ++ rpmDigestUpdate(ctx, offsets, sizeof(*offsets) * npkgs); ++ } ++ rpmDigestFinal(ctx, &cookie, NULL, 1); ++ } ++ rpmdbIndexIteratorFree(ii); ++ return cookie; ++} ++ ++int rpmdbFStat(rpmdb db, struct stat *statbuf) ++{ ++ int rc = -1; ++ if (db) { ++ const char *dbfile = db->db_ops->path; ++ if (dbfile) { ++ char *path = rpmGenPath(rpmdbHome(db), dbfile, NULL); ++ rc = stat(path, statbuf); ++ free(path); ++ } ++ } ++ return rc; ++} ++ ++int rpmdbStat(const char *prefix, struct stat *statbuf) ++{ ++ rpmdb db = NULL; ++ int flags = RPMDB_FLAG_VERIFYONLY; ++ int rc = -1; ++ ++ if (openDatabase(prefix, NULL, &db, O_RDONLY, 0644, flags) == 0) { ++ rc = rpmdbFStat(db, statbuf); ++ rpmdbClose(db); ++ } ++ return rc; ++} +diff --git a/lib/rpmdb.h b/lib/rpmdb.h +index 78765bb..8e3ab5a 100644 +--- a/lib/rpmdb.h ++++ b/lib/rpmdb.h +@@ -8,6 +8,7 @@ + + #include + #include ++#include + + #ifdef __cplusplus + extern "C" { +@@ -152,6 +153,14 @@ Header rpmdbNextIterator(rpmdbMatchIterator mi); + */ + rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi); + ++/** \ingroup rpmdb ++ * Get an iterator for index keys ++ * @param db rpm database ++ * @param rpmtag the index to iterate over ++ * @return the index iterator ++ */ ++rpmdbIndexIterator rpmdbIndexKeyIteratorInit(rpmdb db, rpmDbiTag rpmtag); ++ + /** \ingroup rpmdb + * Get an iterator for an index + * @param db rpm database +@@ -218,6 +227,30 @@ rpmdbIndexIterator rpmdbIndexIteratorFree(rpmdbIndexIterator ii); + */ + int rpmdbCtrl(rpmdb db, rpmdbCtrlOp ctrl); + ++/** \ingroup rpmdb ++ * Retrieve rpm database changed-cookie. ++ * Useful for eg. determining cache validity. ++ * @param db rpm database ++ * @return cookie string (malloced), or NULL on error ++ */ ++char *rpmdbCookie(rpmdb db); ++ ++/** \ingroup rpmdb ++ * Perform stat() on rpm database ++ * @param prefix prefix or NULL for / ++ * @retval statbuf returned data from stat() ++ * @return 0 on success, -1 on error ++ */ ++int rpmdbStat(const char *prefix, struct stat *statbuf); ++ ++/** \ingroup rpmdb ++ * Perform stat() on an open rpm database ++ * @param db rpm database ++ * @retval statbuf returned data from stat() ++ * @return 0 on success, -1 on error ++ */ ++int rpmdbFStat(rpmdb db, struct stat *statbuf); ++ + #ifdef __cplusplus + } + #endif +diff --git a/lib/rpmdb_internal.h b/lib/rpmdb_internal.h +index 92848ab..d7446e5 100644 +--- a/lib/rpmdb_internal.h ++++ b/lib/rpmdb_internal.h +@@ -23,6 +23,10 @@ extern "C" { + #undef HTKEYTYPE + #undef HTDATATYPE + ++enum rpmdbRebuildFlags_e { ++ RPMDB_REBUILD_FLAG_SALVAGE = (1 << 0), ++}; ++ + /** \ingroup rpmdb + * Reference a database instance. + * @param db rpm database +@@ -63,11 +67,13 @@ int rpmdbClose (rpmdb db); + * @param prefix path to top of install tree + * @param ts transaction set (or NULL) + * @param (*hdrchk) headerCheck() vector (or NULL) ++ * @param rebuildflags flags + * @return 0 on success + */ + RPM_GNUC_INTERNAL + int rpmdbRebuild(const char * prefix, rpmts ts, +- rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg)); ++ rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg), ++ int rebuildflags); + + /** \ingroup rpmdb + * Verify database components. +@@ -175,7 +181,7 @@ rpmdbMatchIterator rpmdbInitPrefixIterator(rpmdb db, rpmDbiTagVal rpmtag, + * @return db offsets of pkgs + */ + RPM_GNUC_INTERNAL +-unsigned int *rpmdbIndexIteratorPkgOffsets(rpmdbIndexIterator ii); ++const unsigned int *rpmdbIndexIteratorPkgOffsets(rpmdbIndexIterator ii); + + /** \ingroup rpmdb + * Return current index (position) in iterator. +diff --git a/lib/rpmts.c b/lib/rpmts.c +index 7131b7b..122a2ff 100644 +--- a/lib/rpmts.c ++++ b/lib/rpmts.c +@@ -135,17 +135,21 @@ int rpmtsRebuildDB(rpmts ts) + { + int rc = -1; + rpmtxn txn = NULL; ++ int rebuildflags = 0; + + /* Cannot do this on a populated transaction set */ + if (rpmtsNElements(ts) > 0) + return -1; + ++ if (rpmExpandNumeric("%{?_rebuilddb_salvage}")) ++ rebuildflags |= RPMDB_REBUILD_FLAG_SALVAGE; ++ + txn = rpmtxnBegin(ts, RPMTXN_WRITE); + if (txn) { + if (!(ts->vsflags & RPMVSF_NOHDRCHK)) +- rc = rpmdbRebuild(ts->rootDir, ts, headerCheck); ++ rc = rpmdbRebuild(ts->rootDir, ts, headerCheck, rebuildflags); + else +- rc = rpmdbRebuild(ts->rootDir, NULL, NULL); ++ rc = rpmdbRebuild(ts->rootDir, NULL, NULL, rebuildflags); + rpmtxnEnd(txn); + } + return rc; +diff --git a/macros.in b/macros.in +index fa2c29b..a0f7b13 100644 +--- a/macros.in ++++ b/macros.in +@@ -629,7 +629,7 @@ package or when debugging this package.\ + # ndb new data base format + # sqlite Sqlite database (read-only in this version!) + # +-%_db_backend bdb ++%_db_backend sqlite + + # + # Macros used to configure Berkley db parameters. +diff --git a/rpmdb.c b/rpmdb.c +index 25c088d..b72f0a5 100644 +--- a/rpmdb.c ++++ b/rpmdb.c +@@ -12,6 +12,7 @@ enum modes { + MODE_VERIFYDB = (1 << 2), + MODE_EXPORTDB = (1 << 3), + MODE_IMPORTDB = (1 << 4), ++ MODE_SALVAGEDB = (1 << 5), + }; + + static int mode = 0; +@@ -24,6 +25,8 @@ static struct poptOption dbOptsTable[] = { + NULL}, + { "verifydb", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR|POPT_ARGFLAG_DOC_HIDDEN), + &mode, MODE_VERIFYDB, N_("verify database files"), NULL}, ++ { "salvagedb", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR|POPT_ARGFLAG_DOC_HIDDEN), ++ &mode, MODE_SALVAGEDB, N_("salvage database"), NULL}, + { "exportdb", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_EXPORTDB, + N_("export database to stdout header list"), + NULL}, +@@ -108,8 +111,11 @@ int main(int argc, char *argv[]) + ec = rpmtsInitDB(ts, 0644); + break; + case MODE_REBUILDDB: ++ case MODE_SALVAGEDB: + { rpmVSFlags vsflags = rpmExpandNumeric("%{_vsflags_rebuilddb}"); + rpmVSFlags ovsflags = rpmtsSetVSFlags(ts, vsflags); ++ if (mode == MODE_SALVAGEDB) ++ rpmDefineMacro(NULL, "_rebuilddb_salvage 1", 0); + ec = rpmtsRebuildDB(ts); + rpmtsSetVSFlags(ts, ovsflags); + } break; +-- +2.39.3 + diff --git a/rpm.spec b/rpm.spec index d761dae..b4d463e 100644 --- a/rpm.spec +++ b/rpm.spec @@ -1,4 +1,4 @@ -%define anolis_release .0.1 +%define anolis_release .0.2 %define _legacy_common_support 1 # build against xz? %bcond_without xz @@ -22,6 +22,8 @@ %bcond_with lmdb # build with readonly sqlite support? %bcond_without sqlite +# build with bdb_ro support? +%bcond_without bdb_ro %if 0%{?rhel} > 7 || 0%{?anolis} # Disable python2 build by default @@ -156,6 +158,8 @@ Patch1002: rpm-4.14.2-unversioned-python.patch Patch2000: 1000-rpm-anolis-support-loongarch.patch Patch2001: 0001-debugedit-add-loongarch-support.patch +Patch2002: 0001-support-sqlite-backend-rpmdb.patch + # Partially GPL/LGPL dual-licensed and some bits with BSD # SourceLicense: (GPLv2+ and LGPLv2+ with exceptions) and BSD License: GPLv2+ @@ -481,6 +485,7 @@ done; %{?with_zstd: --enable-zstd} \ %{?with_lmdb: --enable-lmdb} \ %{?with_sqlite: --enable-sqlite} \ + %{?with_bdb_ro: --enable-bdb-ro} \ --with-fapolicyd \ --enable-python \ --with-crypto=openssl \ @@ -559,6 +564,18 @@ chmod a-x $RPM_BUILD_ROOT/%{rpmhome}/python-macro-helper make check || cat tests/rpmtests.log %endif +%pre +# Only update +if [ $1 == 2 ] +then + [ -e /var/lib/rpm/.rpm.lock ] && rm -f /var/lib/rpm/.rpm.lock + [ `rpm --eval "%_db_backend"` == "bdb" ] && rpmdb --exportdb > /var/lib/rpm_bdb_bak +fi + +%posttrans +[ -e /var/lib/rpm/.rpm.lock ] && rm -f /var/lib/rpm/.rpm.lock +rpm -q rpm 2>&1 | grep bdb && rpmdb --rebuilddb + %post libs -p /sbin/ldconfig %postun libs -p /sbin/ldconfig @@ -704,6 +721,9 @@ make check || cat tests/rpmtests.log %doc doc/librpm/html/* %changelog +* Thu Jun 29 2023 forrest_ly - 4.14.4-26.0.2 +- Support and switch rpmdb to sqlite + * Wed Jun 14 2023 Liwei Ge - 4.14.4-26.0.1 - Rebrand for Anolis OS - Fix gcc10 -fno-common compile issue for compatible with gcc10 build -- Gitee