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 # define RPM_INDEX_SIZE 4 /* just the rpmdbid */
27 # define RPM_INDEX_SIZE 8 /* rpmdbid + array index */
31 /******************************************************************/
39 RpmHead *rpmhead; /* header storage space */
42 int dbenvopened; /* database environment opened */
43 int pkgdbopened; /* package database openend */
44 int is_ostree; /* read-only db that lives in /usr/share/rpm */
46 DB_ENV *dbenv; /* database environment */
47 DB *db; /* packages database */
48 int byteswapped; /* endianess of packages database */
49 DBC *dbc; /* iterator over packages database */
54 stat_database(struct rpmdbstate *state, char *dbname, struct stat *statbuf, int seterror)
57 dbpath = solv_dupjoin(state->rootdir, state->is_ostree ? "/usr/share/rpm/" : "/var/lib/rpm/", dbname);
58 if (stat(dbpath, statbuf))
61 pool_error(state->pool, -1, "%s: %s", dbpath, strerror(errno));
71 db2rpmdbid(unsigned char *db, int byteswapped)
74 return db[0] << 24 | db[1] << 16 | db[2] << 8 | db[3];
76 # if defined(WORDS_BIGENDIAN)
81 return db[0] << 24 | db[1] << 16 | db[2] << 8 | db[3];
83 return db[3] << 24 | db[2] << 16 | db[1] << 8 | db[0];
88 rpmdbid2db(unsigned char *db, Id id, int byteswapped)
91 db[0] = id >> 24, db[1] = id >> 16, db[2] = id >> 8, db[3] = id;
93 # if defined(WORDS_BIGENDIAN)
98 db[0] = id >> 24, db[1] = id >> 16, db[2] = id >> 8, db[3] = id;
100 db[3] = id >> 24, db[2] = id >> 16, db[1] = id >> 8, db[0] = id;
104 #if defined(FEDORA) || defined(MAGEIA)
106 serialize_dbenv_ops(struct rpmdbstate *state)
113 lpath = solv_dupjoin(state->rootdir, "/var/lib/rpm/.dbenv.lock", 0);
114 oldmask = umask(022);
115 fd = open(lpath, (O_RDWR|O_CREAT), 0644);
120 memset(&fl, 0, sizeof(fl));
122 fl.l_whence = SEEK_SET;
125 if (fcntl(fd, F_SETLKW, &fl) != -1)
136 /* should look in /usr/lib/rpm/macros instead, but we want speed... */
138 opendbenv(struct rpmdbstate *state)
140 const char *rootdir = state->rootdir;
145 if (db_env_create(&dbenv, 0))
146 return pool_error(state->pool, 0, "db_env_create: %s", strerror(errno));
147 #if (defined(FEDORA) || defined(MAGEIA)) && (DB_VERSION_MAJOR >= 5 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 5))
148 dbenv->set_thread_count(dbenv, 8);
150 dbpath = solv_dupjoin(rootdir, "/var/lib/rpm", 0);
151 if (access(dbpath, W_OK) == -1)
154 dbpath = solv_dupjoin(rootdir, "/usr/share/rpm/Packages", 0);
155 if (access(dbpath, R_OK) == 0)
156 state->is_ostree = 1;
158 dbpath = solv_dupjoin(rootdir, state->is_ostree ? "/usr/share/rpm" : "/var/lib/rpm", 0);
159 r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
163 #if defined(FEDORA) || defined(MAGEIA)
164 int serialize_fd = serialize_dbenv_ops(state);
165 int eflags = DB_CREATE|DB_INIT_CDB|DB_INIT_MPOOL;
166 r = dbenv->open(dbenv, dbpath, eflags, 0644);
167 /* see rpm commit 2822ccbcdf3e898b960fafb23c4d571e26cef0a4 */
168 if (r == DB_VERSION_MISMATCH)
170 eflags |= DB_PRIVATE;
171 dbenv->errx(dbenv, "warning: DB_VERSION_MISMATCH, retrying with DB_PRIVATE");
172 r = dbenv->open(dbenv, dbpath, eflags, 0644);
174 if (serialize_fd >= 0)
177 r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
182 pool_error(state->pool, 0, "dbenv->open: %s", strerror(errno));
184 dbenv->close(dbenv, 0);
188 state->dbenv = dbenv;
189 state->dbenvopened = 1;
194 closedbenv(struct rpmdbstate *state)
196 #if defined(FEDORA) || defined(MAGEIA)
202 #if defined(FEDORA) || defined(MAGEIA)
203 (void)state->dbenv->get_open_flags(state->dbenv, &eflags);
204 if (!(eflags & DB_PRIVATE))
206 int serialize_fd = serialize_dbenv_ops(state);
207 state->dbenv->close(state->dbenv, 0);
208 if (serialize_fd >= 0)
212 state->dbenv->close(state->dbenv, 0);
214 state->dbenv->close(state->dbenv, 0);
217 state->dbenvopened = 0;
221 openpkgdb(struct rpmdbstate *state)
223 if (state->pkgdbopened)
224 return state->pkgdbopened > 0 ? 1 : 0;
225 state->pkgdbopened = -1;
226 if (state->dbenvopened != 1 && !opendbenv(state))
228 if (db_create(&state->db, state->dbenv, 0))
230 pool_error(state->pool, 0, "db_create: %s", strerror(errno));
235 if (state->db->open(state->db, 0, "Packages", 0, DB_UNKNOWN, DB_RDONLY, 0664))
237 pool_error(state->pool, 0, "db->open Packages: %s", strerror(errno));
238 state->db->close(state->db, 0);
243 if (state->db->get_byteswapped(state->db, &state->byteswapped))
245 pool_error(state->pool, 0, "db->get_byteswapped: %s", strerror(errno));
246 state->db->close(state->db, 0);
251 state->pkgdbopened = 1;
256 closepkgdb(struct rpmdbstate *state)
260 state->db->close(state->db, 0);
262 state->pkgdbopened = 0;
265 /* get the rpmdbids of all installed packages from the Name index database.
266 * This is much faster then querying the big Packages database */
267 static struct rpmdbentry *
268 getinstalledrpmdbids(struct rpmdbstate *state, const char *index, const char *match, int *nentriesp, char **namedatap, int keep_gpg_pubkey)
282 struct rpmdbentry *entries = 0;
289 if (state->dbenvopened != 1 && !opendbenv(state))
291 dbenv = state->dbenv;
292 if (db_create(&db, dbenv, 0))
294 pool_error(state->pool, 0, "db_create: %s", strerror(errno));
297 if (db->open(db, 0, index, 0, DB_UNKNOWN, DB_RDONLY, 0664))
299 pool_error(state->pool, 0, "db->open %s: %s", index, strerror(errno));
303 if (db->get_byteswapped(db, &byteswapped))
305 pool_error(state->pool, 0, "db->get_byteswapped: %s", strerror(errno));
309 if (db->cursor(db, NULL, &dbc, 0))
311 pool_error(state->pool, 0, "db->cursor: %s", strerror(errno));
315 memset(&dbkey, 0, sizeof(dbkey));
316 memset(&dbdata, 0, sizeof(dbdata));
319 dbkey.data = (void *)match;
320 dbkey.size = strlen(match);
322 while (dbc->c_get(dbc, &dbkey, &dbdata, match ? DB_SET : DB_NEXT) == 0)
324 if (!match && !keep_gpg_pubkey && dbkey.size == 10 && !memcmp(dbkey.data, "gpg-pubkey", 10))
331 namedata = solv_extend(namedata, namedatal, dbkey.size + 1, 1, NAMEDATA_BLOCK);
332 memcpy(namedata + namedatal, dbkey.data, dbkey.size);
333 namedata[namedatal + dbkey.size] = 0;
334 namedatal += dbkey.size + 1;
336 while(dl >= RPM_INDEX_SIZE)
338 entries = solv_extend(entries, nentries, 1, sizeof(*entries), ENTRIES_BLOCK);
339 entries[nentries].rpmdbid = db2rpmdbid(dp, byteswapped);
340 entries[nentries].nameoff = nameoff;
342 dp += RPM_INDEX_SIZE;
343 dl -= RPM_INDEX_SIZE;
350 /* make sure that enteries is != 0 if there was no error */
352 entries = solv_extend(entries, 1, 1, sizeof(*entries), ENTRIES_BLOCK);
353 *nentriesp = nentries;
355 *namedatap = namedata;
359 /* common code, return dbid on success, -1 on error */
361 getrpm_dbdata(struct rpmdbstate *state, DBT *dbdata, int dbid)
363 unsigned int dsize, cnt, l;
366 if (dbdata->size < 8)
367 return pool_error(state->pool, -1, "corrupt rpm database (size)");
368 cnt = getu32((const unsigned char *)dbdata->data);
369 dsize = getu32((const unsigned char *)dbdata->data + 4);
370 if (cnt >= MAX_HDR_CNT || dsize >= MAX_HDR_DSIZE)
371 return pool_error(state->pool, -1, "corrupt rpm database (cnt/dcnt)");
372 l = cnt * 16 + dsize;
373 if (8 + l > dbdata->size)
374 return pool_error(state->pool, -1, "corrupt rpm database (data size)");
375 if (l + 1 > state->rpmheadsize)
377 state->rpmheadsize = l + 128;
378 state->rpmhead = solv_realloc(state->rpmhead, sizeof(*rpmhead) + state->rpmheadsize);
380 rpmhead = state->rpmhead;
382 rpmhead->dcnt = dsize;
383 memcpy(rpmhead->data, (unsigned char *)dbdata->data + 8, l);
384 rpmhead->data[l] = 0;
385 rpmhead->dp = rpmhead->data + cnt * 16;
389 /* retrive header by rpmdbid, returns 0 if not found, -1 on error */
391 getrpm_dbid(struct rpmdbstate *state, Id dbid)
393 unsigned char buf[4];
398 return pool_error(state->pool, -1, "illegal rpmdbid %d", dbid);
399 if (state->pkgdbopened != 1 && !openpkgdb(state))
401 rpmdbid2db(buf, dbid, state->byteswapped);
402 memset(&dbkey, 0, sizeof(dbkey));
403 memset(&dbdata, 0, sizeof(dbdata));
408 if (state->db->get(state->db, NULL, &dbkey, &dbdata, 0))
410 return getrpm_dbdata(state, &dbdata, dbid);
414 count_headers(struct rpmdbstate *state)
416 Pool *pool = state->pool;
424 if (stat_database(state, "Name", &statbuf, 0))
426 memset(&dbkey, 0, sizeof(dbkey));
427 memset(&dbdata, 0, sizeof(dbdata));
428 if (db_create(&db, state->dbenv, 0))
430 pool_error(pool, 0, "db_create: %s", strerror(errno));
433 if (db->open(db, 0, "Name", 0, DB_UNKNOWN, DB_RDONLY, 0664))
435 pool_error(pool, 0, "db->open Name: %s", strerror(errno));
439 if (db->cursor(db, NULL, &dbc, 0))
442 pool_error(pool, 0, "db->cursor: %s", strerror(errno));
445 while (dbc->c_get(dbc, &dbkey, &dbdata, DB_NEXT) == 0)
446 count += dbdata.size / RPM_INDEX_SIZE;
453 pkgdb_cursor_open(struct rpmdbstate *state)
455 if (state->db->cursor(state->db, NULL, &state->dbc, 0))
456 return pool_error(state->pool, -1, "db->cursor failed");
461 pkgdb_cursor_close(struct rpmdbstate *state)
463 state->dbc->c_close(state->dbc);
467 /* retrive header by berkeleydb cursor, returns 0 on EOF, -1 on error */
469 pkgdb_cursor_getrpm(struct rpmdbstate *state)
475 memset(&dbkey, 0, sizeof(dbkey));
476 memset(&dbdata, 0, sizeof(dbdata));
477 while (state->dbc->c_get(state->dbc, &dbkey, &dbdata, DB_NEXT) == 0)
480 return pool_error(state->pool, -1, "corrupt Packages database (key size)");
481 dbid = db2rpmdbid(dbkey.data, state->byteswapped);
482 if (dbid) /* ignore join key */
483 return getrpm_dbdata(state, &dbdata, dbid);
485 return 0; /* no more entries */