/** \ingroup rpmdb
* \file lib/lmdb.c
*/
#include "system.h"
#include <ctype.h>
#include <errno.h>
#include <sys/wait.h>
#include <popt.h>
#include <lmdb.h>
#include <signal.h>
#include <rpm/rpmtypes.h>
#include <rpm/rpmmacro.h>
#include <rpm/rpmfileutil.h>
#include <rpm/rpmlog.h>
#include "lib/rpmdb_internal.h"
#include "debug.h"
static int _debug = 1; /* XXX if < 0 debugging, > 0 unusual error returns */
struct dbiCursor_s {
dbiIndex dbi;
const void *key;
unsigned int keylen;
int flags;
MDB_cursor * cursor;
MDB_txn * txn;
};
static const char * _EnvF(unsigned eflags)
{
static char t[256];
char *te = t;
*te = '\0';
#define _EF(_v) if (eflags & MDB_##_v) te = stpcpy(stpcpy(te,"|"),#_v)
_EF(FIXEDMAP);
_EF(NOSUBDIR);
_EF(NOSYNC);
_EF(RDONLY);
_EF(NOMETASYNC);
_EF(WRITEMAP);
_EF(MAPASYNC);
_EF(NOTLS);
_EF(NOLOCK);
_EF(NORDAHEAD);
_EF(NOMEMINIT);
#undef _EF
if (t[0] == '\0') te += sprintf(te, "|0x%x", eflags);
*te = '\0';
return t+1;
}
static const char * _OpenF(unsigned oflags)
{
static char t[256];
char *te = t;
*te = '\0';
#define _OF(_v) if (oflags & MDB_##_v) te = stpcpy(stpcpy(te,"|"),#_v)
_OF(REVERSEKEY);
_OF(DUPSORT);
_OF(INTEGERKEY);
_OF(DUPFIXED);
_OF(INTEGERDUP);
_OF(REVERSEDUP);
_OF(CREATE);
#undef _OF
if (t[0] == '\0') te += sprintf(te, "|0x%x", oflags);
*te = '\0';
return t+1;
}
static int dbapi_err(rpmdb rdb, const char * msg, int rc, int printit)
{
if (printit && rc) {
int lvl = RPMLOG_ERR;
if (msg)
rpmlog(lvl, _("%s:\trc(%d) = %s(): %s\n"),
rdb->db_descr, rc, msg, (rc ? mdb_strerror(rc) : ""));
else
rpmlog(lvl, _("%s:\trc(%d) = %s()\n"),
rdb->db_descr, rc, (rc ? mdb_strerror(rc) : ""));
}
return rc;
}
static int cvtdberr(dbiIndex dbi, const char * msg, int rc, int printit)
{
return dbapi_err(dbi->dbi_rpmdb, msg, rc, printit);
}
static void lmdb_assert(MDB_env *env, const char *msg)
{
rpmlog(RPMLOG_ERR, "%s: %s\n", __FUNCTION__, msg);
}
static void lmdb_dbSetFSync(rpmdb rdb, int enable)
{
}
static int lmdb_Ctrl(rpmdb rdb, dbCtrlOp ctrl)
{
return 0;
}
static int db_fini(rpmdb rdb, const char * dbhome)
{
int rc = 0;
MDB_env * env = rdb->db_dbenv;
if (env == NULL)
goto exit;
if (--rdb->db_opens > 0)
goto exit;
mdb_env_close(env);
rdb->db_dbenv = env = NULL;
rpmlog(RPMLOG_DEBUG, "closed db environment %s\n", dbhome);
exit:
return rc;
}
static int db_init(rpmdb rdb, const char * dbhome)
{
int rc = EINVAL;
MDB_env * env = NULL;
int retry_open = 2;
uint32_t eflags = 0;
if (rdb->db_dbenv != NULL) {
rdb->db_opens++;
return 0;
} else {
/* On first call, set backend description to something... */
free(rdb->db_descr);
rdb->db_descr = xstrdup("lmdb");
}
MDB_dbi maxdbs = 32;
unsigned int maxreaders = 16;
size_t mapsize = 256 * 1024 * 1024;
if ((rc = mdb_env_create(&env))
|| (rc = mdb_env_set_maxreaders(env, maxreaders))
|| (rc = mdb_env_set_mapsize(env, mapsize))
|| (rc = mdb_env_set_maxdbs(env, maxdbs))
|| (rc = mdb_env_set_assert(env, lmdb_assert))
|| (rc = mdb_env_set_userctx(env, rdb))
) {
rc = dbapi_err(rdb, "mdb_env_create", rc, _debug);
goto exit;
}
/*
* Actually open the environment. Fall back to private environment
* if we dont have permission to join/create shared environment or
* system doesn't support it..
*/
while (retry_open) {
rpmlog(RPMLOG_DEBUG, "opening db environment %s eflags=%s perms=0%o\n", dbhome, _EnvF(eflags), rdb->db_perms);
eflags = 0;
eflags |= MDB_WRITEMAP;
eflags |= MDB_MAPASYNC;
eflags |= MDB_NOTLS;
if (access(dbhome, W_OK) && (rdb->db_mode & O_ACCMODE) == O_RDONLY)
eflags |= MDB_RDONLY;
rc = mdb_env_open(env, dbhome, eflags, rdb->db_perms);
if (rc) {
rc = dbapi_err(rdb, "mdb_env_open", rc, _debug);
if (rc == EPERM)
rpmlog(RPMLOG_ERR, "lmdb: %s(%s/lock.mdb): %s\n", __FUNCTION__, dbhome, mdb_strerror(rc));
}
retry_open = 0; /* XXX EAGAIN might need a retry */
}
if (rc)
goto exit;
rdb->db_dbenv = env;
rdb->db_opens = 1;
exit:
if (rc && env) {
mdb_env_close(env);
rdb->db_dbenv = env = NULL;
}
return rc;
}
static int dbiSync(dbiIndex dbi, unsigned int flags)
{
int rc = 0;
MDB_dbi db = (unsigned long) dbi->dbi_db;
if (db != 0xdeadbeef && !dbi->cfg.dbi_no_dbsync) {
MDB_env * env = dbi->dbi_rpmdb->db_dbenv;
unsigned eflags = 0;
rc = mdb_env_get_flags(env, &eflags);
if (rc) {
rc = cvtdberr(dbi, "mdb_env_get_flags", rc, _debug);
eflags |= MDB_RDONLY;
}
if (!(eflags & MDB_RDONLY)) {
int force = 0;
rc = mdb_env_sync(env, force);
if (rc)
rc = cvtdberr(dbi, "mdb_env_sync", rc, _debug);
}
}
return rc;
}
static dbiCursor lmdb_dbiCursorInit(dbiIndex dbi, unsigned int flags)
{
dbiCursor dbc = NULL;
if (dbi && dbi->dbi_db != (void *)0xdeadbeefUL) {
MDB_env * env = dbi->dbi_rpmdb->db_dbenv;
MDB_txn * parent = NULL;
unsigned tflags = !(flags & DBC_WRITE) ? MDB_RDONLY : 0;
MDB_txn * txn = NULL;
MDB_cursor * cursor = NULL;
int rc = EINVAL;
rc = mdb_txn_begin(env, parent, tflags, &txn);
if (rc)
rc = cvtdberr(dbi, "mdb_txn_begin", rc, _debug);
if (rc == 0) {
MDB_dbi db = (unsigned long) dbi->dbi_db;
rc = mdb_cursor_open(txn, db, &cursor);
if (rc)
rc = cvtdberr(dbi, "mdb_cursor_open", rc, _debug);
}
if (rc == 0) {
dbc = xcalloc(1, sizeof(*dbc));
dbc->dbi = dbi;
dbc->flags = flags;
dbc->cursor = cursor;
dbc->txn = txn;
}
}
return dbc;
}
static dbiCursor lmdb_dbiCursorFree(dbiIndex dbi, dbiCursor dbc)
{
if (dbc) {
int rc = 0;
MDB_cursor * cursor = dbc->cursor;
MDB_txn * txn = dbc->txn;
dbiIndex dbi = dbc->dbi;
unsigned flags = dbc->flags;
mdb_cursor_close(cursor);
dbc->cursor = cursor = NULL;
if (rc)
cvtdberr(dbc->dbi, "mdb_cursor_close", rc, _debug);
/* Automatically commit close */
if (txn) {
rc = mdb_txn_commit(txn);
dbc->txn = txn = NULL;
if (rc)
rc = cvtdberr(dbc->dbi, "mdb_txn_commit", rc, _debug);
}
/* Automatically sync on write-cursor close */
if (flags & DBC_WRITE)
dbiSync(dbi, 0);
free(dbc);
rc = 0;
}
return NULL;
}
static int dbiCursorPut(dbiCursor dbc, MDB_val * key, MDB_val * data, unsigned flags)
{
int rc = EINVAL;
int sane = (key->mv_data != NULL && key->mv_size > 0 &&
data->mv_data != NULL && data->mv_size > 0);
if (dbc && sane) {
MDB_cursor * cursor = dbc->cursor;
rpmdb rdb = dbc->dbi->dbi_rpmdb;
rpmswEnter(&rdb->db_putops, (ssize_t) 0);
rc = mdb_cursor_put(cursor, key, data, flags);
if (rc) {
rc = cvtdberr(dbc->dbi, "mdb_cursor_put", rc, _debug);
if (dbc->txn) {
mdb_txn_abort(dbc->txn);
dbc->txn = NULL;
}
}
rpmswExit(&rdb->db_putops, (ssize_t) data->mv_size);
}
return rc;
}
static int dbiCursorGet(dbiCursor dbc, MDB_val *key, MDB_val *data, unsigned op)
{
int rc = EINVAL;
int sane = ((op == MDB_NEXT) || (key->mv_data != NULL && key->mv_size > 0));
if (dbc && sane) {
MDB_cursor * cursor = dbc->cursor;
rpmdb rdb = dbc->dbi->dbi_rpmdb;
rpmswEnter(&rdb->db_getops, 0);
/* XXX db4 does DB_FIRST on uninitialized cursor */
rc = mdb_cursor_get(cursor, key, data, op);
if (rc && rc != MDB_NOTFOUND) {
rc = cvtdberr(dbc->dbi, "mdb_cursor_get", rc, _debug);
if (dbc->txn) {
mdb_txn_abort(dbc->txn);
dbc->txn = NULL;
}
}
/* Remember the last key fetched */
if (rc == 0) {
dbc->key = key->mv_data;
dbc->keylen = key->mv_size;
} else {
dbc->key = NULL;
dbc->keylen = 0;
}
rpmswExit(&rdb->db_getops, data->mv_size);
}
return rc;
}
static int dbiCursorDel(dbiCursor dbc, MDB_val *key, MDB_val *data, unsigned int flags)
{
int rc = EINVAL;
int sane = (key->mv_data != NULL && key->mv_size > 0);
if (dbc && sane) {
MDB_cursor * cursor = dbc->cursor;
rpmdb rdb = dbc->dbi->dbi_rpmdb;
rpmswEnter(&rdb->db_delops, 0);
/* XXX TODO: ensure that cursor is positioned with duplicates */
rc = mdb_cursor_get(cursor, key, data, MDB_SET);
if (rc && rc != MDB_NOTFOUND) {
rc = cvtdberr(dbc->dbi, "mdb_cursor_get", rc, _debug);
if (dbc->txn)
dbc->txn = NULL;
}
if (rc == 0) {
rc = mdb_cursor_del(cursor, flags);
if (rc)
rc = cvtdberr(dbc->dbi, "mdb_cursor_del", rc, _debug);
}
rpmswExit(&rdb->db_delops, data->mv_size);
}
return rc;
}
static int lmdb_dbiVerify(dbiIndex dbi, unsigned int flags)
{
return 0;
}
static int lmdb_dbiClose(dbiIndex dbi, unsigned int flags)
{
int rc = 0;
rpmdb rdb = dbi->dbi_rpmdb;
const char * dbhome = rpmdbHome(rdb);
MDB_dbi db = (unsigned long) dbi->dbi_db;
if (db != 0xdeadbeef) {
MDB_env * env = dbi->dbi_rpmdb->db_dbenv;
mdb_dbi_close(env, db);
dbi->dbi_db = (void *) 0xdeadbeefUL;
rpmlog(RPMLOG_DEBUG, "closed db index %s/%s\n",
dbhome, dbi->dbi_file);
}
db_fini(rdb, dbhome ? dbhome : "");
dbi = dbiFree(dbi);
return rc;
}
static int lmdb_dbiOpen(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags)
{
int rc = 1;
const char *dbhome = rpmdbHome(rdb);
dbiIndex dbi = NULL;
int retry_open;
MDB_dbi db = 0;
uint32_t oflags;
if (dbip)
*dbip = NULL;
if ((dbi = dbiNew(rdb, rpmtag)) == NULL)
goto exit;
dbi->dbi_flags = 0;
dbi->dbi_db = (void *) 0xdeadbeefUL;
rc = db_init(rdb, dbhome);
retry_open = (rc == 0) ? 2 : 0;
do {
MDB_env * env = rdb->db_dbenv;
MDB_txn * parent = NULL;
unsigned tflags = access(dbhome, W_OK) ? MDB_RDONLY : 0;
MDB_txn * txn = NULL;
if (tflags & MDB_RDONLY)
dbi->dbi_flags |= DBI_RDONLY;
rc = mdb_txn_begin(env, parent, tflags, &txn);
if (rc)
rc = cvtdberr(dbi, "mdb_txn_begin", rc, _debug);
const char * name = dbi->dbi_file;
oflags = 0;
if (!(tflags & MDB_RDONLY))
oflags |= MDB_CREATE;
if (!strcmp(dbi->dbi_file, "Packages"))
oflags |= MDB_INTEGERKEY;
rpmlog(RPMLOG_DEBUG, "opening db index %s/%s oflags=%s\n",
dbhome, dbi->dbi_file, _OpenF(oflags));
db = 0xdeadbeef;
rc = mdb_dbi_open(txn, name, oflags, &db);
if (rc && rc != MDB_NOTFOUND) {
rc = cvtdberr(dbi, "mdb_dbi_open", rc, _debug);
if (txn) {
mdb_txn_abort(txn);
txn = NULL;
db = 0xdeadbeef;
}
}
if (txn) {
rc = mdb_txn_commit(txn);
if (rc)
rc = cvtdberr(dbi, "mdb_txn_commit", rc, _debug);
}
retry_open = 0;
} while (--retry_open > 0);
dbi->dbi_db = (void *) ((unsigned long)db);
if (!rc && dbip)
*dbip = dbi;
else
(void) dbiClose(dbi, 0);
exit:
return rc;
}
/* The LMDB btree implementation needs BIGENDIAN primary keys. */
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
static int _dbibyteswapped = 1;
#else
static int _dbibyteswapped = 0;
#endif
/**
* Convert retrieved data to index set.
* @param dbi index database handle
* @param data retrieved data
* @retval setp (malloc'ed) index set
* @return 0 on success
*/
static rpmRC dbt2set(dbiIndex dbi, MDB_val * data, dbiIndexSet * setp)
{
rpmRC rc = RPMRC_FAIL;
const char * sdbir;
dbiIndexSet set = NULL;
unsigned int i;
if (dbi == NULL || data == NULL || setp == NULL)
goto exit;
rc = RPMRC_OK;
if ((sdbir = data->mv_data) == NULL) {
*setp = NULL;
goto exit;
}
set = dbiIndexSetNew(data->mv_size / (2 * sizeof(int32_t)));
set->count = data->mv_size / (2 * sizeof(int32_t));
for (i = 0; i < set->count; i++) {
union _dbswap hdrNum, tagNum;
memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
sdbir += sizeof(hdrNum.ui);
memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui));
sdbir += sizeof(tagNum.ui);
if (_dbibyteswapped) {
_DBSWAP(hdrNum);
_DBSWAP(tagNum);
}
set->recs[i].hdrNum = hdrNum.ui;
set->recs[i].tagNum = tagNum.ui;
}
*setp = set;
exit:
return rc;
}
/**
* Convert index set to database representation.
* @param dbi index database handle
* @param data retrieved data
* @param set index set
* @return 0 on success
*/
static rpmRC set2dbt(dbiIndex dbi, MDB_val * data, dbiIndexSet set)
{
rpmRC rc = RPMRC_FAIL;
char * tdbir;
unsigned int i;
if (dbi == NULL || data == NULL || set == NULL)
goto exit;
rc = RPMRC_OK;
data->mv_size = set->count * (2 * sizeof(int32_t));
if (data->mv_size == 0) {
data->mv_data = NULL;
goto exit;
}
tdbir = data->mv_data = xmalloc(data->mv_size);
for (i = 0; i < set->count; i++) {
union _dbswap hdrNum, tagNum;
memset(&hdrNum, 0, sizeof(hdrNum));
memset(&tagNum, 0, sizeof(tagNum));
hdrNum.ui = set->recs[i].hdrNum;
tagNum.ui = set->recs[i].tagNum;
if (_dbibyteswapped) {
_DBSWAP(hdrNum);
_DBSWAP(tagNum);
}
memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
tdbir += sizeof(hdrNum.ui);
memcpy(tdbir, &tagNum.ui, sizeof(tagNum.ui));
tdbir += sizeof(tagNum.ui);
}
exit:
return rc;
}
static rpmRC lmdb_idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen,
dbiIndexSet *set, int searchType)
{
rpmRC rc = RPMRC_FAIL; /* assume failure */
if (dbi != NULL && dbc != NULL && set != NULL) {
int cflags = MDB_NEXT;
int dbrc;
MDB_val key = { 0, NULL };
MDB_val data = { 0, NULL };
if (keyp) {
if (keylen == 0) { /* XXX "/" fixup */
keyp = "";
keylen = 1;
}
key.mv_data = (void *) keyp; /* discards const */
key.mv_size = keylen;
cflags = searchType == DBC_PREFIX_SEARCH ? MDB_SET_RANGE : MDB_SET;
}
for (;;) {
dbiIndexSet newset = NULL;
dbrc = dbiCursorGet(dbc, &key, &data, cflags);
if (dbrc != 0)
break;
if (searchType == DBC_PREFIX_SEARCH &&
(key.mv_size < keylen || memcmp(key.mv_data, keyp, keylen) != 0))
break;
dbt2set(dbi, &data, &newset);
if (*set == NULL) {
*set = newset;
} else {
dbiIndexSetAppendSet(*set, newset, 0);
dbiIndexSetFree(newset);
}
if (searchType != DBC_PREFIX_SEARCH)
break;
key.mv_data = NULL;
key.mv_size = 0;
cflags = MDB_NEXT;
}
/* fixup result status for prefix search */
if (searchType == DBC_PREFIX_SEARCH) {
if (dbrc == MDB_NOTFOUND && *set != NULL && (*set)->count > 0)
dbrc = 0;
else if (dbrc == 0 && (*set == NULL || (*set)->count == 0))
dbrc = MDB_NOTFOUND;
}
if (dbrc == 0) {
rc = RPMRC_OK;
} else if (dbrc == MDB_NOTFOUND) {
rc = RPMRC_NOTFOUND;
} else {
rpmlog(RPMLOG_ERR,
_("rc(%d) getting \"%s\" records from %s index: %s\n"),
dbrc, keyp ? keyp : "???", dbiName(dbi), mdb_strerror(dbrc));
}
}
return rc;
}
/* Update secondary index. NULL set deletes the key */
static rpmRC updateIndex(dbiCursor dbc, const char *keyp, unsigned int keylen,
dbiIndexSet set)
{
rpmRC rc = RPMRC_FAIL;
if (dbc && keyp) {
dbiIndex dbi = dbc->dbi;
int dbrc;
MDB_val key = { 0, NULL };
MDB_val data = { 0, NULL };
key.mv_data = (void *) keyp; /* discards const */
key.mv_size = keylen;
if (set)
set2dbt(dbi, &data, set);
if (dbiIndexSetCount(set) > 0) {
dbrc = dbiCursorPut(dbc, &key, &data, 0);
if (dbrc) {
rpmlog(RPMLOG_ERR,
_("rc(%d) storing record \"%s\" into %s index: %s\n"),
dbrc, (char*)key.mv_data, dbiName(dbi), mdb_strerror(dbrc));
}
free(data.mv_data);
} else {
dbrc = dbiCursorDel(dbc, &key, &data, 0);
if (dbrc) {
rpmlog(RPMLOG_ERR,
_("rc(%d) removing record \"%s\" from %s index: %s\n"),
dbrc, (char*)key.mv_data, dbiName(dbi), mdb_strerror(dbrc));
}
}
if (dbrc == 0)
rc = RPMRC_OK;
}
return rc;
}
static rpmRC lmdb_idxdbPut(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen,
dbiIndexItem rec)
{
dbiIndexSet set = NULL;
rpmRC rc;
if (keyp && keylen == 0) { /* XXX "/" fixup */
keyp = "";
keylen++;
}
rc = idxdbGet(dbi, dbc, keyp, keylen, &set, DBC_NORMAL_SEARCH);
/* Not found means a new key and is not an error. */
if (rc && rc != RPMRC_NOTFOUND)
goto exit;
if (set == NULL)
set = dbiIndexSetNew(1);
dbiIndexSetAppend(set, rec, 1, 0);
rc = updateIndex(dbc, keyp, keylen, set);
dbiIndexSetFree(set);
exit:
return rc;
}
static rpmRC lmdb_idxdbDel(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen,
dbiIndexItem rec)
{
rpmRC rc = RPMRC_FAIL;
dbiIndexSet set = NULL;
if (keyp && keylen == 0) { /* XXX "/" fixup */
keyp = "";
keylen++;
}
rc = idxdbGet(dbi, dbc, keyp, keylen, &set, DBC_NORMAL_SEARCH);
if (rc)
goto exit;
if (dbiIndexSetPrune(set, rec, 1, 1)) {
/* Nothing was pruned. XXX: Can this actually happen? */
rc = RPMRC_OK;
} else {
/* If there's data left, update data. Otherwise delete the key. */
if (dbiIndexSetCount(set) > 0) {
rc = updateIndex(dbc, keyp, keylen, set);
} else {
rc = updateIndex(dbc, keyp, keylen, NULL);
}
};
dbiIndexSetFree(set);
exit:
return rc;
}
static const void * lmdb_idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen)
{
const void *key = NULL;
if (dbc) {
key = dbc->key;
if (key && keylen)
*keylen = dbc->keylen;
}
return key;
}
/* Update primary Packages index. NULL hdr means remove */
static rpmRC updatePackages(dbiCursor dbc, unsigned int hdrNum, MDB_val *hdr)
{
int rc = RPMRC_FAIL;
int dbrc = EINVAL;
if (dbc == NULL || hdrNum == 0)
goto exit;
union _dbswap mi_offset;
mi_offset.ui = hdrNum;
if (_dbibyteswapped)
_DBSWAP(mi_offset);
MDB_val key = { 0, NULL };
key.mv_data = (void *) &mi_offset;
key.mv_size = sizeof(mi_offset.ui);
MDB_val data = { 0, NULL };
dbrc = dbiCursorGet(dbc, &key, &data, MDB_SET);
if (dbrc && dbrc != MDB_NOTFOUND) {
rpmlog(RPMLOG_ERR,
_("rc(%d) positioning header #%d record: %s\n"), dbrc, hdrNum, mdb_strerror(dbrc));
goto exit;
}
if (hdr) {
dbrc = dbiCursorPut(dbc, &key, hdr, 0);
if (dbrc) {
rpmlog(RPMLOG_ERR,
_("rc(%d) adding header #%d record: %s\n"), dbrc, hdrNum, mdb_strerror(dbrc));
}
} else {
dbrc = dbiCursorDel(dbc, &key, &data, 0);
if (dbrc) {
rpmlog(RPMLOG_ERR,
_("rc(%d) deleting header #%d record: %s\n"), dbrc, hdrNum, mdb_strerror(dbrc));
}
}
exit:
rc = dbrc == 0 ? RPMRC_OK : RPMRC_FAIL;
return rc;
}
/* Get current header instance number or try to allocate a new one */
static unsigned int pkgInstance(dbiCursor dbc, int alloc)
{
unsigned int hdrNum = 0;
MDB_val key = { 0, NULL };
MDB_val data = { 0, NULL };
unsigned int firstkey = 0;
union _dbswap mi_offset;
int rc;
/* Key 0 holds the current largest instance, fetch it */
key.mv_data = &firstkey;
key.mv_size = sizeof(firstkey);
rc = dbiCursorGet(dbc, &key, &data, MDB_SET);
if (!rc && data.mv_data) {
memcpy(&mi_offset, data.mv_data, sizeof(mi_offset.ui));
if (_dbibyteswapped)
_DBSWAP(mi_offset);
hdrNum = mi_offset.ui;
}
if (alloc) {
/* Rather complicated "increment by one", bswapping as needed */
++hdrNum;
mi_offset.ui = hdrNum;
if (_dbibyteswapped)
_DBSWAP(mi_offset);
data.mv_data = &mi_offset;
data.mv_size = sizeof(mi_offset.ui);
/* Unless we manage to insert the new instance number, we failed */
rc = dbiCursorPut(dbc, &key, &data, 0);
if (rc) {
hdrNum = 0;
rpmlog(RPMLOG_ERR,
_("rc(%d) allocating new package instance: %s\n"), rc, mdb_strerror(rc));
}
}
return hdrNum;
}
static rpmRC lmdb_pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum,
unsigned char *hdrBlob, unsigned int hdrLen)
{
MDB_val hdr;
hdr.mv_data = hdrBlob;
hdr.mv_size = hdrLen;
return updatePackages(dbc, hdrNum, &hdr);
}
static rpmRC lmdb_pkgdbDel(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum)
{
return updatePackages(dbc, hdrNum, NULL);
}
static rpmRC lmdb_pkgdbGet(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum,
unsigned char **hdrBlob, unsigned int *hdrLen)
{
union _dbswap mi_offset;
MDB_val key = { 0, NULL };
MDB_val data = { 0, NULL };
rpmRC rc = RPMRC_FAIL;
if (dbc == NULL)
goto exit;
if (hdrNum) {
mi_offset.ui = hdrNum;
if (_dbibyteswapped)
_DBSWAP(mi_offset);
key.mv_data = (void *) &mi_offset;
key.mv_size = sizeof(mi_offset.ui);
}
rc = dbiCursorGet(dbc, &key, &data, hdrNum ? MDB_SET : MDB_NEXT);
if (rc == 0) {
if (hdrBlob)
*hdrBlob = data.mv_data;
if (hdrLen)
*hdrLen = data.mv_size;
rc = RPMRC_OK;
} else if (rc == MDB_NOTFOUND)
rc = RPMRC_NOTFOUND;
else
rc = RPMRC_FAIL;
exit:
return rc;
}
static unsigned int lmdb_pkgdbKey(dbiIndex dbi, dbiCursor dbc)
{
union _dbswap mi_offset;
if (dbc == NULL || dbc->key == NULL)
return 0;
memcpy(&mi_offset, dbc->key, sizeof(mi_offset.ui));
if (_dbibyteswapped)
_DBSWAP(mi_offset);
return mi_offset.ui;
}
static rpmRC lmdb_pkgdbNew(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum)
{
unsigned int num;
rpmRC rc = RPMRC_FAIL;
if (dbc == NULL)
goto exit;
num = pkgInstance(dbc, 1);
if (num) {
*hdrNum = num;
rc = RPMRC_OK;
}
exit:
return rc;
}
struct rpmdbOps_s lmdb_dbops = {
.open = lmdb_dbiOpen,
.close = lmdb_dbiClose,
.verify = lmdb_dbiVerify,
.setFSync = lmdb_dbSetFSync,
.ctrl = lmdb_Ctrl,
.cursorInit = lmdb_dbiCursorInit,
.cursorFree = lmdb_dbiCursorFree,
.pkgdbGet = lmdb_pkgdbGet,
.pkgdbPut = lmdb_pkgdbPut,
.pkgdbDel = lmdb_pkgdbDel,
.pkgdbNew = lmdb_pkgdbNew,
.pkgdbKey = lmdb_pkgdbKey,
.idxdbGet = lmdb_idxdbGet,
.idxdbPut = lmdb_idxdbPut,
.idxdbDel = lmdb_idxdbDel,
.idxdbKey = lmdb_idxdbKey
};