2 * Copyright (c) 2018 SUSE Inc.
4 * This program is licensed under the BSD license, read LICENSE.BSD
5 * for further information
11 * Use BerkeleyDB to access the rpm database
16 #if !defined(DB_CREATE) && !defined(ENABLE_RPMDB_LIBRPM)
17 # if defined(SUSE) || defined(HAVE_RPM_DB_H)
25 # include <rpm/rpmversion.h>
26 # if RPMLIB_VERSION < RPMLIB_VERSION_ENCODE(5,3,_,0,0,_)
27 # define RPM_INDEX_SIZE 8 /* rpmdbid + array index */
29 # define RPM_INDEX_SIZE 4 /* just the rpmdbid */
30 # define RPM5_BIG_ENDIAN_ID
33 # define RPM_INDEX_SIZE 8 /* rpmdbid + array index */
37 /******************************************************************/
45 RpmHead *rpmhead; /* header storage space */
46 unsigned int rpmheadsize;
48 int dbenvopened; /* database environment opened */
49 int pkgdbopened; /* package database openend */
50 const char *dbpath; /* path to the database */
52 DB_ENV *dbenv; /* database environment */
53 DB *db; /* packages database */
54 int byteswapped; /* endianess of packages database */
55 DBC *dbc; /* iterator over packages database */
60 access_rootdir(struct rpmdbstate *state, const char *dir, int mode)
64 char *path = solv_dupjoin(state->rootdir, dir, 0);
65 int r = access(path, mode);
69 return access(dir, mode);
73 detect_dbpath(struct rpmdbstate *state)
75 state->dbpath = access_rootdir(state, "/var/lib/rpm", W_OK) == -1
76 && access_rootdir(state, "/usr/share/rpm/Packages", R_OK) == 0
77 ? "/usr/share/rpm" : "/var/lib/rpm";
81 stat_database_name(struct rpmdbstate *state, char *dbname, struct stat *statbuf, int seterror)
86 dbpath = solv_dupjoin(state->rootdir, state->dbpath, dbname);
87 if (stat(dbpath, statbuf))
90 pool_error(state->pool, -1, "%s: %s", dbpath, strerror(errno));
99 stat_database(struct rpmdbstate *state, struct stat *statbuf)
101 return stat_database_name(state, "/Packages", statbuf, 1);
106 db2rpmdbid(unsigned char *db, int byteswapped)
108 #ifdef RPM5_BIG_ENDIAN_ID
109 return db[0] << 24 | db[1] << 16 | db[2] << 8 | db[3];
111 # if defined(WORDS_BIGENDIAN)
116 return db[0] << 24 | db[1] << 16 | db[2] << 8 | db[3];
118 return db[3] << 24 | db[2] << 16 | db[1] << 8 | db[0];
123 rpmdbid2db(unsigned char *db, Id id, int byteswapped)
125 #ifdef RPM5_BIG_ENDIAN_ID
126 db[0] = id >> 24, db[1] = id >> 16, db[2] = id >> 8, db[3] = id;
128 # if defined(WORDS_BIGENDIAN)
133 db[0] = id >> 24, db[1] = id >> 16, db[2] = id >> 8, db[3] = id;
135 db[3] = id >> 24, db[2] = id >> 16, db[1] = id >> 8, db[0] = id;
139 #if defined(FEDORA) || defined(MAGEIA)
141 serialize_dbenv_ops(struct rpmdbstate *state)
148 lpath = solv_dupjoin(state->rootdir, "/var/lib/rpm/.dbenv.lock", 0);
149 oldmask = umask(022);
150 fd = open(lpath, (O_RDWR|O_CREAT), 0644);
155 memset(&fl, 0, sizeof(fl));
157 fl.l_whence = SEEK_SET;
160 if (fcntl(fd, F_SETLKW, &fl) != -1)
171 /* should look in /usr/lib/rpm/macros instead, but we want speed... */
173 opendbenv(struct rpmdbstate *state)
179 if (db_env_create(&dbenv, 0))
180 return pool_error(state->pool, 0, "db_env_create: %s", strerror(errno));
181 #if (defined(FEDORA) || defined(MAGEIA)) && (DB_VERSION_MAJOR >= 5 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 5))
182 dbenv->set_thread_count(dbenv, 8);
184 state->dbpath = "/var/lib/rpm";
185 dbpath = solv_dupjoin(state->rootdir, state->dbpath, 0);
186 if (access(dbpath, W_OK) == -1)
188 if (access_rootdir(state, "/usr/share/rpm/Packages", R_OK) == 0)
190 state->dbpath = "/usr/share/rpm";
192 dbpath = solv_dupjoin(state->rootdir, state->dbpath, 0);
194 r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
198 #if defined(FEDORA) || defined(MAGEIA)
199 int serialize_fd = serialize_dbenv_ops(state);
200 int eflags = DB_CREATE|DB_INIT_CDB|DB_INIT_MPOOL;
201 r = dbenv->open(dbenv, dbpath, eflags, 0644);
202 /* see rpm commit 2822ccbcdf3e898b960fafb23c4d571e26cef0a4 */
203 if (r == DB_VERSION_MISMATCH)
205 eflags |= DB_PRIVATE;
206 dbenv->errx(dbenv, "warning: DB_VERSION_MISMATCH, retrying with DB_PRIVATE");
207 r = dbenv->open(dbenv, dbpath, eflags, 0644);
209 if (serialize_fd >= 0)
212 r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
217 pool_error(state->pool, 0, "dbenv->open: %s", strerror(errno));
219 dbenv->close(dbenv, 0);
223 state->dbenv = dbenv;
224 state->dbenvopened = 1;
229 closedbenv(struct rpmdbstate *state)
231 #if defined(FEDORA) || defined(MAGEIA)
237 state->db->close(state->db, 0);
240 state->pkgdbopened = 0;
243 #if defined(FEDORA) || defined(MAGEIA)
244 (void)state->dbenv->get_open_flags(state->dbenv, &eflags);
245 if (!(eflags & DB_PRIVATE))
247 int serialize_fd = serialize_dbenv_ops(state);
248 state->dbenv->close(state->dbenv, 0);
249 if (serialize_fd >= 0)
253 state->dbenv->close(state->dbenv, 0);
255 state->dbenv->close(state->dbenv, 0);
258 state->dbenvopened = 0;
262 openpkgdb(struct rpmdbstate *state)
264 if (state->pkgdbopened)
265 return state->pkgdbopened > 0 ? 1 : 0;
266 state->pkgdbopened = -1;
267 if (state->dbenvopened != 1 && !opendbenv(state))
269 if (db_create(&state->db, state->dbenv, 0))
271 pool_error(state->pool, 0, "db_create: %s", strerror(errno));
276 if (state->db->open(state->db, 0, "Packages", 0, DB_UNKNOWN, DB_RDONLY, 0664))
278 pool_error(state->pool, 0, "db->open Packages: %s", strerror(errno));
279 state->db->close(state->db, 0);
284 if (state->db->get_byteswapped(state->db, &state->byteswapped))
286 pool_error(state->pool, 0, "db->get_byteswapped: %s", strerror(errno));
287 state->db->close(state->db, 0);
292 state->pkgdbopened = 1;
296 /* get the rpmdbids of all installed packages from the Name index database.
297 * This is much faster then querying the big Packages database */
298 static struct rpmdbentry *
299 getinstalledrpmdbids(struct rpmdbstate *state, const char *index, const char *match, int *nentriesp, char **namedatap, int keep_gpg_pubkey)
313 struct rpmdbentry *entries = 0;
320 if (state->dbenvopened != 1 && !opendbenv(state))
322 dbenv = state->dbenv;
323 if (db_create(&db, dbenv, 0))
325 pool_error(state->pool, 0, "db_create: %s", strerror(errno));
328 if (db->open(db, 0, index, 0, DB_UNKNOWN, DB_RDONLY, 0664))
330 pool_error(state->pool, 0, "db->open %s: %s", index, strerror(errno));
334 if (db->get_byteswapped(db, &byteswapped))
336 pool_error(state->pool, 0, "db->get_byteswapped: %s", strerror(errno));
340 if (db->cursor(db, NULL, &dbc, 0))
342 pool_error(state->pool, 0, "db->cursor: %s", strerror(errno));
346 memset(&dbkey, 0, sizeof(dbkey));
347 memset(&dbdata, 0, sizeof(dbdata));
350 dbkey.data = (void *)match;
351 dbkey.size = strlen(match);
353 while (dbc->c_get(dbc, &dbkey, &dbdata, match ? DB_SET : DB_NEXT) == 0)
355 if (!match && !keep_gpg_pubkey && dbkey.size == 10 && !memcmp(dbkey.data, "gpg-pubkey", 10))
362 namedata = solv_extend(namedata, namedatal, dbkey.size + 1, 1, NAMEDATA_BLOCK);
363 memcpy(namedata + namedatal, dbkey.data, dbkey.size);
364 namedata[namedatal + dbkey.size] = 0;
365 namedatal += dbkey.size + 1;
367 while(dl >= RPM_INDEX_SIZE)
369 entries = solv_extend(entries, nentries, 1, sizeof(*entries), ENTRIES_BLOCK);
370 entries[nentries].rpmdbid = db2rpmdbid(dp, byteswapped);
371 entries[nentries].nameoff = nameoff;
373 dp += RPM_INDEX_SIZE;
374 dl -= RPM_INDEX_SIZE;
381 /* make sure that enteries is != 0 if there was no error */
383 entries = solv_extend(entries, 1, 1, sizeof(*entries), ENTRIES_BLOCK);
384 *nentriesp = nentries;
386 *namedatap = namedata;
390 static int headfromhdrblob(struct rpmdbstate *state, const unsigned char *data, unsigned int size);
392 /* retrive header by rpmdbid, returns 0 if not found, -1 on error */
394 getrpm_dbid(struct rpmdbstate *state, Id dbid)
396 unsigned char buf[4];
401 return pool_error(state->pool, -1, "illegal rpmdbid %d", dbid);
402 if (state->pkgdbopened != 1 && !openpkgdb(state))
404 rpmdbid2db(buf, dbid, state->byteswapped);
405 memset(&dbkey, 0, sizeof(dbkey));
406 memset(&dbdata, 0, sizeof(dbdata));
411 if (state->db->get(state->db, NULL, &dbkey, &dbdata, 0))
413 if (!headfromhdrblob(state, (const unsigned char *)dbdata.data, (unsigned int)dbdata.size))
419 count_headers(struct rpmdbstate *state)
421 Pool *pool = state->pool;
429 if (stat_database_name(state, "/Name", &statbuf, 0))
431 memset(&dbkey, 0, sizeof(dbkey));
432 memset(&dbdata, 0, sizeof(dbdata));
433 if (db_create(&db, state->dbenv, 0))
435 pool_error(pool, 0, "db_create: %s", strerror(errno));
438 if (db->open(db, 0, "Name", 0, DB_UNKNOWN, DB_RDONLY, 0664))
440 pool_error(pool, 0, "db->open Name: %s", strerror(errno));
444 if (db->cursor(db, NULL, &dbc, 0))
447 pool_error(pool, 0, "db->cursor: %s", strerror(errno));
450 while (dbc->c_get(dbc, &dbkey, &dbdata, DB_NEXT) == 0)
451 count += dbdata.size / RPM_INDEX_SIZE;
458 pkgdb_cursor_open(struct rpmdbstate *state)
460 if (state->pkgdbopened != 1 && !openpkgdb(state))
462 if (state->db->cursor(state->db, NULL, &state->dbc, 0))
463 return pool_error(state->pool, -1, "db->cursor failed");
468 pkgdb_cursor_close(struct rpmdbstate *state)
470 state->dbc->c_close(state->dbc);
474 /* retrive header by berkeleydb cursor, returns 0 on EOF, -1 on error */
476 pkgdb_cursor_getrpm(struct rpmdbstate *state)
482 memset(&dbkey, 0, sizeof(dbkey));
483 memset(&dbdata, 0, sizeof(dbdata));
484 while (state->dbc->c_get(state->dbc, &dbkey, &dbdata, DB_NEXT) == 0)
487 return pool_error(state->pool, -1, "corrupt Packages database (key size)");
488 dbid = db2rpmdbid(dbkey.data, state->byteswapped);
490 continue; /* ignore join key */
491 if (!headfromhdrblob(state, (const unsigned char *)dbdata.data, (unsigned int)dbdata.size))
495 return 0; /* no more entries */
499 hash_name_index(struct rpmdbstate *state, Chksum *chk)
506 detect_dbpath(state);
507 dbpath = solv_dupjoin(state->rootdir, state->dbpath, "/Name");
508 if ((fd = open(dbpath, O_RDONLY)) < 0)
510 while ((l = read(fd, buf, sizeof(buf))) > 0)
511 solv_chksum_add(chk, buf, l);