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