1 /** \ingroup rpmdb
2 * \file lib/db3.c
3 */
4
5 static int _debug = 1; /* XXX if < 0 debugging, > 0 unusual error returns */
6
7 #include "system.h"
8
9 #include <errno.h>
10 #include <sys/wait.h>
11 #include <popt.h>
12 #include <db.h>
13 #include <signal.h>
14 #include <fcntl.h>
15
16 #include <rpm/rpmtypes.h>
17 #include <rpm/rpmmacro.h>
18 #include <rpm/rpmfileutil.h>
19 #include <rpm/rpmlog.h>
20
21 #include "lib/rpmdb_internal.h"
22
23 #include "debug.h"
24
25 static const char * _errpfx = "rpmdb";
26
27 struct dbiCursor_s {
28 dbiIndex dbi;
29 const void *key;
30 unsigned int keylen;
31 int flags;
32 DBC *cursor;
33 };
34
35 static struct dbiConfig_s staticdbicfg;
36 static struct dbConfig_s staticcfg;
37
38 /** \ingroup dbi
39 */
40 static const struct poptOption rdbOptions[] = {
41 /* Environment options */
42
43 { "cdb", 0,POPT_BIT_SET, &staticcfg.db_eflags, DB_INIT_CDB,
44 NULL, NULL },
45 { "lock", 0,POPT_BIT_SET, &staticcfg.db_eflags, DB_INIT_LOCK,
46 NULL, NULL },
47 { "log", 0,POPT_BIT_SET, &staticcfg.db_eflags, DB_INIT_LOG,
48 NULL, NULL },
49 { "txn", 0,POPT_BIT_SET, &staticcfg.db_eflags, DB_INIT_TXN,
50 NULL, NULL },
51 { "recover", 0,POPT_BIT_SET, &staticcfg.db_eflags, DB_RECOVER,
52 NULL, NULL },
53 { "recover_fatal", 0,POPT_BIT_SET, &staticcfg.db_eflags, DB_RECOVER_FATAL,
54 NULL, NULL },
55 { "lockdown", 0,POPT_BIT_SET, &staticcfg.db_eflags, DB_LOCKDOWN,
56 NULL, NULL },
57 { "private", 0,POPT_BIT_SET, &staticcfg.db_eflags, DB_PRIVATE,
58 NULL, NULL },
59
60 { "deadlock", 0,POPT_BIT_SET, &staticcfg.db_verbose, DB_VERB_DEADLOCK,
61 NULL, NULL },
62 { "recovery", 0,POPT_BIT_SET, &staticcfg.db_verbose, DB_VERB_RECOVERY,
63 NULL, NULL },
64 { "waitsfor", 0,POPT_BIT_SET, &staticcfg.db_verbose, DB_VERB_WAITSFOR,
65 NULL, NULL },
66 { "verbose", 0,POPT_ARG_VAL, &staticcfg.db_verbose, -1,
67 NULL, NULL },
68
69 { "cachesize", 0,POPT_ARG_INT, &staticcfg.db_cachesize, 0,
70 NULL, NULL },
71 { "mmapsize", 0,POPT_ARG_INT, &staticcfg.db_mmapsize, 0,
72 NULL, NULL },
73 { "mp_mmapsize", 0,POPT_ARG_INT, &staticcfg.db_mmapsize, 0,
74 NULL, NULL },
75 { "mp_size", 0,POPT_ARG_INT, &staticcfg.db_cachesize, 0,
76 NULL, NULL },
77
78 { "nofsync", 0,POPT_ARG_NONE, &staticcfg.db_no_fsync, 0,
79 NULL, NULL },
80
81 /* Per-dbi options */
82 { "nommap", 0,POPT_BIT_SET, &staticdbicfg.dbi_oflags, DB_NOMMAP,
83 NULL, NULL },
84
85 { "nodbsync", 0,POPT_ARG_NONE, &staticdbicfg.dbi_no_dbsync, 0,
86 NULL, NULL },
87 { "lockdbfd", 0,POPT_ARG_NONE, &staticdbicfg.dbi_lockdbfd, 0,
88 NULL, NULL },
89
90 POPT_TABLEEND
91 };
92
93
dbapi_err(rpmdb rdb,const char * msg,int error,int printit)94 static int dbapi_err(rpmdb rdb, const char * msg, int error, int printit)
95 {
96 if (printit && error) {
97 if (msg)
98 rpmlog(RPMLOG_ERR, _("%s error(%d) from %s: %s\n"),
99 rdb->db_descr, error, msg, db_strerror(error));
100 else
101 rpmlog(RPMLOG_ERR, _("%s error(%d): %s\n"),
102 rdb->db_descr, error, db_strerror(error));
103 }
104 return error;
105 }
106
cvtdberr(dbiIndex dbi,const char * msg,int error,int printit)107 static int cvtdberr(dbiIndex dbi, const char * msg, int error, int printit)
108 {
109 return dbapi_err(dbi->dbi_rpmdb, msg, error, printit);
110 }
111
errlog(const DB_ENV * env,const char * errpfx,const char * msg)112 static void errlog(const DB_ENV * env, const char *errpfx, const char *msg)
113 {
114 rpmlog(RPMLOG_ERR, "%s: %s\n", errpfx, msg);
115 }
116
warnlog(const DB_ENV * env,const char * msg)117 static void warnlog(const DB_ENV *env, const char *msg)
118 {
119 rpmlog(RPMLOG_WARNING, "%s: %s\n", _errpfx, msg);
120 }
121
db_envflags(DB * db)122 static uint32_t db_envflags(DB * db)
123 {
124 DB_ENV * env = db->get_env(db);
125 uint32_t eflags = 0;
126 (void) env->get_open_flags(env, &eflags);
127 return eflags;
128 }
129
130 /*
131 * Try to acquire db environment open/close serialization lock.
132 * Return the open, locked fd on success, -1 on failure.
133 */
serialize_env(const char * dbhome)134 static int serialize_env(const char *dbhome)
135 {
136 char *lock_path = rstrscat(NULL, dbhome, "/.dbenv.lock", NULL);
137 mode_t oldmask = umask(022);
138 int fd = open(lock_path, (O_RDWR|O_CREAT), 0644);
139 umask(oldmask);
140
141 if (fd >= 0) {
142 int rc;
143 struct flock info;
144 memset(&info, 0, sizeof(info));
145 info.l_type = F_WRLCK;
146 info.l_whence = SEEK_SET;
147 do {
148 rc = fcntl(fd, F_SETLKW, &info);
149 } while (rc == -1 && errno == EINTR);
150
151 if (rc == -1) {
152 close(fd);
153 fd = -1;
154 }
155 }
156
157 free(lock_path);
158 return fd;
159 }
160
db_fini(rpmdb rdb,const char * dbhome)161 static int db_fini(rpmdb rdb, const char * dbhome)
162 {
163 DB_ENV * dbenv = rdb->db_dbenv;
164 int rc;
165 int lockfd = -1;
166 uint32_t eflags = 0;
167
168 if (dbenv == NULL)
169 return 0;
170
171 if (rdb->db_opens > 1) {
172 rdb->db_opens--;
173 return 0;
174 }
175
176 (void) dbenv->get_open_flags(dbenv, &eflags);
177 if (!(eflags & DB_PRIVATE))
178 lockfd = serialize_env(dbhome);
179
180 rc = dbenv->close(dbenv, 0);
181 rc = dbapi_err(rdb, "dbenv->close", rc, _debug);
182
183 rpmlog(RPMLOG_DEBUG, "closed db environment %s\n", dbhome);
184
185 if (!(eflags & DB_PRIVATE) && rdb->db_remove_env) {
186 int xx;
187
188 xx = db_env_create(&dbenv, 0);
189 xx = dbapi_err(rdb, "db_env_create", xx, _debug);
190 xx = dbenv->remove(dbenv, dbhome, 0);
191 /* filter out EBUSY as it just means somebody else gets to clean it */
192 xx = dbapi_err(rdb, "dbenv->remove", xx, (xx == EBUSY ? 0 : _debug));
193
194 rpmlog(RPMLOG_DEBUG, "removed db environment %s\n", dbhome);
195
196 }
197
198 if (lockfd >= 0)
199 close(lockfd);
200
201 return rc;
202 }
203
fsync_disable(int fd)204 static int fsync_disable(int fd)
205 {
206 return 0;
207 }
208
209 /*
210 * dbenv->failchk() callback method for determining is the given pid/tid
211 * is alive. We only care about pid's though.
212 */
isalive(DB_ENV * dbenv,pid_t pid,db_threadid_t tid,uint32_t flags)213 static int isalive(DB_ENV *dbenv, pid_t pid, db_threadid_t tid, uint32_t flags)
214 {
215 int alive = 0;
216
217 if (pid == getpid()) {
218 alive = 1;
219 } else if (kill(pid, 0) == 0) {
220 alive = 1;
221 /* only existing processes can fail with EPERM */
222 } else if (errno == EPERM) {
223 alive = 1;
224 }
225
226 return alive;
227 }
228
229
dbConfigure(rpmDbiTagVal rpmtag,struct dbConfig_s * cfg,struct dbiConfig_s * dbicfg)230 static void dbConfigure(rpmDbiTagVal rpmtag, struct dbConfig_s *cfg, struct dbiConfig_s *dbicfg)
231 {
232 char *dbOpts;
233
234 dbOpts = rpmExpand("%{_dbi_config_", rpmTagGetName(rpmtag), "}", NULL);
235
236 if (!(dbOpts && *dbOpts && *dbOpts != '%')) {
237 dbOpts = _free(dbOpts);
238 dbOpts = rpmExpand("%{_dbi_config}", NULL);
239 if (!(dbOpts && *dbOpts && *dbOpts != '%')) {
240 dbOpts = _free(dbOpts);
241 }
242 }
243
244 /* Parse the options for the database element(s). */
245 if (dbOpts && *dbOpts && *dbOpts != '%') {
246 char *o, *oe;
247 char *p, *pe;
248
249 memset(&staticdbicfg, 0, sizeof(staticdbicfg));
250 /*=========*/
251 for (o = dbOpts; o && *o; o = oe) {
252 const struct poptOption *opt;
253 const char * tok;
254 unsigned int argInfo;
255
256 /* Skip leading white space. */
257 while (*o && risspace(*o))
258 o++;
259
260 /* Find and terminate next key=value pair. Save next start point. */
261 for (oe = o; oe && *oe; oe++) {
262 if (risspace(*oe))
263 break;
264 if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/'))
265 break;
266 }
267 if (oe && *oe)
268 *oe++ = '\0';
269 if (*o == '\0')
270 continue;
271
272 /* Separate key from value, save value start (if any). */
273 for (pe = o; pe && *pe && *pe != '='; pe++)
274 {};
275 p = (pe ? *pe++ = '\0', pe : NULL);
276
277 /* Skip over negation at start of token. */
278 for (tok = o; *tok == '!'; tok++)
279 {};
280
281 /* Find key in option table. */
282 for (opt = rdbOptions; opt->longName != NULL; opt++) {
283 if (!rstreq(tok, opt->longName))
284 continue;
285 break;
286 }
287 if (opt->longName == NULL) {
288 rpmlog(RPMLOG_ERR,
289 _("unrecognized db option: \"%s\" ignored.\n"), o);
290 continue;
291 }
292
293 /* Toggle the flags for negated tokens, if necessary. */
294 argInfo = opt->argInfo;
295 if (argInfo == POPT_BIT_SET && *o == '!' && ((tok - o) % 2))
296 argInfo = POPT_BIT_CLR;
297
298 /* Save value in template as appropriate. */
299 switch (argInfo & POPT_ARG_MASK) {
300
301 case POPT_ARG_NONE:
302 (void) poptSaveInt((int *)opt->arg, argInfo, 1L);
303 break;
304 case POPT_ARG_VAL:
305 (void) poptSaveInt((int *)opt->arg, argInfo, (long)opt->val);
306 break;
307 case POPT_ARG_STRING:
308 { char ** t = opt->arg;
309 if (t) {
310 /* FIX: opt->arg annotation in popt.h */
311 *t = _free(*t);
312 *t = xstrdup( (p ? p : "") );
313 }
314 } break;
315
316 case POPT_ARG_INT:
317 case POPT_ARG_LONG:
318 { long aLong = strtol(p, &pe, 0);
319 if (pe) {
320 if (!rstrncasecmp(pe, "Mb", 2))
321 aLong *= 1024 * 1024;
322 else if (!rstrncasecmp(pe, "Kb", 2))
323 aLong *= 1024;
324 else if (*pe != '\0') {
325 rpmlog(RPMLOG_ERR,
326 _("%s has invalid numeric value, skipped\n"),
327 opt->longName);
328 continue;
329 }
330 }
331
332 if ((argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) {
333 if (aLong == LONG_MIN || aLong == LONG_MAX) {
334 rpmlog(RPMLOG_ERR,
335 _("%s has too large or too small long value, skipped\n"),
336 opt->longName);
337 continue;
338 }
339 (void) poptSaveLong((long *)opt->arg, argInfo, aLong);
340 break;
341 } else {
342 if (aLong > INT_MAX || aLong < INT_MIN) {
343 rpmlog(RPMLOG_ERR,
344 _("%s has too large or too small integer value, skipped\n"),
345 opt->longName);
346 continue;
347 }
348 (void) poptSaveInt((int *)opt->arg, argInfo, aLong);
349 }
350 } break;
351 default:
352 break;
353 }
354 }
355 /*=========*/
356 }
357
358 dbOpts = _free(dbOpts);
359 if (cfg) {
360 *cfg = staticcfg; /* structure assignment */
361 /* Throw in some defaults if configuration didn't set any */
362 if (!cfg->db_mmapsize)
363 cfg->db_mmapsize = 16 * 1024 * 1024;
364 if (!cfg->db_cachesize)
365 cfg->db_cachesize = 8 * 1024 * 1024;
366 }
367 if (dbicfg) {
368 *dbicfg = staticdbicfg;
369 }
370 }
371
prDbiOpenFlags(int dbflags,int print_dbenv_flags)372 static char * prDbiOpenFlags(int dbflags, int print_dbenv_flags)
373 {
374 ARGV_t flags = NULL;
375 const struct poptOption *opt;
376 char *buf;
377
378 for (opt = rdbOptions; opt->longName != NULL; opt++) {
379 if (opt->argInfo != POPT_BIT_SET)
380 continue;
381 if (print_dbenv_flags) {
382 if (!(opt->arg == &staticcfg.db_eflags))
383 continue;
384 } else {
385 if (!(opt->arg == &staticdbicfg.dbi_oflags))
386 continue;
387 }
388 if ((dbflags & opt->val) != opt->val)
389 continue;
390 argvAdd(&flags, opt->longName);
391 dbflags &= ~opt->val;
392 }
393 if (dbflags) {
394 char *df = NULL;
395 rasprintf(&df, "0x%x", (unsigned)dbflags);
396 argvAdd(&flags, df);
397 free(df);
398 }
399 buf = argvJoin(flags, ":");
400 argvFree(flags);
401
402 return buf ? buf : xstrdup("(none)");
403 }
404
db_init(rpmdb rdb,const char * dbhome)405 static int db_init(rpmdb rdb, const char * dbhome)
406 {
407 DB_ENV *dbenv = NULL;
408 int rc, xx;
409 int retry_open = 2;
410 int lockfd = -1;
411 int rdonly = ((rdb->db_mode & O_ACCMODE) == O_RDONLY);
412 struct dbConfig_s * cfg = &rdb->cfg;
413 /* This is our setup, thou shall not have other setups before us */
414 uint32_t eflags = (DB_CREATE|DB_INIT_MPOOL|DB_INIT_CDB);
415
416 if (rdb->db_dbenv != NULL) {
417 rdb->db_opens++;
418 return 0;
419 }
420
421 /*
422 * Both verify and rebuild are rather special, if for different reasons:
423 * On rebuild we dont want to be affected by eg paniced environment, and
424 * CDB only slows things down there. Verify is a quirky beast unlike
425 * anything else in BDB, and does not like shared env or CDB.
426 */
427 if (rdb->db_flags & (RPMDB_FLAG_VERIFYONLY|RPMDB_FLAG_REBUILD)) {
428 eflags |= DB_PRIVATE;
429 eflags &= ~DB_INIT_CDB;
430 }
431
432 rc = db_env_create(&dbenv, 0);
433 rc = dbapi_err(rdb, "db_env_create", rc, _debug);
434 if (dbenv == NULL || rc)
435 goto errxit;
436
437 dbenv->set_alloc(dbenv, rmalloc, rrealloc, NULL);
438 dbenv->set_errcall(dbenv, NULL);
439 dbenv->set_errpfx(dbenv, _errpfx);
440 dbenv->set_msgcall(dbenv, warnlog);
441
442 /*
443 * These enable automatic stale lock removal.
444 * thread_count 8 is some kind of "magic minimum" value...
445 */
446 dbenv->set_thread_count(dbenv, 8);
447 dbenv->set_isalive(dbenv, isalive);
448
449 dbenv->set_verbose(dbenv, DB_VERB_DEADLOCK,
450 (cfg->db_verbose & DB_VERB_DEADLOCK));
451 dbenv->set_verbose(dbenv, DB_VERB_RECOVERY,
452 (cfg->db_verbose & DB_VERB_RECOVERY));
453 dbenv->set_verbose(dbenv, DB_VERB_WAITSFOR,
454 (cfg->db_verbose & DB_VERB_WAITSFOR));
455
456 if (cfg->db_mmapsize) {
457 xx = dbenv->set_mp_mmapsize(dbenv, cfg->db_mmapsize);
458 xx = dbapi_err(rdb, "dbenv->set_mp_mmapsize", xx, _debug);
459 }
460
461 if (cfg->db_cachesize) {
462 xx = dbenv->set_cachesize(dbenv, 0, cfg->db_cachesize, 0);
463 xx = dbapi_err(rdb, "dbenv->set_cachesize", xx, _debug);
464 }
465
466 /*
467 * Serialize shared environment open (and clock) via fcntl() lock.
468 * Otherwise we can end up calling dbenv->failchk() while another
469 * process is joining the environment, leading to transient
470 * DB_RUNRECOVER errors. Also prevents races wrt removing the
471 * environment (eg chrooted operation). Silently fall back to
472 * private environment on failure to allow non-privileged queries
473 * to "work", broken as it might be.
474 */
475 if (!(eflags & DB_PRIVATE)) {
476 lockfd = serialize_env(dbhome);
477 if (lockfd < 0 && rdonly) {
478 eflags |= DB_PRIVATE;
479 retry_open--;
480 rpmlog(RPMLOG_DEBUG, "serialize failed, using private dbenv\n");
481 }
482 }
483
484 /*
485 * Actually open the environment. Fall back to private environment
486 * if we dont have permission to join/create shared environment or
487 * system doesn't support it..
488 */
489 while (retry_open) {
490 char *fstr = prDbiOpenFlags(eflags, 1);
491 rpmlog(RPMLOG_DEBUG, "opening db environment %s %s\n", dbhome, fstr);
492 free(fstr);
493
494 rc = (dbenv->open)(dbenv, dbhome, eflags, rdb->db_perms);
495 if (rc == EINVAL && errno == rc) {
496 eflags |= DB_PRIVATE;
497 retry_open--;
498 } else if (rdonly && (rc == EACCES || rc == EROFS || rc == DB_VERSION_MISMATCH)) {
499 eflags |= DB_PRIVATE;
500 retry_open--;
501 } else {
502 retry_open = 0;
503 }
504 }
505 rc = dbapi_err(rdb, "dbenv->open", rc, _debug);
506 if (rc)
507 goto errxit;
508
509 dbenv->set_errcall(dbenv, errlog);
510
511 /* stale lock removal */
512 rc = dbenv->failchk(dbenv, 0);
513 rc = dbapi_err(rdb, "dbenv->failchk", rc, _debug);
514 if (rc)
515 goto errxit;
516
517 rdb->db_dbenv = dbenv;
518 rdb->db_opens = 1;
519
520 if (lockfd >= 0)
521 close(lockfd);
522 return 0;
523
524 errxit:
525 if (dbenv) {
526 int xx;
527 xx = dbenv->close(dbenv, 0);
528 xx = dbapi_err(rdb, "dbenv->close", xx, _debug);
529 }
530 if (lockfd >= 0)
531 close(lockfd);
532 return rc;
533 }
534
db3_dbSetFSync(rpmdb rdb,int enable)535 static void db3_dbSetFSync(rpmdb rdb, int enable)
536 {
537 #ifdef HAVE_FDATASYNC
538 db_env_set_func_fsync(enable ? fdatasync : fsync_disable);
539 #else
540 db_env_set_func_fsync(enable ? fsync : fsync_disable);
541 #endif
542 }
543
db3_Ctrl(rpmdb rdb,dbCtrlOp ctrl)544 static int db3_Ctrl(rpmdb rdb, dbCtrlOp ctrl)
545 {
546 return 0;
547 }
548
dbiSync(dbiIndex dbi,unsigned int flags)549 static int dbiSync(dbiIndex dbi, unsigned int flags)
550 {
551 DB * db = dbi->dbi_db;
552 int rc = 0;
553
554 if (db != NULL && !dbi->cfg.dbi_no_dbsync) {
555 rc = db->sync(db, flags);
556 rc = cvtdberr(dbi, "db->sync", rc, _debug);
557 }
558 return rc;
559 }
560
db3_dbiCursorInit(dbiIndex dbi,unsigned int flags)561 static dbiCursor db3_dbiCursorInit(dbiIndex dbi, unsigned int flags)
562 {
563 dbiCursor dbc = NULL;
564
565 if (dbi && dbi->dbi_db) {
566 DB * db = dbi->dbi_db;
567 DBC * cursor;
568 int cflags;
569 int rc = 0;
570 uint32_t eflags = db_envflags(db);
571
572 /* DB_WRITECURSOR requires CDB and writable db */
573 if ((flags & DBC_WRITE) &&
574 (eflags & DB_INIT_CDB) && !(dbi->dbi_flags & DBI_RDONLY))
575 {
576 cflags = DB_WRITECURSOR;
577 } else
578 cflags = 0;
579
580 /*
581 * Check for stale locks which could block writes "forever".
582 * XXX: Should we also do this on reads? Reads are less likely
583 * to get blocked so it seems excessive...
584 * XXX: On DB_RUNRECOVER, we should abort everything. Now
585 * we'll just fail to open a cursor again and again and again.
586 */
587 if (cflags & DB_WRITECURSOR) {
588 DB_ENV *dbenv = db->get_env(db);
589 rc = dbenv->failchk(dbenv, 0);
590 rc = cvtdberr(dbi, "dbenv->failchk", rc, _debug);
591 }
592
593 if (rc == 0) {
594 rc = db->cursor(db, NULL, &cursor, cflags);
595 rc = cvtdberr(dbi, "db->cursor", rc, _debug);
596 }
597
598 if (rc == 0) {
599 dbc = xcalloc(1, sizeof(*dbc));
600 dbc->cursor = cursor;
601 dbc->dbi = dbi;
602 dbc->flags = flags;
603 }
604 }
605
606 return dbc;
607 }
608
db3_dbiCursorFree(dbiIndex dbi,dbiCursor dbc)609 static dbiCursor db3_dbiCursorFree(dbiIndex dbi, dbiCursor dbc)
610 {
611 if (dbc) {
612 /* Automatically sync on write-cursor close */
613 if (dbc->flags & DBC_WRITE)
614 dbiSync(dbc->dbi, 0);
615 DBC * cursor = dbc->cursor;
616 int rc = cursor->c_close(cursor);
617 cvtdberr(dbc->dbi, "dbcursor->c_close", rc, _debug);
618 free(dbc);
619 }
620 return NULL;
621 }
622
dbiCursorPut(dbiCursor dbc,DBT * key,DBT * data,unsigned int flags)623 static int dbiCursorPut(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags)
624 {
625 int rc = EINVAL;
626 int sane = (key->data != NULL && key->size > 0 &&
627 data->data != NULL && data->size > 0);
628
629 if (dbc && sane) {
630 DBC * cursor = dbc->cursor;
631 rpmdb rdb = dbc->dbi->dbi_rpmdb;
632 rpmswEnter(&rdb->db_putops, (ssize_t) 0);
633
634 rc = cursor->c_put(cursor, key, data, DB_KEYLAST);
635 rc = cvtdberr(dbc->dbi, "dbcursor->c_put", rc, _debug);
636
637 rpmswExit(&rdb->db_putops, (ssize_t) data->size);
638 }
639 return rc;
640 }
641
dbiCursorGet(dbiCursor dbc,DBT * key,DBT * data,unsigned int flags)642 static int dbiCursorGet(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags)
643 {
644 int rc = EINVAL;
645 int sane = ((flags == DB_NEXT) || (key->data != NULL && key->size > 0));
646
647 if (dbc && sane) {
648 DBC * cursor = dbc->cursor;
649 rpmdb rdb = dbc->dbi->dbi_rpmdb;
650 int _printit;
651 rpmswEnter(&rdb->db_getops, 0);
652
653 /* XXX db4 does DB_FIRST on uninitialized cursor */
654 rc = cursor->c_get(cursor, key, data, flags);
655 /* XXX DB_NOTFOUND can be returned */
656 _printit = (rc == DB_NOTFOUND ? 0 : _debug);
657 rc = cvtdberr(dbc->dbi, "dbcursor->c_get", rc, _printit);
658
659 /* Remember the last key fetched */
660 if (rc == 0) {
661 dbc->key = key->data;
662 dbc->keylen = key->size;
663 } else {
664 dbc->key = NULL;
665 dbc->keylen = 0;
666 }
667
668 rpmswExit(&rdb->db_getops, data->size);
669 }
670 return rc;
671 }
672
dbiCursorDel(dbiCursor dbc,DBT * key,DBT * data,unsigned int flags)673 static int dbiCursorDel(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags)
674 {
675 int rc = EINVAL;
676 int sane = (key->data != NULL && key->size > 0);
677
678 if (dbc && sane) {
679 DBC * cursor = dbc->cursor;
680 int _printit;
681 rpmdb rdb = dbc->dbi->dbi_rpmdb;
682 rpmswEnter(&rdb->db_delops, 0);
683
684 /* XXX TODO: insure that cursor is positioned with duplicates */
685 rc = cursor->c_get(cursor, key, data, DB_SET);
686 /* XXX DB_NOTFOUND can be returned */
687 _printit = (rc == DB_NOTFOUND ? 0 : _debug);
688 rc = cvtdberr(dbc->dbi, "dbcursor->c_get", rc, _printit);
689
690 if (rc == 0) {
691 rc = cursor->c_del(cursor, flags);
692 rc = cvtdberr(dbc->dbi, "dbcursor->c_del", rc, _debug);
693 }
694 rpmswExit(&rdb->db_delops, data->size);
695 }
696 return rc;
697 }
698
dbiByteSwapped(dbiIndex dbi)699 static int dbiByteSwapped(dbiIndex dbi)
700 {
701 DB * db = dbi->dbi_db;
702 int rc = 0;
703
704 if (dbi->dbi_byteswapped != -1)
705 return dbi->dbi_byteswapped;
706
707 if (db != NULL) {
708 int isswapped = 0;
709 rc = db->get_byteswapped(db, &isswapped);
710 if (rc == 0)
711 dbi->dbi_byteswapped = rc = isswapped;
712 }
713 return rc;
714 }
715
db3_dbiVerify(dbiIndex dbi,unsigned int flags)716 static int db3_dbiVerify(dbiIndex dbi, unsigned int flags)
717 {
718 int rc = 0;
719
720 if (dbi && dbi->dbi_db) {
721 DB * db = dbi->dbi_db;
722
723 rc = db->verify(db, dbi->dbi_file, NULL, NULL, flags);
724 rc = cvtdberr(dbi, "db->verify", rc, _debug);
725
726 rpmlog(RPMLOG_DEBUG, "verified db index %s\n", dbi->dbi_file);
727
728 /* db->verify() destroys the handle, make sure nobody accesses it */
729 dbi->dbi_db = NULL;
730 }
731 return rc;
732 }
733
db3_dbiClose(dbiIndex dbi,unsigned int flags)734 static int db3_dbiClose(dbiIndex dbi, unsigned int flags)
735 {
736 rpmdb rdb = dbi->dbi_rpmdb;
737 const char * dbhome = rpmdbHome(rdb);
738 DB * db = dbi->dbi_db;
739 int _printit;
740 int rc = 0;
741
742 if (db) {
743 rc = db->close(db, flags);
744 /* XXX ignore not found error messages. */
745 _printit = (rc == ENOENT ? 0 : _debug);
746 rc = cvtdberr(dbi, "db->close", rc, _printit);
747 db = dbi->dbi_db = NULL;
748
749 rpmlog(RPMLOG_DEBUG, "closed db index %s/%s\n",
750 dbhome, dbi->dbi_file);
751 }
752
753 db_fini(rdb, dbhome ? dbhome : "");
754
755 dbi->dbi_db = NULL;
756
757 dbi = dbiFree(dbi);
758
759 return rc;
760 }
761
762 /*
763 * Lock a file using fcntl(2). Traditionally this is Packages,
764 * the file used to store metadata of installed header(s),
765 * as Packages is always opened, and should be opened first,
766 * for any rpmdb access.
767 *
768 * If no DBENV is used, then access is protected with a
769 * shared/exclusive locking scheme, as always.
770 *
771 * With a DBENV, the fcntl(2) lock is necessary only to keep
772 * the riff-raff from playing where they don't belong, as
773 * the DBENV should provide it's own locking scheme. So try to
774 * acquire a lock, but permit failures, as some other
775 * DBENV player may already have acquired the lock.
776 *
777 * With NPTL posix mutexes, revert to fcntl lock on non-functioning
778 * glibc/kernel combinations.
779 */
dbiFlock(dbiIndex dbi,int mode)780 static int dbiFlock(dbiIndex dbi, int mode)
781 {
782 int fdno = -1;
783 int rc = 0;
784 DB * db = dbi->dbi_db;
785
786 if (!(db->fd(db, &fdno) == 0 && fdno >= 0)) {
787 rc = 1;
788 } else {
789 const char *dbhome = rpmdbHome(dbi->dbi_rpmdb);
790 struct flock l;
791 memset(&l, 0, sizeof(l));
792 l.l_whence = 0;
793 l.l_start = 0;
794 l.l_len = 0;
795 l.l_type = (mode & O_ACCMODE) == O_RDONLY
796 ? F_RDLCK : F_WRLCK;
797 l.l_pid = 0;
798
799 rc = fcntl(fdno, F_SETLK, (void *) &l);
800 if (rc) {
801 uint32_t eflags = db_envflags(db);
802 /* Warning iff using non-private CDB locking. */
803 rc = (((eflags & DB_INIT_CDB) && !(eflags & DB_PRIVATE)) ? 0 : 1);
804 rpmlog( (rc ? RPMLOG_ERR : RPMLOG_WARNING),
805 _("cannot get %s lock on %s/%s\n"),
806 ((mode & O_ACCMODE) == O_RDONLY)
807 ? _("shared") : _("exclusive"),
808 dbhome, dbi->dbi_file);
809 } else {
810 rpmlog(RPMLOG_DEBUG,
811 "locked db index %s/%s\n",
812 dbhome, dbi->dbi_file);
813 }
814 }
815 return rc;
816 }
817
db3_dbiOpen(rpmdb rdb,rpmDbiTagVal rpmtag,dbiIndex * dbip,int flags)818 static int db3_dbiOpen(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags)
819 {
820 const char *dbhome = rpmdbHome(rdb);
821 dbiIndex dbi = NULL;
822 int rc = 0;
823 int retry_open;
824 int verifyonly = (flags & RPMDB_FLAG_VERIFYONLY);
825
826 DB * db = NULL;
827 DBTYPE dbtype = DB_UNKNOWN;
828 uint32_t oflags;
829 static int _lockdbfd = 0;
830
831 if (dbip)
832 *dbip = NULL;
833
834 if ((dbi = dbiNew(rdb, rpmtag)) == NULL)
835 return 1;
836
837 /*
838 * Parse db configuration parameters.
839 */
840 dbConfigure(rpmtag, rdb->db_dbenv == NULL ? &rdb->cfg : NULL, &dbi->cfg);
841
842 /*
843 * Map open mode flags onto configured database/environment flags.
844 */
845 oflags = dbi->cfg.dbi_oflags;
846 if ((rdb->db_mode & O_ACCMODE) == O_RDONLY)
847 oflags |= DB_RDONLY;
848
849 rc = db_init(rdb, dbhome);
850
851 retry_open = (rc == 0) ? 2 : 0;
852
853 while (retry_open) {
854 rc = db_create(&db, rdb->db_dbenv, 0);
855 rc = cvtdberr(dbi, "db_create", rc, _debug);
856
857 /* For verify we only want the handle, not an open db */
858 if (verifyonly)
859 break;
860
861 if (rc == 0 && db != NULL) {
862 int _printit;
863 char *dbfs = prDbiOpenFlags(oflags, 0);
864 rpmlog(RPMLOG_DEBUG, "opening db index %s/%s %s mode=0x%x\n",
865 dbhome, dbi->dbi_file, dbfs, rdb->db_mode);
866 free(dbfs);
867
868 rc = (db->open)(db, NULL, dbi->dbi_file, NULL,
869 dbtype, oflags, rdb->db_perms);
870
871 /* Attempt to create if missing, discarding DB_RDONLY (!) */
872 if (rc == ENOENT) {
873 oflags |= DB_CREATE;
874 oflags &= ~DB_RDONLY;
875 dbtype = (rpmtag == RPMDBI_PACKAGES) ? DB_HASH : DB_BTREE;
876 retry_open--;
877 } else {
878 retry_open = 0;
879 }
880
881 /* XXX return rc == errno without printing */
882 _printit = (rc > 0 ? 0 : _debug);
883 rc = cvtdberr(dbi, "db->open", rc, _printit);
884
885 /* Validate the index type is something we can support */
886 if ((rc == 0) && (dbtype == DB_UNKNOWN)) {
887 db->get_type(db, &dbtype);
888 if (dbtype != DB_HASH && dbtype != DB_BTREE) {
889 rpmlog(RPMLOG_ERR, _("invalid index type %x on %s/%s\n"),
890 dbtype, dbhome, dbi->dbi_file);
891 rc = 1;
892 }
893 }
894
895 if (rc != 0) {
896 db->close(db, 0);
897 db = NULL;
898 }
899 }
900 }
901
902 dbi->dbi_db = db;
903
904 dbi->dbi_flags = 0;
905 if (oflags & DB_CREATE)
906 dbi->dbi_flags |= DBI_CREATED;
907 if (oflags & DB_RDONLY)
908 dbi->dbi_flags |= DBI_RDONLY;
909
910 if (!verifyonly && rc == 0 && dbi->cfg.dbi_lockdbfd && _lockdbfd++ == 0) {
911 rc = dbiFlock(dbi, rdb->db_mode);
912 }
913
914 if (rc == 0 && dbi->dbi_db != NULL && dbip != NULL) {
915 *dbip = dbi;
916 } else {
917 (void) dbiClose(dbi, 0);
918 }
919
920 return rc;
921 }
922
923 /**
924 * Convert retrieved data to index set.
925 * @param dbi index database handle
926 * @param data retrieved data
927 * @retval setp (malloc'ed) index set
928 * @return 0 on success
929 */
dbt2set(dbiIndex dbi,DBT * data,dbiIndexSet * setp)930 static int dbt2set(dbiIndex dbi, DBT * data, dbiIndexSet * setp)
931 {
932 int _dbbyteswapped = dbiByteSwapped(dbi);
933 const char * sdbir;
934 dbiIndexSet set;
935 unsigned int i;
936
937 if (dbi == NULL || data == NULL || setp == NULL)
938 return -1;
939
940 if ((sdbir = data->data) == NULL) {
941 *setp = NULL;
942 return 0;
943 }
944
945 set = dbiIndexSetNew(data->size / (2 * sizeof(int32_t)));
946 set->count = data->size / (2 * sizeof(int32_t));
947
948 for (i = 0; i < set->count; i++) {
949 union _dbswap hdrNum, tagNum;
950
951 memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
952 sdbir += sizeof(hdrNum.ui);
953 memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui));
954 sdbir += sizeof(tagNum.ui);
955 if (_dbbyteswapped) {
956 _DBSWAP(hdrNum);
957 _DBSWAP(tagNum);
958 }
959 set->recs[i].hdrNum = hdrNum.ui;
960 set->recs[i].tagNum = tagNum.ui;
961 }
962 *setp = set;
963 return 0;
964 }
965
966 /**
967 * Convert index set to database representation.
968 * @param dbi index database handle
969 * @param data retrieved data
970 * @param set index set
971 * @return 0 on success
972 */
set2dbt(dbiIndex dbi,DBT * data,dbiIndexSet set)973 static int set2dbt(dbiIndex dbi, DBT * data, dbiIndexSet set)
974 {
975 int _dbbyteswapped = dbiByteSwapped(dbi);
976 char * tdbir;
977 unsigned int i;
978
979 if (dbi == NULL || data == NULL || set == NULL)
980 return -1;
981
982 data->size = set->count * (2 * sizeof(int32_t));
983 if (data->size == 0) {
984 data->data = NULL;
985 return 0;
986 }
987 tdbir = data->data = xmalloc(data->size);
988
989 for (i = 0; i < set->count; i++) {
990 union _dbswap hdrNum, tagNum;
991
992 memset(&hdrNum, 0, sizeof(hdrNum));
993 memset(&tagNum, 0, sizeof(tagNum));
994 hdrNum.ui = set->recs[i].hdrNum;
995 tagNum.ui = set->recs[i].tagNum;
996 if (_dbbyteswapped) {
997 _DBSWAP(hdrNum);
998 _DBSWAP(tagNum);
999 }
1000 memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
1001 tdbir += sizeof(hdrNum.ui);
1002 memcpy(tdbir, &tagNum.ui, sizeof(tagNum.ui));
1003 tdbir += sizeof(tagNum.ui);
1004 }
1005 return 0;
1006 }
1007
db3_idxdbGet(dbiIndex dbi,dbiCursor dbc,const char * keyp,size_t keylen,dbiIndexSet * set,int searchType)1008 static rpmRC db3_idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen,
1009 dbiIndexSet *set, int searchType)
1010 {
1011 rpmRC rc = RPMRC_FAIL; /* assume failure */
1012 if (dbi != NULL && dbc != NULL) {
1013 int cflags = DB_NEXT;
1014 int dbrc;
1015 DBT data, key;
1016 memset(&data, 0, sizeof(data));
1017 memset(&key, 0, sizeof(key));
1018
1019 if (keyp) {
1020 if (keylen == 0) { /* XXX "/" fixup */
1021 keyp = "";
1022 keylen = 1;
1023 }
1024 key.data = (void *) keyp; /* discards const */
1025 key.size = keylen;
1026 cflags = searchType == DBC_PREFIX_SEARCH ? DB_SET_RANGE : DB_SET;
1027 }
1028
1029 for (;;) {
1030 dbiIndexSet newset = NULL;
1031 dbrc = dbiCursorGet(dbc, &key, &data, cflags);
1032 if (dbrc != 0)
1033 break;
1034 if (searchType == DBC_PREFIX_SEARCH &&
1035 (key.size < keylen || memcmp(key.data, keyp, keylen) != 0))
1036 break;
1037 if (set) {
1038 dbt2set(dbi, &data, &newset);
1039 if (*set == NULL) {
1040 *set = newset;
1041 } else {
1042 dbiIndexSetAppendSet(*set, newset, 0);
1043 dbiIndexSetFree(newset);
1044 }
1045 }
1046 if (searchType != DBC_PREFIX_SEARCH)
1047 break;
1048 key.data = NULL;
1049 key.size = 0;
1050 cflags = DB_NEXT;
1051 }
1052
1053 /* fixup result status for prefix search */
1054 if (searchType == DBC_PREFIX_SEARCH && set) {
1055 if (dbrc == DB_NOTFOUND && *set != NULL && (*set)->count > 0)
1056 dbrc = 0;
1057 else if (dbrc == 0 && (*set == NULL || (*set)->count == 0))
1058 dbrc = DB_NOTFOUND;
1059 }
1060
1061 if (dbrc == 0) {
1062 rc = RPMRC_OK;
1063 } else if (dbrc == DB_NOTFOUND) {
1064 rc = RPMRC_NOTFOUND;
1065 } else {
1066 rpmlog(RPMLOG_ERR,
1067 _("error(%d) getting \"%s\" records from %s index: %s\n"),
1068 dbrc, keyp ? keyp : "???", dbiName(dbi), db_strerror(dbrc));
1069 }
1070 }
1071 return rc;
1072 }
1073
1074 /* Update secondary index. NULL set deletes the key */
updateIndex(dbiCursor dbc,const char * keyp,unsigned int keylen,dbiIndexSet set)1075 static rpmRC updateIndex(dbiCursor dbc, const char *keyp, unsigned int keylen,
1076 dbiIndexSet set)
1077 {
1078 rpmRC rc = RPMRC_FAIL;
1079
1080 if (dbc && keyp) {
1081 dbiIndex dbi = dbc->dbi;
1082 int dbrc;
1083 DBT data, key;
1084 memset(&key, 0, sizeof(data));
1085 memset(&data, 0, sizeof(data));
1086
1087 key.data = (void *) keyp; /* discards const */
1088 key.size = keylen;
1089
1090 if (set)
1091 set2dbt(dbi, &data, set);
1092
1093 if (dbiIndexSetCount(set) > 0) {
1094 dbrc = dbiCursorPut(dbc, &key, &data, DB_KEYLAST);
1095 if (dbrc) {
1096 rpmlog(RPMLOG_ERR,
1097 _("error(%d) storing record \"%s\" into %s\n"),
1098 dbrc, (char*)key.data, dbiName(dbi));
1099 }
1100 free(data.data);
1101 } else {
1102 dbrc = dbiCursorDel(dbc, &key, &data, 0);
1103 if (dbrc) {
1104 rpmlog(RPMLOG_ERR,
1105 _("error(%d) removing record \"%s\" from %s\n"),
1106 dbrc, (char*)key.data, dbiName(dbi));
1107 }
1108 }
1109
1110 if (dbrc == 0)
1111 rc = RPMRC_OK;
1112 }
1113
1114 return rc;
1115 }
1116
db3_idxdbPutOne(dbiIndex dbi,dbiCursor dbc,const char * keyp,size_t keylen,dbiIndexItem rec)1117 static rpmRC db3_idxdbPutOne(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen,
1118 dbiIndexItem rec)
1119 {
1120 dbiIndexSet set = NULL;
1121 rpmRC rc;
1122
1123 if (keyp && keylen == 0) { /* XXX "/" fixup */
1124 keyp = "";
1125 keylen++;
1126 }
1127 rc = idxdbGet(dbi, dbc, keyp, keylen, &set, DBC_NORMAL_SEARCH);
1128
1129 /* Not found means a new key and is not an error. */
1130 if (rc && rc != RPMRC_NOTFOUND)
1131 return rc;
1132
1133 if (set == NULL)
1134 set = dbiIndexSetNew(1);
1135 dbiIndexSetAppend(set, rec, 1, 0);
1136
1137 rc = updateIndex(dbc, keyp, keylen, set);
1138
1139 dbiIndexSetFree(set);
1140 return rc;
1141 }
1142
db3_idxdbPut(dbiIndex dbi,rpmTagVal rpmtag,unsigned int hdrNum,Header h)1143 static rpmRC db3_idxdbPut(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h)
1144 {
1145 return tag2index(dbi, rpmtag, hdrNum, h, db3_idxdbPutOne);
1146 }
1147
db3_idxdbDelOne(dbiIndex dbi,dbiCursor dbc,const char * keyp,size_t keylen,dbiIndexItem rec)1148 static rpmRC db3_idxdbDelOne(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen,
1149 dbiIndexItem rec)
1150 {
1151 dbiIndexSet set = NULL;
1152 rpmRC rc;
1153
1154 if (keyp && keylen == 0) { /* XXX "/" fixup */
1155 keyp = "";
1156 keylen++;
1157 }
1158 rc = idxdbGet(dbi, dbc, keyp, keylen, &set, DBC_NORMAL_SEARCH);
1159 if (rc)
1160 return rc;
1161
1162 if (dbiIndexSetPrune(set, rec, 1, 1)) {
1163 /* Nothing was pruned. XXX: Can this actually happen? */
1164 rc = RPMRC_OK;
1165 } else {
1166 /* If there's data left, update data. Otherwise delete the key. */
1167 if (dbiIndexSetCount(set) > 0) {
1168 rc = updateIndex(dbc, keyp, keylen, set);
1169 } else {
1170 rc = updateIndex(dbc, keyp, keylen, NULL);
1171 }
1172 };
1173 dbiIndexSetFree(set);
1174
1175 return rc;
1176 }
1177
db3_idxdbDel(dbiIndex dbi,rpmTagVal rpmtag,unsigned int hdrNum,Header h)1178 static rpmRC db3_idxdbDel(dbiIndex dbi, rpmTagVal rpmtag, unsigned int hdrNum, Header h)
1179 {
1180 return tag2index(dbi, rpmtag, hdrNum, h, db3_idxdbDelOne);
1181 }
1182
db3_idxdbKey(dbiIndex dbi,dbiCursor dbc,unsigned int * keylen)1183 static const void * db3_idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen)
1184 {
1185 const void *key = NULL;
1186 if (dbc) {
1187 key = dbc->key;
1188 if (key && keylen)
1189 *keylen = dbc->keylen;
1190 }
1191 return key;
1192 }
1193
1194
1195 /* Update primary Packages index. NULL hdr means remove */
updatePackages(dbiCursor dbc,unsigned int hdrNum,DBT * hdr)1196 static rpmRC updatePackages(dbiCursor dbc, unsigned int hdrNum, DBT *hdr)
1197 {
1198 union _dbswap mi_offset;
1199 int rc = 0;
1200 DBT key;
1201
1202 if (dbc == NULL || hdrNum == 0)
1203 return RPMRC_FAIL;
1204
1205 memset(&key, 0, sizeof(key));
1206
1207 mi_offset.ui = hdrNum;
1208 if (dbiByteSwapped(dbc->dbi) == 1)
1209 _DBSWAP(mi_offset);
1210 key.data = (void *) &mi_offset;
1211 key.size = sizeof(mi_offset.ui);
1212
1213 if (hdr) {
1214 rc = dbiCursorPut(dbc, &key, hdr, DB_KEYLAST);
1215 if (rc) {
1216 rpmlog(RPMLOG_ERR,
1217 _("error(%d) adding header #%d record\n"), rc, hdrNum);
1218 }
1219 } else {
1220 DBT data;
1221
1222 memset(&data, 0, sizeof(data));
1223 rc = dbiCursorGet(dbc, &key, &data, DB_SET);
1224 if (rc) {
1225 rpmlog(RPMLOG_ERR,
1226 _("error(%d) removing header #%d record\n"), rc, hdrNum);
1227 } else
1228 rc = dbiCursorDel(dbc, &key, &data, 0);
1229 }
1230
1231 return rc == 0 ? RPMRC_OK : RPMRC_FAIL;
1232 }
1233
1234 /* Get current header instance number or try to allocate a new one */
pkgInstance(dbiIndex dbi,int alloc)1235 static unsigned int pkgInstance(dbiIndex dbi, int alloc)
1236 {
1237 unsigned int hdrNum = 0;
1238
1239 if (dbi != NULL && dbi->dbi_type == DBI_PRIMARY) {
1240 dbiCursor dbc;
1241 DBT key, data;
1242 unsigned int firstkey = 0;
1243 union _dbswap mi_offset;
1244 int ret;
1245
1246 memset(&key, 0, sizeof(key));
1247 memset(&data, 0, sizeof(data));
1248
1249 dbc = dbiCursorInit(dbi, alloc ? DBC_WRITE : 0);
1250
1251 /* Key 0 holds the current largest instance, fetch it */
1252 key.data = &firstkey;
1253 key.size = sizeof(firstkey);
1254 ret = dbiCursorGet(dbc, &key, &data, DB_SET);
1255
1256 if (ret == 0 && data.data) {
1257 memcpy(&mi_offset, data.data, sizeof(mi_offset.ui));
1258 if (dbiByteSwapped(dbi) == 1)
1259 _DBSWAP(mi_offset);
1260 hdrNum = mi_offset.ui;
1261 }
1262
1263 if (alloc) {
1264 /* Rather complicated "increment by one", bswapping as needed */
1265 ++hdrNum;
1266 mi_offset.ui = hdrNum;
1267 if (dbiByteSwapped(dbi) == 1)
1268 _DBSWAP(mi_offset);
1269 if (ret == 0 && data.data) {
1270 memcpy(data.data, &mi_offset, sizeof(mi_offset.ui));
1271 } else {
1272 data.data = &mi_offset;
1273 data.size = sizeof(mi_offset.ui);
1274 }
1275
1276 /* Unless we manage to insert the new instance number, we failed */
1277 ret = dbiCursorPut(dbc, &key, &data, DB_KEYLAST);
1278 if (ret) {
1279 hdrNum = 0;
1280 rpmlog(RPMLOG_ERR,
1281 _("error(%d) allocating new package instance\n"), ret);
1282 }
1283 }
1284 dbiCursorFree(dbi, dbc);
1285 }
1286
1287 return hdrNum;
1288 }
1289
db3_pkgdbPut(dbiIndex dbi,dbiCursor dbc,unsigned int * hdrNum,unsigned char * hdrBlob,unsigned int hdrLen)1290 static rpmRC db3_pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum,
1291 unsigned char *hdrBlob, unsigned int hdrLen)
1292 {
1293 rpmRC rc = RPMRC_FAIL;
1294 unsigned int hnum = *hdrNum;
1295 DBT hdr;
1296 memset(&hdr, 0, sizeof(hdr));
1297 hdr.data = hdrBlob;
1298 hdr.size = hdrLen;
1299
1300 if (hnum == 0)
1301 hnum = pkgInstance(dbi, 1);
1302
1303 if (hnum)
1304 rc = updatePackages(dbc, hnum, &hdr);
1305
1306 if (rc == RPMRC_OK)
1307 *hdrNum = hnum;
1308
1309 return rc;
1310 }
1311
db3_pkgdbDel(dbiIndex dbi,dbiCursor dbc,unsigned int hdrNum)1312 static rpmRC db3_pkgdbDel(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum)
1313 {
1314 return updatePackages(dbc, hdrNum, NULL);
1315 }
1316
db3_pkgdbGet(dbiIndex dbi,dbiCursor dbc,unsigned int hdrNum,unsigned char ** hdrBlob,unsigned int * hdrLen)1317 static rpmRC db3_pkgdbGet(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum,
1318 unsigned char **hdrBlob, unsigned int *hdrLen)
1319 {
1320 DBT key, data;
1321 union _dbswap mi_offset;
1322 int rc;
1323
1324 if (dbc == NULL)
1325 return RPMRC_FAIL;
1326
1327 memset(&key, 0, sizeof(key));
1328 memset(&data, 0, sizeof(data));
1329
1330 if (hdrNum) {
1331 mi_offset.ui = hdrNum;
1332 if (dbiByteSwapped(dbc->dbi) == 1)
1333 _DBSWAP(mi_offset);
1334 key.data = (void *) &mi_offset;
1335 key.size = sizeof(mi_offset.ui);
1336 }
1337
1338 #if !defined(_USE_COPY_LOAD)
1339 data.flags |= DB_DBT_MALLOC;
1340 #endif
1341 rc = dbiCursorGet(dbc, &key, &data, hdrNum ? DB_SET : DB_NEXT);
1342 if (rc == 0) {
1343 if (hdrBlob)
1344 *hdrBlob = data.data;
1345 if (hdrLen)
1346 *hdrLen = data.size;
1347 return RPMRC_OK;
1348 } else if (rc == DB_NOTFOUND)
1349 return RPMRC_NOTFOUND;
1350 else
1351 return RPMRC_FAIL;
1352 }
1353
db3_pkgdbKey(dbiIndex dbi,dbiCursor dbc)1354 static unsigned int db3_pkgdbKey(dbiIndex dbi, dbiCursor dbc)
1355 {
1356 union _dbswap mi_offset;
1357
1358 if (dbc == NULL || dbc->key == NULL)
1359 return 0;
1360 memcpy(&mi_offset, dbc->key, sizeof(mi_offset.ui));
1361 if (dbiByteSwapped(dbc->dbi) == 1)
1362 _DBSWAP(mi_offset);
1363 return mi_offset.ui;
1364 }
1365
1366 struct rpmdbOps_s db3_dbops = {
1367 .name = "bdb",
1368 .path = "Packages",
1369
1370 .open = db3_dbiOpen,
1371 .close = db3_dbiClose,
1372 .verify = db3_dbiVerify,
1373
1374 .setFSync = db3_dbSetFSync,
1375 .ctrl = db3_Ctrl,
1376
1377 .cursorInit = db3_dbiCursorInit,
1378 .cursorFree = db3_dbiCursorFree,
1379
1380 .pkgdbGet = db3_pkgdbGet,
1381 .pkgdbPut = db3_pkgdbPut,
1382 .pkgdbDel = db3_pkgdbDel,
1383 .pkgdbKey = db3_pkgdbKey,
1384
1385 .idxdbGet = db3_idxdbGet,
1386 .idxdbPut = db3_idxdbPut,
1387 .idxdbDel = db3_idxdbDel,
1388 .idxdbKey = db3_idxdbKey
1389 };
1390
1391