Skip to content

Commit

Permalink
Implement a database cookie API for determining whether rpmdb has cha…
Browse files Browse the repository at this point in the history
…nged

Add rpmdbCookie() public function and matching python bindings.
The returned value is an opaque string which changes any time the rpmdb
is changed (eg packages added or removed, rebuild changes things etc).
A key point is that this is entirely database backend agnostic.

The actual implementation here is an SHA1 hash of RPMDBI_NAME keys and
the corresponding package offsets, but this is an implementation detail
that can be changed anytime and callers must not rely on. The only thing
that matters is whether the string equals an earlier cookie value or not.
A cryptographic hash seems like an overkill, but then it saves us the
headaches of coming up with something that reflects order changes etc.

Closes: rpm-software-management#388
  • Loading branch information
pmatilai committed May 23, 2019
1 parent aa095c1 commit 83d40ef
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 0 deletions.
20 changes: 20 additions & 0 deletions lib/rpmdb.c
Expand Up @@ -2644,3 +2644,23 @@ 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) {
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;
}
8 changes: 8 additions & 0 deletions lib/rpmdb.h
Expand Up @@ -218,6 +218,14 @@ 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);

#ifdef __cplusplus
}
#endif
Expand Down
18 changes: 18 additions & 0 deletions python/rpmts-py.c
Expand Up @@ -366,6 +366,21 @@ rpmts_VerifyDB(rpmtsObject * s)
return Py_BuildValue("i", rc);
}

static PyObject *
rpmts_dbCookie(rpmtsObject * s)
{
PyObject *ret = NULL;
char *cookie = NULL;

Py_BEGIN_ALLOW_THREADS
cookie = rpmdbCookie(rpmtsGetRdb(s->ts));
Py_END_ALLOW_THREADS

ret = utf8FromString(cookie);
free(cookie);
return ret;
}

static PyObject *
rpmts_HdrFromFdno(rpmtsObject * s, PyObject *arg)
{
Expand Down Expand Up @@ -797,6 +812,9 @@ Remove all elements from the transaction set\n" },
{"dbIndex", (PyCFunction) rpmts_index, METH_VARARGS|METH_KEYWORDS,
"ts.dbIndex(TagN) -> ii\n\
- Create a key iterator for the default transaction rpmdb.\n" },
{"dbCookie", (PyCFunction) rpmts_dbCookie, METH_NOARGS,
"dbCookie -> cookie\n\
- Return a cookie string for determining if database has changed\n" },
{NULL, NULL} /* sentinel */
};

Expand Down
69 changes: 69 additions & 0 deletions tests/rpmpython.at
Expand Up @@ -368,6 +368,75 @@ hi
[])
AT_CLEANUP

AT_SETUP([database cookies])
AT_KEYWORDS([python rpmdb])
AT_CHECK([
RPMDB_CLEAR
RPMDB_INIT
],
[0],
[],
[])

RPMPY_CHECK([
ts = rpm.ts()
ts.openDB()
c1 = ts.dbCookie()
ts.closeDB()
ts.openDB()
c2 = ts.dbCookie()
myprint(c1 == c2 != None)
open("dbcookie", "w+").write(c1)
],
[True
],
[])

AT_CHECK([
runroot rpm -i \
--justdb --nodeps --ignorearch --ignoreos \
/data/RPMS/foo-1.0-1.noarch.rpm \
/data/RPMS/hello-2.0-1.i686.rpm \
],
[0],
[],
[])

RPMPY_CHECK([
ts = rpm.ts()
ts.openDB()
c1 = ts.dbCookie()
c2 = open("dbcookie", "r").read()
myprint(c1 != c2)
open("dbcookie", "w+").write(c1)
],
[True
],
[])

AT_CHECK([
runroot rpm -i \
--justdb --nodeps --ignorearch --ignoreos \
--define "_transaction_color 3" \
--define "_prefer_color 2" \
/data/RPMS/hello-2.0-1.x86_64.rpm
],
[0],
[],
[])

RPMPY_CHECK([
ts = rpm.ts()
ts.openDB()
c1 = ts.dbCookie()
c2 = open("dbcookie", "r").read()
myprint(c1 != c2)
],
[True
],
[])
AT_CLEANUP

RPMPY_TEST([dependency sets 1],[
ts = rpm.ts()
h = ts.hdrFromFdno('${RPMDATA}/RPMS/hello-1.0-1.ppc64.rpm')
Expand Down

0 comments on commit 83d40ef

Please sign in to comment.