1 /** \ingroup rpmdb dbi
2  * \file lib/rpmdb.c
3  */
4 
5 #include "system.h"
6 
7 #include <sys/file.h>
8 #include <utime.h>
9 #include <errno.h>
10 #include <dirent.h>
11 #include <fcntl.h>
12 
13 #ifndef	DYING	/* XXX already in "system.h" */
14 #include <fnmatch.h>
15 #endif
16 
17 #include <regex.h>
18 
19 #include <rpm/rpmtypes.h>
20 #include <rpm/rpmurl.h>
21 #include <rpm/rpmpgp.h>
22 #include <rpm/rpmpgp.h>
23 #include <rpm/rpmmacro.h>
24 #include <rpm/rpmsq.h>
25 #include <rpm/rpmstring.h>
26 #include <rpm/rpmfileutil.h>
27 #include <rpm/rpmds.h>			/* XXX isInstallPreReq macro only */
28 #include <rpm/rpmlog.h>
29 #include <rpm/rpmdb.h>
30 #include <rpm/rpmts.h>
31 #include <rpm/argv.h>
32 
33 #include "lib/rpmchroot.h"
34 #include "lib/rpmdb_internal.h"
35 #include "lib/fprint.h"
36 #include "lib/header_internal.h"	/* XXX for headerSetInstance() */
37 #include "lib/backend/dbi.h"
38 #include "lib/backend/dbiset.h"
39 #include "lib/misc.h"
40 #include "debug.h"
41 
42 #undef HASHTYPE
43 #undef HTKEYTYPE
44 #undef HTDATATYPE
45 #define HASHTYPE dbChk
46 #define HTKEYTYPE unsigned int
47 #define HTDATATYPE rpmRC
48 #include "lib/rpmhash.H"
49 #include "lib/rpmhash.C"
50 #undef HASHTYPE
51 #undef HTKEYTYPE
52 #undef HTDATATYPE
53 
54 static rpmdb rpmdbUnlink(rpmdb db);
55 
buildIndexes(rpmdb db)56 static int buildIndexes(rpmdb db)
57 {
58     int rc = 0;
59     Header h;
60     rpmdbMatchIterator mi;
61 
62     rc += rpmdbOpenAll(db);
63 
64     /* If the main db was just created, this is expected - dont whine */
65     if (!(dbiFlags(db->db_pkgs) & DBI_CREATED)) {
66 	rpmlog(RPMLOG_WARNING,
67 	       _("Generating %d missing index(es), please wait...\n"),
68 	       db->db_buildindex);
69     }
70 
71     /* Don't call us again */
72     db->db_buildindex = 0;
73 
74     dbSetFSync(db, 0);
75 
76     dbCtrl(db, RPMDB_CTRL_LOCK_RW);
77 
78     mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, NULL, 0);
79     while ((h = rpmdbNextIterator(mi))) {
80 	unsigned int hdrNum = headerGetInstance(h);
81 	/* Build all secondary indexes which were created on open */
82 	for (int dbix = 0; dbix < db->db_ndbi; dbix++) {
83 	    dbiIndex dbi = db->db_indexes[dbix];
84 	    if (dbi && (dbiFlags(dbi) & DBI_CREATED)) {
85 		rc += idxdbPut(dbi, db->db_tags[dbix], hdrNum, h);
86 	    }
87 	}
88     }
89     rpmdbFreeIterator(mi);
90 
91     dbCtrl(db, DB_CTRL_INDEXSYNC);
92     dbCtrl(db, DB_CTRL_UNLOCK_RW);
93 
94     dbSetFSync(db, !db->cfg.db_no_fsync);
95     return rc;
96 }
97 
uintCmp(unsigned int a,unsigned int b)98 static int uintCmp(unsigned int a, unsigned int b)
99 {
100     return (a != b);
101 }
102 
uintId(unsigned int a)103 static unsigned int uintId(unsigned int a)
104 {
105     return a;
106 }
107 
108 /** \ingroup dbi
109  * Return (newly allocated) integer of the epoch.
110  * @param s		version string optionally containing epoch number
111  * @retval version	only the version part of s
112  * @return		epoch integer within the [0; UINT32_MAX] interval,
113  *                      or -1 for no epoch
114  */
splitEpoch(const char * s,const char ** version)115 static int64_t splitEpoch(const char *s, const char **version)
116 {
117     int64_t e;
118     char *end;
119     int saveerrno = errno;
120 
121     *version = s;
122     e = strtol(s, &end, 10);
123     if (*end == ':' && e >= 0 && e <= UINT32_MAX) {
124 	*version = end + 1;
125     } else {
126 	e = -1;
127     }
128 
129     errno = saveerrno;
130     return e;
131 }
132 
pkgdbOpen(rpmdb db,int flags,dbiIndex * dbip)133 static int pkgdbOpen(rpmdb db, int flags, dbiIndex *dbip)
134 {
135     int rc = 0;
136     dbiIndex dbi = NULL;
137 
138     if (db == NULL)
139 	return -1;
140 
141     /* Is this it already open ? */
142     if ((dbi = db->db_pkgs) != NULL)
143 	goto exit;
144 
145     rc = dbiOpen(db, RPMDBI_PACKAGES, &dbi, flags);
146 
147     if (rc == 0) {
148 	int verifyonly = (flags & RPMDB_FLAG_VERIFYONLY);
149 
150 	db->db_pkgs = dbi;
151 	/* Allocate header checking cache .. based on some random number */
152 	if (!verifyonly && (db->db_checked == NULL)) {
153 	    db->db_checked = dbChkCreate(567, uintId, uintCmp, NULL, NULL);
154 	}
155 	/* If primary got created, we can safely run without fsync */
156 	if ((!verifyonly && (dbiFlags(dbi) & DBI_CREATED)) || db->cfg.db_no_fsync) {
157 	    rpmlog(RPMLOG_DEBUG, "disabling fsync on database\n");
158 	    db->cfg.db_no_fsync = 1;
159 	    dbSetFSync(db, 0);
160 	}
161     } else {
162 	rpmlog(RPMLOG_ERR, _("cannot open %s index using %s - %s (%d)\n"),
163 		   rpmTagGetName(RPMDBI_PACKAGES), db->db_descr,
164 		   (rc > 0 ? strerror(rc) : ""), rc);
165     }
166 
167 exit:
168     if (rc == 0 && dbip)
169 	*dbip = dbi;
170 
171     return rc;
172 }
173 
indexOpen(rpmdb db,rpmDbiTagVal rpmtag,int flags,dbiIndex * dbip)174 static int indexOpen(rpmdb db, rpmDbiTagVal rpmtag, int flags, dbiIndex *dbip)
175 {
176     int dbix, rc = 0;
177     dbiIndex dbi = NULL;
178 
179     if (db == NULL)
180 	return -1;
181 
182     for (dbix = 0; dbix < db->db_ndbi; dbix++) {
183 	if (rpmtag == db->db_tags[dbix])
184 	    break;
185     }
186     if (dbix >= db->db_ndbi)
187 	return -1;
188 
189     /* Is this index already open ? */
190     if ((dbi = db->db_indexes[dbix]) != NULL)
191 	goto exit;
192 
193     rc = dbiOpen(db, rpmtag, &dbi, flags);
194 
195     if (rc == 0) {
196 	int verifyonly = (flags & RPMDB_FLAG_VERIFYONLY);
197 	int rebuild = (db->db_flags & RPMDB_FLAG_REBUILD);
198 
199 	db->db_indexes[dbix] = dbi;
200 	if (!rebuild && !verifyonly && (dbiFlags(dbi) & DBI_CREATED)) {
201 	    rpmlog(RPMLOG_DEBUG, "index %s needs creating\n", dbiName(dbi));
202 	    db->db_buildindex++;
203 	    if (db->db_buildindex == 1) {
204 		buildIndexes(db);
205 	    }
206 	}
207     } else {
208 	rpmlog(RPMLOG_ERR, _("cannot open %s index using %s - %s (%d)\n"),
209 		   rpmTagGetName(rpmtag), db->db_descr,
210 		   (rc > 0 ? strerror(rc) : ""), rc);
211     }
212 
213 exit:
214     if (rc == 0 && dbip)
215 	*dbip = dbi;
216     return rc;
217 }
218 
indexGet(dbiIndex dbi,const char * keyp,size_t keylen,dbiIndexSet * set)219 static rpmRC indexGet(dbiIndex dbi, const char *keyp, size_t keylen,
220 		       dbiIndexSet *set)
221 {
222     rpmRC rc = RPMRC_FAIL; /* assume failure */
223     if (dbi != NULL) {
224 	dbiCursor dbc = dbiCursorInit(dbi, DBC_READ);
225 
226 	if (keyp) {
227 	    if (keylen == 0)
228 		keylen = strlen(keyp);
229 	    rc = idxdbGet(dbi, dbc, keyp, keylen, set, DBC_NORMAL_SEARCH);
230 	} else {
231 	    do {
232 		rc = idxdbGet(dbi, dbc, NULL, 0, set, DBC_NORMAL_SEARCH);
233 	    } while (rc == RPMRC_OK);
234 
235 	    /* If we got some results, not found is not an error */
236 	    if (rc == RPMRC_NOTFOUND && set != NULL)
237 		rc = RPMRC_OK;
238 	}
239 
240 	dbiCursorFree(dbi, dbc);
241     }
242     return rc;
243 }
244 
indexPrefixGet(dbiIndex dbi,const char * pfx,size_t plen,dbiIndexSet * set)245 static rpmRC indexPrefixGet(dbiIndex dbi, const char *pfx, size_t plen,
246 			    dbiIndexSet *set)
247 {
248     rpmRC rc = RPMRC_FAIL; /* assume failure */
249 
250     if (dbi != NULL && pfx) {
251 	dbiCursor dbc = dbiCursorInit(dbi, DBC_READ);
252 
253 	if (plen == 0)
254 	    plen = strlen(pfx);
255 	rc = idxdbGet(dbi, dbc, pfx, plen, set, DBC_PREFIX_SEARCH);
256 
257 	dbiCursorFree(dbi, dbc);
258     }
259     return rc;
260 }
261 
262 
263 typedef struct miRE_s {
264     rpmTagVal		tag;		/*!< header tag */
265     rpmMireMode		mode;		/*!< pattern match mode */
266     char *		pattern;	/*!< pattern string */
267     int			notmatch;	/*!< like "grep -v" */
268     regex_t *		preg;		/*!< regex compiled pattern buffer */
269     int			cflags;		/*!< regcomp(3) flags */
270     int			eflags;		/*!< regexec(3) flags */
271     int			fnflags;	/*!< fnmatch(3) flags */
272 } * miRE;
273 
274 struct rpmdbMatchIterator_s {
275     rpmdbMatchIterator	mi_next;
276     rpmdb		mi_db;
277     rpmDbiTagVal	mi_rpmtag;
278     dbiIndexSet		mi_set;
279     dbiCursor		mi_dbc;
280     int			mi_setx;
281     Header		mi_h;
282     int			mi_sorted;
283     int			mi_cflags;
284     int			mi_modified;
285     unsigned int	mi_prevoffset;	/* header instance (native endian) */
286     unsigned int	mi_offset;	/* header instance (native endian) */
287     unsigned int	mi_filenum;	/* tag element (native endian) */
288     int			mi_nre;
289     miRE		mi_re;
290     rpmts		mi_ts;
291     rpmRC (*mi_hdrchk) (rpmts ts, const void * uh, size_t uc, char ** msg);
292 
293 };
294 
295 struct rpmdbIndexIterator_s {
296     rpmdbIndexIterator	ii_next;
297     rpmdb		ii_db;
298     dbiIndex		ii_dbi;
299     rpmDbiTag		ii_rpmtag;
300     dbiCursor		ii_dbc;
301     dbiIndexSet		ii_set;
302     unsigned int	*ii_hdrNums;
303     int			ii_skipdata;
304 };
305 
306 static rpmdb rpmdbRock;
307 static rpmdbMatchIterator rpmmiRock;
308 static rpmdbIndexIterator rpmiiRock;
309 
rpmAtExit(void)310 void rpmAtExit(void)
311 {
312     rpmdb db;
313     rpmdbMatchIterator mi;
314     rpmdbIndexIterator ii;
315 
316     while ((mi = rpmmiRock) != NULL)
317 	rpmdbFreeIterator(mi);
318 
319     while ((ii = rpmiiRock) != NULL)
320 	rpmdbIndexIteratorFree(ii);
321 
322     while ((db = rpmdbRock) != NULL)
323 	(void) rpmdbClose(db);
324 }
325 
rpmdbOp(rpmdb rpmdb,rpmdbOpX opx)326 rpmop rpmdbOp(rpmdb rpmdb, rpmdbOpX opx)
327 {
328     rpmop op = NULL;
329     switch (opx) {
330     case RPMDB_OP_DBGET:
331 	op = &rpmdb->db_getops;
332 	break;
333     case RPMDB_OP_DBPUT:
334 	op = &rpmdb->db_putops;
335 	break;
336     case RPMDB_OP_DBDEL:
337 	op = &rpmdb->db_delops;
338 	break;
339     default:
340 	break;
341     }
342     return op;
343 }
344 
rpmdbHome(rpmdb db)345 const char *rpmdbHome(rpmdb db)
346 {
347     const char *dbdir = NULL;
348     if (db) {
349 	dbdir = rpmChrootDone() ? db->db_home : db->db_fullpath;
350     }
351     return dbdir;
352 }
353 
doOpen(rpmdb db,int justPkgs)354 static int doOpen(rpmdb db, int justPkgs)
355 {
356     int rc = pkgdbOpen(db, db->db_flags, NULL);
357     if (!justPkgs) {
358 	for (int dbix = 0; rc == 0 && dbix < db->db_ndbi; dbix++) {
359 	    rc += indexOpen(db, db->db_tags[dbix], db->db_flags, NULL);
360 	}
361     }
362     return rc;
363 }
364 
rpmdbOpenAll(rpmdb db)365 int rpmdbOpenAll(rpmdb db)
366 {
367     if (db == NULL) return -2;
368 
369     return doOpen(db, 0);
370 }
371 
dbiForeach(dbiIndex * dbis,int ndbi,int (* func)(dbiIndex,unsigned int),int del)372 static int dbiForeach(dbiIndex *dbis, int ndbi,
373 		  int (*func) (dbiIndex, unsigned int), int del)
374 {
375     int xx, rc = 0;
376     for (int dbix = ndbi; --dbix >= 0; ) {
377 	if (dbis[dbix] == NULL)
378 	    continue;
379 	xx = func(dbis[dbix], 0);
380 	if (xx && rc == 0) rc = xx;
381 	if (del)
382 	    dbis[dbix] = NULL;
383     }
384     return rc;
385 }
386 
rpmdbClose(rpmdb db)387 int rpmdbClose(rpmdb db)
388 {
389     rpmdb * prev, next;
390     int rc = 0;
391 
392     if (db == NULL)
393 	goto exit;
394 
395     prev = &rpmdbRock;
396     while ((next = *prev) != NULL && next != db)
397 	prev = &next->db_next;
398     if (!next)
399 	goto exit;
400 
401     (void) rpmdbUnlink(db);
402 
403     if (db->nrefs > 0)
404 	goto exit;
405 
406     /* Always re-enable fsync on close of rw-database */
407     if ((db->db_mode & O_ACCMODE) != O_RDONLY)
408 	dbSetFSync(db, 1);
409 
410     if (db->db_pkgs)
411 	rc = dbiClose(db->db_pkgs, 0);
412     rc += dbiForeach(db->db_indexes, db->db_ndbi, dbiClose, 1);
413 
414     db->db_root = _free(db->db_root);
415     db->db_home = _free(db->db_home);
416     db->db_fullpath = _free(db->db_fullpath);
417     db->db_checked = dbChkFree(db->db_checked);
418     db->db_indexes = _free(db->db_indexes);
419 
420     if (next) {
421         *prev = next->db_next;
422 	next->db_next = NULL;
423     }
424 
425     db = _free(db);
426 
427     if (rpmdbRock == NULL) {
428 	rpmsqActivate(0);
429     }
430 exit:
431     return rc;
432 }
433 
newRpmdb(const char * root,const char * home,int mode,int perms,int flags)434 static rpmdb newRpmdb(const char * root, const char * home,
435 		      int mode, int perms, int flags)
436 {
437     rpmdb db = NULL;
438     char * db_home = rpmGetPath((home && *home) ? home : "%{_dbpath}", NULL);
439 
440     static rpmDbiTag const dbiTags[] = {
441 	RPMDBI_NAME,
442 	RPMDBI_BASENAMES,
443 	RPMDBI_GROUP,
444 	RPMDBI_REQUIRENAME,
445 	RPMDBI_PROVIDENAME,
446 	RPMDBI_CONFLICTNAME,
447 	RPMDBI_OBSOLETENAME,
448 	RPMDBI_TRIGGERNAME,
449 	RPMDBI_DIRNAMES,
450 	RPMDBI_INSTALLTID,
451 	RPMDBI_SIGMD5,
452 	RPMDBI_SHA1HEADER,
453 	RPMDBI_FILETRIGGERNAME,
454 	RPMDBI_TRANSFILETRIGGERNAME,
455 	RPMDBI_RECOMMENDNAME,
456 	RPMDBI_SUGGESTNAME,
457 	RPMDBI_SUPPLEMENTNAME,
458 	RPMDBI_ENHANCENAME,
459     };
460 
461     if (!(db_home && db_home[0] != '%')) {
462 	rpmlog(RPMLOG_ERR, _("no dbpath has been set\n"));
463 	free(db_home);
464 	return NULL;
465     }
466 
467     db = xcalloc(sizeof(*db), 1);
468 
469     if (!(perms & 0600)) perms = 0644;	/* XXX sanity */
470 
471     db->db_mode = (mode >= 0) ? mode : 0;
472     db->db_perms = (perms >= 0) ? perms : 0644;
473     db->db_flags = (flags >= 0) ? flags : 0;
474 
475     db->db_home = db_home;
476     db->db_root = rpmGetPath((root && *root) ? root : "/", NULL);
477     db->db_fullpath = rpmGenPath(db->db_root, db->db_home, NULL);
478     /* XXX remove environment after chrooted operations, for now... */
479     db->db_remove_env = (!rstreq(db->db_root, "/") ? 1 : 0);
480     db->db_tags = dbiTags;
481     db->db_ndbi = sizeof(dbiTags) / sizeof(rpmDbiTag);
482     db->db_indexes = xcalloc(db->db_ndbi, sizeof(*db->db_indexes));
483     db->nrefs = 0;
484     return rpmdbLink(db);
485 }
486 
openDatabase(const char * prefix,const char * dbpath,rpmdb * dbp,int mode,int perms,int flags)487 static int openDatabase(const char * prefix,
488 		const char * dbpath, rpmdb *dbp,
489 		int mode, int perms, int flags)
490 {
491     rpmdb db;
492     int rc;
493     int justCheck = flags & RPMDB_FLAG_JUSTCHECK;
494 
495     if (dbp)
496 	*dbp = NULL;
497     if ((mode & O_ACCMODE) == O_WRONLY)
498 	return 1;
499 
500     db = newRpmdb(prefix, dbpath, mode, perms, flags);
501     if (db == NULL)
502 	return 1;
503 
504     db->db_next = rpmdbRock;
505     rpmdbRock = db;
506 
507     /* Try to ensure db home exists, error out if we can't even create */
508     rc = rpmioMkpath(rpmdbHome(db), 0755, getuid(), getgid());
509     if (rc == 0) {
510 	/* Open just bare minimum when rebuilding a potentially damaged db */
511 	int justPkgs = (db->db_flags & RPMDB_FLAG_REBUILD) &&
512 		       ((db->db_mode & O_ACCMODE) == O_RDONLY);
513 	/* Enable signal queue on the first db open */
514 	if (db->db_next == NULL) {
515 	    rpmsqActivate(1);
516 	}
517 
518 	rc = doOpen(db, justPkgs);
519 
520 	if (!db->db_descr)
521 	    db->db_descr = "unknown db";
522     }
523 
524     if (rc || justCheck || dbp == NULL)
525 	rpmdbClose(db);
526     else {
527         *dbp = db;
528     }
529 
530     return rc;
531 }
532 
rpmdbUnlink(rpmdb db)533 static rpmdb rpmdbUnlink(rpmdb db)
534 {
535     if (db)
536 	db->nrefs--;
537     return NULL;
538 }
539 
rpmdbLink(rpmdb db)540 rpmdb rpmdbLink(rpmdb db)
541 {
542     if (db)
543 	db->nrefs++;
544     return db;
545 }
546 
rpmdbOpen(const char * prefix,rpmdb * dbp,int mode,int perms)547 int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
548 {
549     return openDatabase(prefix, NULL, dbp, mode, perms, 0);
550 }
551 
rpmdbInit(const char * prefix,int perms)552 int rpmdbInit (const char * prefix, int perms)
553 {
554     rpmdb db = NULL;
555     int rc;
556 
557     rc = openDatabase(prefix, NULL, &db, (O_CREAT | O_RDWR), perms, 0);
558     if (db != NULL) {
559 	int xx = rpmdbClose(db);
560 	if (xx && rc == 0) rc = xx;
561 	db = NULL;
562     }
563     return rc;
564 }
565 
rpmdbVerify(const char * prefix)566 int rpmdbVerify(const char * prefix)
567 {
568     rpmdb db = NULL;
569     int rc = 0;
570 
571     rc = openDatabase(prefix, NULL, &db, O_RDONLY, 0644, RPMDB_FLAG_VERIFYONLY);
572 
573     if (db != NULL) {
574 	int xx;
575 
576 	if (db->db_pkgs)
577 	    rc += dbiVerify(db->db_pkgs, 0);
578 	rc += dbiForeach(db->db_indexes, db->db_ndbi, dbiVerify, 0);
579 
580 	xx = rpmdbClose(db);
581 	if (xx && rc == 0) rc = xx;
582 	db = NULL;
583     }
584     return rc;
585 }
586 
rpmdbGetHeaderAt(rpmdb db,unsigned int offset)587 Header rpmdbGetHeaderAt(rpmdb db, unsigned int offset)
588 {
589     rpmdbMatchIterator mi = rpmdbInitIterator(db, RPMDBI_PACKAGES,
590 					      &offset, sizeof(offset));
591     Header h = headerLink(rpmdbNextIterator(mi));
592     rpmdbFreeIterator(mi);
593     return h;
594 }
595 
596 /**
597  * Find file matches in database.
598  * @param db		rpm database
599  * @param dbi		index database handle (always RPMDBI_BASENAMES)
600  * @param filespec
601  * @param usestate	take file state into account?
602  * @retval matches
603  * @return 		RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL
604  */
rpmdbFindByFile(rpmdb db,dbiIndex dbi,const char * filespec,int usestate,dbiIndexSet * matches)605 static rpmRC rpmdbFindByFile(rpmdb db, dbiIndex dbi, const char *filespec,
606 			   int usestate, dbiIndexSet * matches)
607 {
608     char * dirName = NULL;
609     const char * baseName;
610     fingerPrintCache fpc = NULL;
611     fingerPrint * fp1 = NULL;
612     dbiIndexSet allMatches = NULL;
613     unsigned int i;
614     rpmRC rc = RPMRC_FAIL; /* assume error */
615 
616     *matches = NULL;
617     if (filespec == NULL) return rc; /* nothing alloced yet */
618 
619     if ((baseName = strrchr(filespec, '/')) != NULL) {
620 	size_t len = baseName - filespec + 1;
621 	dirName = strncpy(xmalloc(len + 1), filespec, len);
622 	dirName[len] = '\0';
623 	baseName++;
624     } else {
625 	dirName = xstrdup("");
626 	baseName = filespec;
627     }
628     if (baseName == NULL)
629 	goto exit;
630 
631     rc = indexGet(dbi, baseName, 0, &allMatches);
632 
633     if (rc || allMatches == NULL) goto exit;
634 
635     *matches = dbiIndexSetNew(0);
636     fpc = fpCacheCreate(allMatches->count, NULL);
637     fpLookup(fpc, dirName, baseName, &fp1);
638 
639     i = 0;
640     while (i < allMatches->count) {
641 	struct rpmtd_s bn, dn, di, fs;
642 	const char ** baseNames, ** dirNames;
643 	uint32_t * dirIndexes;
644 	unsigned int offset = dbiIndexRecordOffset(allMatches, i);
645 	unsigned int prevoff;
646 	Header h = rpmdbGetHeaderAt(db, offset);
647 
648 	if (h == NULL) {
649 	    i++;
650 	    continue;
651 	}
652 
653 	headerGet(h, RPMTAG_BASENAMES, &bn, HEADERGET_MINMEM);
654 	headerGet(h, RPMTAG_DIRNAMES, &dn, HEADERGET_MINMEM);
655 	headerGet(h, RPMTAG_DIRINDEXES, &di, HEADERGET_MINMEM);
656 	baseNames = bn.data;
657 	dirNames = dn.data;
658 	dirIndexes = di.data;
659 	if (usestate)
660 	    headerGet(h, RPMTAG_FILESTATES, &fs, HEADERGET_MINMEM);
661 
662 	do {
663 	    unsigned int num = dbiIndexRecordFileNumber(allMatches, i);
664 	    int skip = 0;
665 
666 	    if (usestate) {
667 		rpmtdSetIndex(&fs, num);
668 		if (!RPMFILE_IS_INSTALLED(rpmtdGetNumber(&fs))) {
669 		    skip = 1;
670 		}
671 	    }
672 
673 	    if (!skip) {
674 		const char *dirName = dirNames[dirIndexes[num]];
675 		if (fpLookupEquals(fpc, fp1, dirName, baseNames[num])) {
676 		    dbiIndexSetAppendOne(*matches, dbiIndexRecordOffset(allMatches, i),
677 					 dbiIndexRecordFileNumber(allMatches, i), 0);
678 		}
679 	    }
680 
681 	    prevoff = offset;
682 	    i++;
683 	    if (i < allMatches->count)
684 		offset = dbiIndexRecordOffset(allMatches, i);
685 	} while (i < allMatches->count && offset == prevoff);
686 
687 	rpmtdFreeData(&bn);
688 	rpmtdFreeData(&dn);
689 	rpmtdFreeData(&di);
690 	if (usestate)
691 	    rpmtdFreeData(&fs);
692 	headerFree(h);
693     }
694 
695     free(fp1);
696     fpCacheFree(fpc);
697 
698     if ((*matches)->count == 0) {
699 	*matches = dbiIndexSetFree(*matches);
700 	rc = RPMRC_NOTFOUND;
701     } else {
702 	rc = RPMRC_OK;
703     }
704 
705 exit:
706     dbiIndexSetFree(allMatches);
707     free(dirName);
708     return rc;
709 }
710 
rpmdbCountPackages(rpmdb db,const char * name)711 int rpmdbCountPackages(rpmdb db, const char * name)
712 {
713     int count = -1;
714     dbiIndex dbi = NULL;
715 
716     if (name != NULL && indexOpen(db, RPMDBI_NAME, 0, &dbi) == 0) {
717 	dbiIndexSet matches = NULL;
718 
719 	rpmRC rc = indexGet(dbi, name, strlen(name), &matches);
720 
721 	if (rc == RPMRC_OK) {
722 	    count = dbiIndexSetCount(matches);
723 	} else {
724 	    count = (rc == RPMRC_NOTFOUND) ? 0 : -1;
725 	}
726 	dbiIndexSetFree(matches);
727     }
728 
729     return count;
730 }
731 
732 /**
733  * Attempt partial matches on name[-version[-release]][.arch] strings.
734  * @param db		rpmdb handle
735  * @param dbi		index database
736  * @param name		package name
737  * @param epoch 	package epoch (-1 for any epoch)
738  * @param version	package version (can be a pattern)
739  * @param release	package release (can be a pattern)
740  * @param arch		package arch (can be a pattern)
741  * @retval matches	set of header instances that match
742  * @return 		RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL
743  */
dbiFindMatches(rpmdb db,dbiIndex dbi,const char * name,int64_t epoch,const char * version,const char * release,const char * arch,dbiIndexSet * matches)744 static rpmRC dbiFindMatches(rpmdb db, dbiIndex dbi,
745 		const char * name,
746 		int64_t epoch,
747 		const char * version,
748 		const char * release,
749 		const char * arch,
750 		dbiIndexSet * matches)
751 {
752     unsigned int gotMatches = 0;
753     rpmRC rc;
754     unsigned int i;
755 
756     rc = indexGet(dbi, name, strlen(name), matches);
757 
758     /* No matches on the name, anything else wont match either */
759     if (rc != RPMRC_OK)
760 	goto exit;
761 
762     /* If we got matches on name and nothing else was specified, we're done */
763     if (epoch < 0 && version == NULL && release == NULL && arch == NULL)
764 	goto exit;
765 
766     /* Make sure the version and release match. */
767     for (i = 0; i < dbiIndexSetCount(*matches); i++) {
768 	unsigned int recoff = dbiIndexRecordOffset(*matches, i);
769 	rpmdbMatchIterator mi;
770 	Header h;
771 
772 	if (recoff == 0)
773 	    continue;
774 
775 	mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &recoff, sizeof(recoff));
776 
777 	/* Set iterator selectors for version/release if available. */
778 	if (version &&
779 	    rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version))
780 	{
781 	    rc = RPMRC_FAIL;
782 	    goto exit;
783 	}
784 	if (release &&
785 	    rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release))
786 	{
787 	    rc = RPMRC_FAIL;
788 	    goto exit;
789 	}
790 	if (arch &&
791 	    rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_DEFAULT, arch))
792 	{
793 	    rc = RPMRC_FAIL;
794 	    goto exit;
795 	}
796 
797 	h = rpmdbNextIterator(mi);
798 
799 	if (epoch >= 0 && h) {
800 	    struct rpmtd_s td;
801 	    headerGet(h, RPMTAG_EPOCH, &td, HEADERGET_MINMEM);
802 	    if (epoch != rpmtdGetNumber(&td))
803 		h = NULL;
804 	    rpmtdFreeData(&td);
805 	}
806 	if (h)
807 	    (*matches)->recs[gotMatches++] = (*matches)->recs[i];
808 	else
809 	    (*matches)->recs[i].hdrNum = 0;
810 	rpmdbFreeIterator(mi);
811     }
812 
813     if (gotMatches) {
814 	(*matches)->count = gotMatches;
815 	rc = RPMRC_OK;
816     } else
817 	rc = RPMRC_NOTFOUND;
818 
819 exit:
820 /* FIX: double indirection */
821     if (rc && matches && *matches)
822 	*matches = dbiIndexSetFree(*matches);
823     return rc;
824 }
825 
826 /**
827  * Lookup by name, name-version, and finally by name-version-release.
828  * Both version and release can be patterns.
829  * @todo Name must be an exact match, as name is a db key.
830  * @param db		rpmdb handle
831  * @param dbi		index database handle (always RPMDBI_NAME)
832  * @param arg		name[-[epoch:]version[-release]] string
833  * @param arglen	length of arg
834  * @param arch		possible arch string (or NULL)
835  * @retval matches	set of header instances that match
836  * @return 		RPMRC_OK on match, RPMRC_NOMATCH or RPMRC_FAIL
837  */
dbiFindByLabelArch(rpmdb db,dbiIndex dbi,const char * arg,size_t arglen,const char * arch,dbiIndexSet * matches)838 static rpmRC dbiFindByLabelArch(rpmdb db, dbiIndex dbi,
839 			    const char * arg, size_t arglen, const char *arch,
840 			    dbiIndexSet * matches)
841 {
842     char localarg[arglen+1];
843     int64_t epoch;
844     const char * version;
845     const char * release;
846     char * s;
847     char c;
848     int brackets;
849     rpmRC rc;
850 
851     if (arglen == 0) return RPMRC_NOTFOUND;
852 
853     strncpy(localarg, arg, arglen);
854     localarg[arglen] = '\0';
855 
856     /* did they give us just a name? */
857     rc = dbiFindMatches(db, dbi, localarg, -1, NULL, NULL, arch, matches);
858     if (rc != RPMRC_NOTFOUND)
859 	goto exit;
860 
861     /* FIX: double indirection */
862     *matches = dbiIndexSetFree(*matches);
863 
864     /* maybe a name-[epoch:]version ? */
865     s = localarg + arglen;
866 
867     c = '\0';
868     brackets = 0;
869     for (s -= 1; s > localarg; s--) {
870 	switch (*s) {
871 	case '[':
872 	    brackets = 1;
873 	    break;
874 	case ']':
875 	    if (c != '[') brackets = 0;
876 	    break;
877 	}
878 	if (!brackets && c && *s == '-')
879 	    break;
880 	c = *s;
881     }
882 
883    	/* FIX: *matches may be NULL. */
884     if (s == localarg) {
885 	rc = RPMRC_NOTFOUND;
886 	goto exit;
887     }
888 
889     *s = '\0';
890 
891     epoch = splitEpoch(s + 1, &version);
892     rc = dbiFindMatches(db, dbi, localarg, epoch, version, NULL, arch, matches);
893     if (rc != RPMRC_NOTFOUND) goto exit;
894 
895     /* FIX: double indirection */
896     *matches = dbiIndexSetFree(*matches);
897 
898     /* how about name-[epoch:]version-release? */
899 
900     release = s + 1;
901 
902     c = '\0';
903     brackets = 0;
904     for (; s > localarg; s--) {
905 	switch (*s) {
906 	case '[':
907 	    brackets = 1;
908 	    break;
909 	case ']':
910 	    if (c != '[') brackets = 0;
911 	    break;
912 	}
913 	if (!brackets && c && *s == '-')
914 	    break;
915 	c = *s;
916     }
917 
918     if (s == localarg) {
919 	rc = RPMRC_NOTFOUND;
920 	goto exit;
921     }
922 
923     *s = '\0';
924    	/* FIX: *matches may be NULL. */
925     epoch = splitEpoch(s + 1, &version);
926     rc = dbiFindMatches(db, dbi, localarg, epoch, version, release, arch, matches);
927 exit:
928     return rc;
929 }
930 
dbiFindByLabel(rpmdb db,dbiIndex dbi,const char * label,dbiIndexSet * matches)931 static rpmRC dbiFindByLabel(rpmdb db, dbiIndex dbi, const char * label,
932 			    dbiIndexSet * matches)
933 {
934     const char *arch = NULL;
935     /* First, try with label as it is */
936     rpmRC rc = dbiFindByLabelArch(db, dbi, label, strlen(label), NULL, matches);
937 
938     /* If not found, retry with possible .arch specifier if there is one */
939     if (rc == RPMRC_NOTFOUND && (arch = strrchr(label, '.')))
940 	rc = dbiFindByLabelArch(db, dbi, label, arch-label, arch+1, matches);
941 
942     return rc;
943 }
944 
945 /**
946  * Rewrite a header into packages (if necessary) and free the header.
947  *   Note: this is called from a markReplacedFiles iteration, and *must*
948  *   preserve the "join key" (i.e. offset) for the header.
949  * @param mi		database iterator
950  * @param dbi		index database handle
951  * @return 		0 on success
952  */
miFreeHeader(rpmdbMatchIterator mi,dbiIndex dbi)953 static int miFreeHeader(rpmdbMatchIterator mi, dbiIndex dbi)
954 {
955     int rc = 0;
956 
957     if (mi == NULL || mi->mi_h == NULL)
958 	return 0;
959 
960     if (dbi && mi->mi_dbc && mi->mi_modified && mi->mi_prevoffset) {
961 	rpmRC rpmrc = RPMRC_NOTFOUND;
962 	unsigned int hdrLen = 0;
963 	unsigned char *hdrBlob = headerExport(mi->mi_h, &hdrLen);
964 
965 	/* Check header digest/signature on blob export (if requested). */
966 	if (mi->mi_hdrchk && mi->mi_ts) {
967 	    char * msg = NULL;
968 	    int lvl;
969 
970 	    rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, hdrBlob, hdrLen, &msg);
971 	    lvl = (rpmrc == RPMRC_FAIL ? RPMLOG_ERR : RPMLOG_DEBUG);
972 	    rpmlog(lvl, "%s h#%8u %s",
973 		(rpmrc == RPMRC_FAIL ? _("miFreeHeader: skipping") : "write"),
974 			mi->mi_prevoffset, (msg ? msg : "\n"));
975 	    msg = _free(msg);
976 	}
977 
978 	if (hdrBlob != NULL && rpmrc != RPMRC_FAIL) {
979 	    rpmsqBlock(SIG_BLOCK);
980 	    dbCtrl(mi->mi_db, DB_CTRL_LOCK_RW);
981 	    rc = pkgdbPut(dbi, mi->mi_dbc, &mi->mi_prevoffset,
982 			  hdrBlob, hdrLen);
983 	    dbCtrl(mi->mi_db, DB_CTRL_INDEXSYNC);
984 	    dbCtrl(mi->mi_db, DB_CTRL_UNLOCK_RW);
985 	    rpmsqBlock(SIG_UNBLOCK);
986 
987 	    if (rc) {
988 		rpmlog(RPMLOG_ERR,
989 			_("error(%d) storing record #%d into %s\n"),
990 			rc, mi->mi_prevoffset, dbiName(dbi));
991 	    }
992 	}
993 	free(hdrBlob);
994     }
995 
996     mi->mi_h = headerFree(mi->mi_h);
997 
998     return rc;
999 }
1000 
rpmdbFreeIterator(rpmdbMatchIterator mi)1001 rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi)
1002 {
1003     rpmdbMatchIterator * prev, next;
1004     dbiIndex dbi = NULL;
1005     int i;
1006 
1007     if (mi == NULL)
1008 	return NULL;
1009 
1010     prev = &rpmmiRock;
1011     while ((next = *prev) != NULL && next != mi)
1012 	prev = &next->mi_next;
1013     if (next) {
1014 	*prev = next->mi_next;
1015 	next->mi_next = NULL;
1016     } else
1017 	return NULL;
1018 
1019     pkgdbOpen(mi->mi_db, 0, &dbi);
1020 
1021     miFreeHeader(mi, dbi);
1022 
1023     mi->mi_dbc = dbiCursorFree(dbi, mi->mi_dbc);
1024 
1025     if (mi->mi_re != NULL)
1026     for (i = 0; i < mi->mi_nre; i++) {
1027 	miRE mire = mi->mi_re + i;
1028 	mire->pattern = _free(mire->pattern);
1029 	if (mire->preg != NULL) {
1030 	    regfree(mire->preg);
1031 	    mire->preg = _free(mire->preg);
1032 	}
1033     }
1034     mi->mi_re = _free(mi->mi_re);
1035 
1036     mi->mi_set = dbiIndexSetFree(mi->mi_set);
1037     rpmdbClose(mi->mi_db);
1038     mi->mi_ts = rpmtsFree(mi->mi_ts);
1039 
1040     mi = _free(mi);
1041 
1042     (void) rpmsqPoll();
1043 
1044     return NULL;
1045 }
1046 
rpmdbGetIteratorOffset(rpmdbMatchIterator mi)1047 unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi)
1048 {
1049     return (mi ? mi->mi_offset : 0);
1050 }
1051 
rpmdbGetIteratorFileNum(rpmdbMatchIterator mi)1052 unsigned int rpmdbGetIteratorFileNum(rpmdbMatchIterator mi)
1053 {
1054     return (mi ? mi->mi_filenum : 0);
1055 }
1056 
rpmdbGetIteratorCount(rpmdbMatchIterator mi)1057 int rpmdbGetIteratorCount(rpmdbMatchIterator mi)
1058 {
1059     return (mi && mi->mi_set ?  mi->mi_set->count : 0);
1060 }
1061 
rpmdbGetIteratorIndex(rpmdbMatchIterator mi)1062 int rpmdbGetIteratorIndex(rpmdbMatchIterator mi)
1063 {
1064     return (mi ? mi->mi_setx : 0);
1065 }
1066 
rpmdbSetIteratorIndex(rpmdbMatchIterator mi,unsigned int ix)1067 void rpmdbSetIteratorIndex(rpmdbMatchIterator mi, unsigned int ix)
1068 {
1069     if (mi)
1070 	mi->mi_setx = ix;
1071 }
1072 
rpmdbGetIteratorOffsetFor(rpmdbMatchIterator mi,unsigned int ix)1073 unsigned int rpmdbGetIteratorOffsetFor(rpmdbMatchIterator mi, unsigned int ix)
1074 {
1075     if (mi && mi->mi_set && ix < mi->mi_set->count)
1076 	return mi->mi_set->recs[ix].hdrNum;
1077     return 0;
1078 }
1079 
1080 /**
1081  * Return pattern match.
1082  * @param mire		match iterator regex
1083  * @param val		value to match
1084  * @return		0 if pattern matches, >0 on nomatch, <0 on error
1085  */
miregexec(miRE mire,const char * val)1086 static int miregexec(miRE mire, const char * val)
1087 {
1088     int rc = 0;
1089 
1090     switch (mire->mode) {
1091     case RPMMIRE_STRCMP:
1092 	rc = (!rstreq(mire->pattern, val));
1093 	break;
1094     case RPMMIRE_DEFAULT:
1095     case RPMMIRE_REGEX:
1096 	rc = regexec(mire->preg, val, 0, NULL, mire->eflags);
1097 	if (rc && rc != REG_NOMATCH) {
1098 	    char msg[256];
1099 	    (void) regerror(rc, mire->preg, msg, sizeof(msg)-1);
1100 	    msg[sizeof(msg)-1] = '\0';
1101 	    rpmlog(RPMLOG_ERR, _("%s: regexec failed: %s\n"),
1102 			mire->pattern, msg);
1103 	    rc = -1;
1104 	}
1105 	break;
1106     case RPMMIRE_GLOB:
1107 	rc = fnmatch(mire->pattern, val, mire->fnflags);
1108 	if (rc && rc != FNM_NOMATCH)
1109 	    rc = -1;
1110 	break;
1111     default:
1112 	rc = -1;
1113 	break;
1114     }
1115 
1116     return rc;
1117 }
1118 
1119 /**
1120  * Compare iterator selectors by rpm tag (qsort/bsearch).
1121  * @param a		1st iterator selector
1122  * @param b		2nd iterator selector
1123  * @return		result of comparison
1124  */
mireCmp(const void * a,const void * b)1125 static int mireCmp(const void * a, const void * b)
1126 {
1127     const miRE mireA = (const miRE) a;
1128     const miRE mireB = (const miRE) b;
1129     return (mireA->tag - mireB->tag);
1130 }
1131 
1132 /**
1133  * Copy pattern, escaping for appropriate mode.
1134  * @param tag		rpm tag
1135  * @retval modep	type of pattern match
1136  * @param pattern	pattern to duplicate
1137  * @return		duplicated pattern
1138  */
mireDup(rpmTagVal tag,rpmMireMode * modep,const char * pattern)1139 static char * mireDup(rpmTagVal tag, rpmMireMode *modep,
1140 			const char * pattern)
1141 {
1142     const char * s;
1143     char * pat;
1144     char * t;
1145     int brackets;
1146     size_t nb;
1147     int c;
1148 
1149     switch (*modep) {
1150     default:
1151     case RPMMIRE_DEFAULT:
1152 	if (tag == RPMTAG_DIRNAMES || tag == RPMTAG_BASENAMES) {
1153 	    *modep = RPMMIRE_GLOB;
1154 	    pat = xstrdup(pattern);
1155 	    break;
1156 	}
1157 
1158 	nb = strlen(pattern) + sizeof("^$");
1159 
1160 	/* Find no. of bytes needed for pattern. */
1161 	/* periods and plusses are escaped, splats become '.*' */
1162 	c = '\0';
1163 	brackets = 0;
1164 	for (s = pattern; *s != '\0'; s++) {
1165 	    switch (*s) {
1166 	    case '.':
1167 	    case '+':
1168 	    case '*':
1169 		if (!brackets) nb++;
1170 		break;
1171 	    case '\\':
1172 		s++;
1173 		break;
1174 	    case '[':
1175 		brackets = 1;
1176 		break;
1177 	    case ']':
1178 		if (c != '[') brackets = 0;
1179 		break;
1180 	    }
1181 	    c = *s;
1182 	}
1183 
1184 	pat = t = xmalloc(nb);
1185 
1186 	if (pattern[0] != '^') *t++ = '^';
1187 
1188 	/* Copy pattern, escaping periods, prefixing splats with period. */
1189 	c = '\0';
1190 	brackets = 0;
1191 	for (s = pattern; *s != '\0'; s++, t++) {
1192 	    switch (*s) {
1193 	    case '.':
1194 	    case '+':
1195 		if (!brackets) *t++ = '\\';
1196 		break;
1197 	    case '*':
1198 		if (!brackets) *t++ = '.';
1199 		break;
1200 	    case '\\':
1201 		*t++ = *s++;
1202 		break;
1203 	    case '[':
1204 		brackets = 1;
1205 		break;
1206 	    case ']':
1207 		if (c != '[') brackets = 0;
1208 		break;
1209 	    }
1210 	    c = *t = *s;
1211 	}
1212 
1213 	if (s > pattern && s[-1] != '$') *t++ = '$';
1214 	*t = '\0';
1215 	*modep = RPMMIRE_REGEX;
1216 	break;
1217     case RPMMIRE_STRCMP:
1218     case RPMMIRE_REGEX:
1219     case RPMMIRE_GLOB:
1220 	pat = xstrdup(pattern);
1221 	break;
1222     }
1223 
1224     return pat;
1225 }
1226 
rpmdbSetIteratorRE(rpmdbMatchIterator mi,rpmTagVal tag,rpmMireMode mode,const char * pattern)1227 int rpmdbSetIteratorRE(rpmdbMatchIterator mi, rpmTagVal tag,
1228 		rpmMireMode mode, const char * pattern)
1229 {
1230     static rpmMireMode defmode = (rpmMireMode)-1;
1231     miRE mire = NULL;
1232     char * allpat = NULL;
1233     int notmatch = 0;
1234     regex_t * preg = NULL;
1235     int cflags = 0;
1236     int eflags = 0;
1237     int fnflags = 0;
1238     int rc = 0;
1239 
1240     if (defmode == (rpmMireMode)-1) {
1241 	char *t = rpmExpand("%{?_query_selector_match}", NULL);
1242 
1243 	if (*t == '\0' || rstreq(t, "default"))
1244 	    defmode = RPMMIRE_DEFAULT;
1245 	else if (rstreq(t, "strcmp"))
1246 	    defmode = RPMMIRE_STRCMP;
1247 	else if (rstreq(t, "regex"))
1248 	    defmode = RPMMIRE_REGEX;
1249 	else if (rstreq(t, "glob"))
1250 	    defmode = RPMMIRE_GLOB;
1251 	else
1252 	    defmode = RPMMIRE_DEFAULT;
1253 	free(t);
1254      }
1255 
1256     if (mi == NULL || pattern == NULL)
1257 	return rc;
1258 
1259     /* Leading '!' inverts pattern match sense, like "grep -v". */
1260     if (*pattern == '!') {
1261 	notmatch = 1;
1262 	pattern++;
1263     }
1264 
1265     allpat = mireDup(tag, &mode, pattern);
1266 
1267     if (mode == RPMMIRE_DEFAULT)
1268 	mode = defmode;
1269 
1270     switch (mode) {
1271     case RPMMIRE_DEFAULT:
1272     case RPMMIRE_STRCMP:
1273 	break;
1274     case RPMMIRE_REGEX:
1275 	preg = xcalloc(1, sizeof(*preg));
1276 	cflags = (REG_EXTENDED | REG_NOSUB);
1277 	rc = regcomp(preg, allpat, cflags);
1278 	if (rc) {
1279 	    char msg[256];
1280 	    (void) regerror(rc, preg, msg, sizeof(msg)-1);
1281 	    msg[sizeof(msg)-1] = '\0';
1282 	    rpmlog(RPMLOG_ERR, _("%s: regcomp failed: %s\n"), allpat, msg);
1283 	}
1284 	break;
1285     case RPMMIRE_GLOB:
1286 	fnflags = FNM_PATHNAME | FNM_PERIOD;
1287 	break;
1288     default:
1289 	rc = -1;
1290 	break;
1291     }
1292 
1293     if (rc) {
1294 	/* FIX: mire has kept values */
1295 	allpat = _free(allpat);
1296 	if (preg) {
1297 	    regfree(preg);
1298 	    preg = _free(preg);
1299 	}
1300 	return rc;
1301     }
1302 
1303     mi->mi_re = xrealloc(mi->mi_re, (mi->mi_nre + 1) * sizeof(*mi->mi_re));
1304     mire = mi->mi_re + mi->mi_nre;
1305     mi->mi_nre++;
1306 
1307     mire->tag = tag;
1308     mire->mode = mode;
1309     mire->pattern = allpat;
1310     mire->notmatch = notmatch;
1311     mire->preg = preg;
1312     mire->cflags = cflags;
1313     mire->eflags = eflags;
1314     mire->fnflags = fnflags;
1315 
1316     if (mi->mi_nre > 1)
1317 	qsort(mi->mi_re, mi->mi_nre, sizeof(*mi->mi_re), mireCmp);
1318 
1319     return rc;
1320 }
1321 
1322 /**
1323  * Return iterator selector match.
1324  * @param mi		rpm database iterator
1325  * @return		1 if header should be skipped
1326  */
mireSkip(const rpmdbMatchIterator mi)1327 static int mireSkip (const rpmdbMatchIterator mi)
1328 {
1329     miRE mire;
1330     uint32_t zero = 0;
1331     int ntags = 0;
1332     int nmatches = 0;
1333     int rc;
1334 
1335     if (mi->mi_h == NULL)	/* XXX can't happen */
1336 	return 0;
1337 
1338     /*
1339      * Apply tag tests, implicitly "||" for multiple patterns/values of a
1340      * single tag, implicitly "&&" between multiple tag patterns.
1341      */
1342     if ((mire = mi->mi_re) != NULL)
1343     for (int i = 0; i < mi->mi_nre; i++, mire++) {
1344 	int anymatch;
1345 	struct rpmtd_s td;
1346 
1347 	if (!headerGet(mi->mi_h, mire->tag, &td, HEADERGET_MINMEM)) {
1348 	    if (mire->tag != RPMTAG_EPOCH) {
1349 		ntags++;
1350 		continue;
1351 	    }
1352 	    /* "is package already installed" checks rely on this behavior */
1353 	    td.count = 1;
1354 	    td.type = RPM_INT32_TYPE;
1355 	    td.data = &zero;
1356 	}
1357 
1358 	anymatch = 0;		/* no matches yet */
1359 	while (1) {
1360 	    rpmtdInit(&td);
1361 	    while (rpmtdNext(&td) >= 0) {
1362 	    	char *str = rpmtdFormat(&td, RPMTD_FORMAT_STRING, NULL);
1363 		if (str) {
1364 		    rc = miregexec(mire, str);
1365 		    if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
1366 			anymatch++;
1367 		    free(str);
1368 		}
1369 	    }
1370 	    if ((i+1) < mi->mi_nre && mire[0].tag == mire[1].tag) {
1371 		i++;
1372 		mire++;
1373 		continue;
1374 	    }
1375 	    break;
1376 	}
1377 	rpmtdFreeData(&td);
1378 
1379 	ntags++;
1380 	if (anymatch)
1381 	    nmatches++;
1382     }
1383 
1384     return (ntags == nmatches ? 0 : 1);
1385 }
1386 
rpmdbSetIteratorRewrite(rpmdbMatchIterator mi,int rewrite)1387 int rpmdbSetIteratorRewrite(rpmdbMatchIterator mi, int rewrite)
1388 {
1389     int rc;
1390     if (mi == NULL)
1391 	return 0;
1392     rc = (mi->mi_cflags & DBC_WRITE) ? 1 : 0;
1393     if (rewrite)
1394 	mi->mi_cflags |= DBC_WRITE;
1395     else
1396 	mi->mi_cflags &= ~DBC_WRITE;
1397     return rc;
1398 }
1399 
rpmdbSetIteratorModified(rpmdbMatchIterator mi,int modified)1400 int rpmdbSetIteratorModified(rpmdbMatchIterator mi, int modified)
1401 {
1402     int rc;
1403     if (mi == NULL)
1404 	return 0;
1405     rc = mi->mi_modified;
1406     mi->mi_modified = modified;
1407     return rc;
1408 }
1409 
rpmdbSetHdrChk(rpmdbMatchIterator mi,rpmts ts,rpmRC (* hdrchk)(rpmts ts,const void * uh,size_t uc,char ** msg))1410 int rpmdbSetHdrChk(rpmdbMatchIterator mi, rpmts ts,
1411 	rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg))
1412 {
1413     int rc = 0;
1414     if (mi == NULL)
1415 	return 0;
1416     mi->mi_ts = rpmtsLink(ts);
1417     mi->mi_hdrchk = hdrchk;
1418     return rc;
1419 }
1420 
miVerifyHeader(rpmdbMatchIterator mi,const void * uh,size_t uhlen)1421 static rpmRC miVerifyHeader(rpmdbMatchIterator mi, const void *uh, size_t uhlen)
1422 {
1423     rpmRC rpmrc = RPMRC_NOTFOUND;
1424 
1425     if (!(mi->mi_hdrchk && mi->mi_ts))
1426 	return rpmrc;
1427 
1428     /* Don't bother re-checking a previously read header. */
1429     if (mi->mi_db->db_checked) {
1430 	rpmRC *res;
1431 	if (dbChkGetEntry(mi->mi_db->db_checked, mi->mi_offset,
1432 			  &res, NULL, NULL)) {
1433 	    rpmrc = res[0];
1434 	}
1435     }
1436 
1437     /* If blob is unchecked, check blob import consistency now. */
1438     if (rpmrc != RPMRC_OK) {
1439 	char * msg = NULL;
1440 	int lvl;
1441 
1442 	rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, uh, uhlen, &msg);
1443 	lvl = (rpmrc == RPMRC_FAIL ? RPMLOG_ERR : RPMLOG_DEBUG);
1444 	rpmlog(lvl, "%s h#%8u %s\n",
1445 	    (rpmrc == RPMRC_FAIL ? _("rpmdbNextIterator: skipping") : " read"),
1446 		    mi->mi_offset, (msg ? msg : ""));
1447 	msg = _free(msg);
1448 
1449 	/* Mark header checked. */
1450 	if (mi->mi_db && mi->mi_db->db_checked) {
1451 	    dbChkAddEntry(mi->mi_db->db_checked, mi->mi_offset, rpmrc);
1452 	}
1453     }
1454     return rpmrc;
1455 }
1456 
1457 /* FIX: mi->mi_key.data may be NULL */
rpmdbNextIterator(rpmdbMatchIterator mi)1458 Header rpmdbNextIterator(rpmdbMatchIterator mi)
1459 {
1460     dbiIndex dbi = NULL;
1461     unsigned char * uh;
1462     unsigned int uhlen;
1463     int rc;
1464     headerImportFlags importFlags = HEADERIMPORT_FAST;
1465 
1466     if (mi == NULL)
1467 	return NULL;
1468 
1469     if (pkgdbOpen(mi->mi_db, 0, &dbi))
1470 	return NULL;
1471 
1472 #if defined(_USE_COPY_LOAD)
1473     importFlags |= HEADERIMPORT_COPY;
1474 #endif
1475     /*
1476      * Cursors are per-iterator, not per-dbi, so get a cursor for the
1477      * iterator on 1st call. If the iteration is to rewrite headers,
1478      * then the cursor needs to marked with DBC_WRITE as well.
1479      */
1480     if (mi->mi_dbc == NULL)
1481 	mi->mi_dbc = dbiCursorInit(dbi, mi->mi_cflags);
1482 
1483 top:
1484     uh = NULL;
1485     uhlen = 0;
1486 
1487     do {
1488 	if (mi->mi_set) {
1489 	    if (!(mi->mi_setx < mi->mi_set->count))
1490 		return NULL;
1491 	    mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx);
1492 	    mi->mi_filenum = dbiIndexRecordFileNumber(mi->mi_set, mi->mi_setx);
1493 	} else {
1494 	    rc = pkgdbGet(dbi, mi->mi_dbc, 0, &uh, &uhlen);
1495 	    if (rc == 0)
1496 		mi->mi_offset = pkgdbKey(dbi, mi->mi_dbc);
1497 
1498 	    /* Terminate on error or end of keys */
1499 	    if (rc || (mi->mi_setx && mi->mi_offset == 0))
1500 		return NULL;
1501 	}
1502 	mi->mi_setx++;
1503     } while (mi->mi_offset == 0);
1504 
1505     /* If next header is identical, return it now. */
1506     if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset)
1507 	return mi->mi_h;
1508 
1509     /* Retrieve next header blob for index iterator. */
1510     if (uh == NULL) {
1511 	rc = pkgdbGet(dbi, mi->mi_dbc, mi->mi_offset, &uh, &uhlen);
1512 	if (rc)
1513 	    return NULL;
1514     }
1515 
1516     /* Rewrite current header (if necessary) and unlink. */
1517     miFreeHeader(mi, dbi);
1518 
1519     /* Is this the end of the iteration? */
1520     if (uh == NULL)
1521 	return NULL;
1522 
1523     /* Verify header if enabled, skip damaged and inconsistent headers */
1524     if (miVerifyHeader(mi, uh, uhlen) == RPMRC_FAIL) {
1525 	goto top;
1526     }
1527 
1528     /* Did the header blob load correctly? */
1529     mi->mi_h = headerImport(uh, uhlen, importFlags);
1530     if (mi->mi_h == NULL || !headerIsEntry(mi->mi_h, RPMTAG_NAME)) {
1531 	rpmlog(RPMLOG_ERR,
1532 		_("rpmdb: damaged header #%u retrieved -- skipping.\n"),
1533 		mi->mi_offset);
1534 	goto top;
1535     }
1536 
1537     /*
1538      * Skip this header if iterator selector (if any) doesn't match.
1539      */
1540     if (mireSkip(mi)) {
1541 	goto top;
1542     }
1543     headerSetInstance(mi->mi_h, mi->mi_offset);
1544 
1545     mi->mi_prevoffset = mi->mi_offset;
1546     mi->mi_modified = 0;
1547 
1548     return mi->mi_h;
1549 }
1550 
1551 /** \ingroup rpmdb
1552  * sort the iterator by (recnum, filenum)
1553  * Return database iterator.
1554  * @param mi		rpm database iterator
1555  */
rpmdbSortIterator(rpmdbMatchIterator mi)1556 void rpmdbSortIterator(rpmdbMatchIterator mi)
1557 {
1558     if (mi && mi->mi_set) {
1559 	dbiIndexSetSort(mi->mi_set);
1560 	mi->mi_sorted = 1;
1561     }
1562 }
1563 
rpmdbUniqIterator(rpmdbMatchIterator mi)1564 void rpmdbUniqIterator(rpmdbMatchIterator mi)
1565 {
1566     if (mi && mi->mi_set) {
1567 	dbiIndexSetUniq(mi->mi_set, mi->mi_sorted);
1568     }
1569 }
1570 
rpmdbExtendIterator(rpmdbMatchIterator mi,const void * keyp,size_t keylen)1571 int rpmdbExtendIterator(rpmdbMatchIterator mi,
1572 			const void * keyp, size_t keylen)
1573 {
1574     dbiIndex dbi = NULL;
1575     dbiIndexSet set = NULL;
1576     int rc = 1; /* assume failure */
1577 
1578     if (mi == NULL || keyp == NULL)
1579 	return rc;
1580 
1581     rc = indexOpen(mi->mi_db, mi->mi_rpmtag, 0, &dbi);
1582 
1583     if (rc == 0 && indexGet(dbi, keyp, keylen, &set) == RPMRC_OK) {
1584 	if (mi->mi_set == NULL) {
1585 	    mi->mi_set = set;
1586 	} else {
1587 	    dbiIndexSetAppendSet(mi->mi_set, set, 0);
1588 	    dbiIndexSetFree(set);
1589 	}
1590 	mi->mi_sorted = 0;
1591 	rc = 0;
1592     }
1593 
1594     return rc;
1595 }
1596 
rpmdbFilterIterator(rpmdbMatchIterator mi,packageHash hdrNums,int neg)1597 int rpmdbFilterIterator(rpmdbMatchIterator mi, packageHash hdrNums, int neg)
1598 {
1599     if (mi == NULL || hdrNums == NULL)
1600 	return 1;
1601 
1602     if (!mi->mi_set)
1603 	return 0;
1604 
1605     if (packageHashNumKeys(hdrNums) == 0) {
1606 	if (!neg)
1607 	    mi->mi_set->count = 0;
1608 	return 0;
1609     }
1610 
1611     unsigned int from;
1612     unsigned int to = 0;
1613     unsigned int num = mi->mi_set->count;
1614     int cond;
1615 
1616     assert(mi->mi_set->count > 0);
1617 
1618     for (from = 0; from < num; from++) {
1619 	cond = !packageHashHasEntry(hdrNums, mi->mi_set->recs[from].hdrNum);
1620 	cond = neg ? !cond : cond;
1621 	if (cond) {
1622 	    mi->mi_set->count--;
1623 	    continue;
1624 	}
1625 	if (from != to)
1626 	    mi->mi_set->recs[to] = mi->mi_set->recs[from]; /* structure assignment */
1627 	to++;
1628     }
1629     return 0;
1630 }
1631 
rpmdbPruneIterator(rpmdbMatchIterator mi,packageHash hdrNums)1632 int rpmdbPruneIterator(rpmdbMatchIterator mi, packageHash hdrNums)
1633 {
1634     if (packageHashNumKeys(hdrNums) <= 0)
1635 	return 1;
1636 
1637     return rpmdbFilterIterator(mi, hdrNums, 1);
1638 }
1639 
1640 
rpmdbAppendIterator(rpmdbMatchIterator mi,const unsigned int * hdrNums,unsigned int nHdrNums)1641 int rpmdbAppendIterator(rpmdbMatchIterator mi,
1642 			const unsigned int * hdrNums, unsigned int nHdrNums)
1643 {
1644     if (mi == NULL || hdrNums == NULL || nHdrNums == 0)
1645 	return 1;
1646 
1647     if (mi->mi_set == NULL)
1648 	mi->mi_set = dbiIndexSetNew(nHdrNums);
1649 
1650     for (unsigned int i = 0; i < nHdrNums; i++)
1651 	dbiIndexSetAppendOne(mi->mi_set, hdrNums[i], 0, 0);
1652     mi->mi_sorted = 0;
1653     return 0;
1654 }
1655 
rpmdbNewIterator(rpmdb db,rpmDbiTagVal dbitag)1656 rpmdbMatchIterator rpmdbNewIterator(rpmdb db, rpmDbiTagVal dbitag)
1657 {
1658     rpmdbMatchIterator mi = NULL;
1659 
1660     if (dbitag == RPMDBI_PACKAGES) {
1661 	if (pkgdbOpen(db, 0, NULL))
1662 	    return NULL;
1663     } else {
1664 	if (indexOpen(db, dbitag, 0, NULL))
1665 	    return NULL;
1666     }
1667 
1668     mi = xcalloc(1, sizeof(*mi));
1669     mi->mi_set = NULL;
1670     mi->mi_db = rpmdbLink(db);
1671     mi->mi_rpmtag = dbitag;
1672 
1673     mi->mi_dbc = NULL;
1674     mi->mi_setx = 0;
1675     mi->mi_h = NULL;
1676     mi->mi_sorted = 0;
1677     mi->mi_cflags = 0;
1678     mi->mi_modified = 0;
1679     mi->mi_prevoffset = 0;
1680     mi->mi_offset = 0;
1681     mi->mi_filenum = 0;
1682     mi->mi_nre = 0;
1683     mi->mi_re = NULL;
1684 
1685     mi->mi_ts = NULL;
1686     mi->mi_hdrchk = NULL;
1687 
1688     /* Chain cursors for teardown on abnormal exit. */
1689     mi->mi_next = rpmmiRock;
1690     rpmmiRock = mi;
1691 
1692     return mi;
1693 };
1694 
pkgdbIterInit(rpmdb db,const unsigned int * keyp,size_t keylen)1695 static rpmdbMatchIterator pkgdbIterInit(rpmdb db,
1696 				    const unsigned int * keyp, size_t keylen)
1697 {
1698     rpmdbMatchIterator mi = NULL;
1699     rpmDbiTagVal dbtag = RPMDBI_PACKAGES;
1700     dbiIndex pkgs = NULL;
1701 
1702     /* Require a sane keylen if one is specified */
1703     if (keyp && keylen != sizeof(*keyp))
1704 	return NULL;
1705 
1706     if (pkgdbOpen(db, 0, &pkgs) == 0) {
1707 	mi = rpmdbNewIterator(db, dbtag);
1708 	if (keyp)
1709 	    rpmdbAppendIterator(mi, keyp, 1);
1710     }
1711     return mi;
1712 }
1713 
indexIterInit(rpmdb db,rpmDbiTagVal rpmtag,const void * keyp,size_t keylen)1714 static rpmdbMatchIterator indexIterInit(rpmdb db, rpmDbiTagVal rpmtag,
1715 				        const void * keyp, size_t keylen)
1716 {
1717     rpmdbMatchIterator mi = NULL;
1718     rpmDbiTagVal dbtag = rpmtag;
1719     dbiIndex dbi = NULL;
1720     dbiIndexSet set = NULL;
1721 
1722     /* Fixup the physical index for our pseudo indexes */
1723     if (rpmtag == RPMDBI_LABEL) {
1724 	dbtag = RPMDBI_NAME;
1725     } else if (rpmtag == RPMDBI_INSTFILENAMES) {
1726 	dbtag = RPMDBI_BASENAMES;
1727     }
1728 
1729     if (indexOpen(db, dbtag, 0, &dbi) == 0) {
1730 	int rc = 0;
1731 
1732         if (keyp) {
1733             if (rpmtag == RPMDBI_LABEL) {
1734                 rc = dbiFindByLabel(db, dbi, keyp, &set);
1735             } else if (rpmtag == RPMDBI_BASENAMES) {
1736                 rc = rpmdbFindByFile(db, dbi, keyp, 0, &set);
1737             } else if (rpmtag == RPMDBI_INSTFILENAMES) {
1738                 rc = rpmdbFindByFile(db, dbi, keyp, 1, &set);
1739             } else {
1740 		rc = indexGet(dbi, keyp, keylen, &set);
1741 	    }
1742 	} else {
1743             /* get all entries from index */
1744 	    rc = indexGet(dbi, NULL, 0, &set);
1745         }
1746 
1747 	if (rc)	{	/* error/not found */
1748 	    set = dbiIndexSetFree(set);
1749 	} else {
1750 	    mi = rpmdbNewIterator(db, dbtag);
1751 	    mi->mi_set = set;
1752 
1753 	    if (keyp) {
1754 		rpmdbSortIterator(mi);
1755 	    }
1756 	}
1757     }
1758 
1759     return mi;
1760 }
1761 
rpmdbInitIterator(rpmdb db,rpmDbiTagVal rpmtag,const void * keyp,size_t keylen)1762 rpmdbMatchIterator rpmdbInitIterator(rpmdb db, rpmDbiTagVal rpmtag,
1763 		const void * keyp, size_t keylen)
1764 {
1765     rpmdbMatchIterator mi = NULL;
1766 
1767     if (db != NULL) {
1768 	(void) rpmsqPoll();
1769 
1770 	if (rpmtag == RPMDBI_PACKAGES)
1771 	    mi = pkgdbIterInit(db, keyp, keylen);
1772 	else
1773 	    mi = indexIterInit(db, rpmtag, keyp, keylen);
1774     }
1775 
1776     return mi;
1777 }
1778 
rpmdbInitPrefixIterator(rpmdb db,rpmDbiTagVal rpmtag,const void * pfx,size_t plen)1779 rpmdbMatchIterator rpmdbInitPrefixIterator(rpmdb db, rpmDbiTagVal rpmtag,
1780 					    const void * pfx, size_t plen)
1781 {
1782     rpmdbMatchIterator mi = NULL;
1783     dbiIndexSet set = NULL;
1784     dbiIndex dbi = NULL;
1785     rpmDbiTagVal dbtag = rpmtag;
1786 
1787     if (!pfx)
1788 	return NULL;
1789 
1790     if (db != NULL && rpmtag != RPMDBI_PACKAGES) {
1791 	(void) rpmsqPoll();
1792 
1793 
1794 	if (indexOpen(db, dbtag, 0, &dbi) == 0) {
1795 	    int rc = 0;
1796 
1797 	    rc = indexPrefixGet(dbi, pfx, plen, &set);
1798 
1799 	    if (rc)	{
1800 		set = dbiIndexSetFree(set);
1801 	    } else {
1802 		mi = rpmdbNewIterator(db, dbtag);
1803 		mi->mi_set = set;
1804 		rpmdbSortIterator(mi);
1805 	    }
1806 	}
1807 
1808     }
1809 
1810     return mi;
1811 }
1812 
1813 /*
1814  * Convert current tag data to db key
1815  * @param tagdata	Tag data container
1816  * @retval keylen	Length of key
1817  * @return 		Pointer to key value or NULL to signal skip
1818  */
td2key(rpmtd tagdata,unsigned int * keylen)1819 static const void * td2key(rpmtd tagdata, unsigned int *keylen)
1820 {
1821     const void * data = NULL;
1822     unsigned int size = 0;
1823     const char *str = NULL;
1824 
1825     switch (rpmtdType(tagdata)) {
1826     case RPM_CHAR_TYPE:
1827     case RPM_INT8_TYPE:
1828 	size = sizeof(uint8_t);
1829 	data = rpmtdGetChar(tagdata);
1830 	break;
1831     case RPM_INT16_TYPE:
1832 	size = sizeof(uint16_t);
1833 	data = rpmtdGetUint16(tagdata);
1834 	break;
1835     case RPM_INT32_TYPE:
1836 	size = sizeof(uint32_t);
1837 	data = rpmtdGetUint32(tagdata);
1838 	break;
1839     case RPM_INT64_TYPE:
1840 	size = sizeof(uint64_t);
1841 	data = rpmtdGetUint64(tagdata);
1842 	break;
1843     case RPM_BIN_TYPE:
1844 	size = tagdata->count;
1845 	data = tagdata->data;
1846 	break;
1847     case RPM_STRING_TYPE:
1848     case RPM_I18NSTRING_TYPE:
1849     case RPM_STRING_ARRAY_TYPE:
1850 	str = rpmtdGetString(tagdata);
1851 	if (str) {
1852 	    size = strlen(str);
1853 	    data = str;
1854 	}
1855 	break;
1856     default:
1857 	break;
1858     }
1859 
1860     if (data && keylen)
1861 	*keylen = size;
1862 
1863     return data;
1864 }
1865 /*
1866  * rpmdbIndexIterator
1867  */
1868 
rpmdbIndexIteratorInit(rpmdb db,rpmDbiTag rpmtag)1869 rpmdbIndexIterator rpmdbIndexIteratorInit(rpmdb db, rpmDbiTag rpmtag)
1870 {
1871     rpmdbIndexIterator ii;
1872     dbiIndex dbi = NULL;
1873 
1874     if (db == NULL)
1875 	return NULL;
1876 
1877     (void) rpmsqPoll();
1878 
1879     if (indexOpen(db, rpmtag, 0, &dbi))
1880 	return NULL;
1881 
1882     /* Chain cursors for teardown on abnormal exit. */
1883     ii = xcalloc(1, sizeof(*ii));
1884     ii->ii_next = rpmiiRock;
1885     rpmiiRock = ii;
1886 
1887     ii->ii_db = rpmdbLink(db);
1888     ii->ii_rpmtag = rpmtag;
1889     ii->ii_dbi = dbi;
1890     ii->ii_set = NULL;
1891 
1892     return ii;
1893 }
1894 
rpmdbIndexKeyIteratorInit(rpmdb db,rpmDbiTag rpmtag)1895 rpmdbIndexIterator rpmdbIndexKeyIteratorInit(rpmdb db, rpmDbiTag rpmtag)
1896 {
1897     rpmdbIndexIterator ki = rpmdbIndexIteratorInit(db, rpmtag);
1898     ki->ii_skipdata = 1;
1899     return ki;
1900 }
1901 
rpmdbIndexIteratorNext(rpmdbIndexIterator ii,const void ** key,size_t * keylen)1902 int rpmdbIndexIteratorNext(rpmdbIndexIterator ii, const void ** key, size_t * keylen)
1903 {
1904     int rc;
1905     unsigned int iikeylen = 0; /* argh, size_t vs uint pointer... */
1906 
1907     if (ii == NULL)
1908 	return -1;
1909 
1910     if (ii->ii_dbc == NULL)
1911 	ii->ii_dbc = dbiCursorInit(ii->ii_dbi, DBC_READ);
1912 
1913     /* free old data */
1914     ii->ii_set = dbiIndexSetFree(ii->ii_set);
1915 
1916     rc = idxdbGet(ii->ii_dbi, ii->ii_dbc, NULL, 0,
1917 		ii->ii_skipdata ? NULL : &ii->ii_set, DBC_NORMAL_SEARCH);
1918 
1919     *key = idxdbKey(ii->ii_dbi, ii->ii_dbc, &iikeylen);
1920     *keylen = iikeylen;
1921 
1922     return (rc == RPMRC_OK) ? 0 : -1;
1923 }
1924 
rpmdbIndexIteratorNextTd(rpmdbIndexIterator ii,rpmtd keytd)1925 int rpmdbIndexIteratorNextTd(rpmdbIndexIterator ii, rpmtd keytd)
1926 {
1927     size_t keylen = 0;
1928     const void * keyp = NULL;
1929 
1930     int rc = rpmdbIndexIteratorNext(ii, &keyp, &keylen);
1931 
1932     if (rc == 0) {
1933 	rpmTagVal tag = ii->ii_rpmtag;
1934 	rpmTagClass tagclass = rpmTagGetClass(tag);
1935 
1936 	/* Set the common values, overridden below as necessary */
1937 	keytd->type = rpmTagGetTagType(tag);
1938 	keytd->tag = tag;
1939 	keytd->flags = RPMTD_ALLOCED;
1940 	keytd->count = 1;
1941 
1942 	switch (tagclass) {
1943 	case RPM_STRING_CLASS: {
1944 	    /*
1945 	     * XXX: We never return arrays here, so everything is a
1946 	     * "simple" string. However this can disagree with the
1947 	     * type of the index tag, eg requires are string arrays.
1948 	     */
1949 	    char *key = memcpy(xmalloc(keylen + 1), keyp, keylen);
1950 	    key[keylen] = '\0';
1951 	    keytd->data = key;
1952 	    keytd->type = RPM_STRING_TYPE;
1953 	    } break;
1954 	case RPM_BINARY_CLASS:
1955 	    /* Binary types abuse count for data length */
1956 	    keytd->count = keylen;
1957 	    /* fallthrough */
1958 	case RPM_NUMERIC_CLASS:
1959 	    keytd->data = memcpy(xmalloc(keylen), keyp, keylen);
1960 	    break;
1961 	default:
1962 	    rpmtdReset(keytd);
1963 	    rc = -1;
1964 	    break;
1965 	}
1966     }
1967 
1968     return rc;
1969 }
1970 
rpmdbIndexIteratorNumPkgs(rpmdbIndexIterator ii)1971 unsigned int rpmdbIndexIteratorNumPkgs(rpmdbIndexIterator ii)
1972 {
1973     return (ii && ii->ii_set) ? dbiIndexSetCount(ii->ii_set) : 0;
1974 }
1975 
rpmdbIndexIteratorPkgOffset(rpmdbIndexIterator ii,unsigned int nr)1976 unsigned int rpmdbIndexIteratorPkgOffset(rpmdbIndexIterator ii, unsigned int nr)
1977 {
1978     if (!ii || !ii->ii_set)
1979         return 0;
1980     if (dbiIndexSetCount(ii->ii_set) <= nr)
1981         return 0;
1982     return dbiIndexRecordOffset(ii->ii_set, nr);
1983 }
1984 
rpmdbIndexIteratorPkgOffsets(rpmdbIndexIterator ii)1985 const unsigned int *rpmdbIndexIteratorPkgOffsets(rpmdbIndexIterator ii)
1986 {
1987     int i;
1988 
1989     if (!ii || !ii->ii_set)
1990 	return NULL;
1991 
1992     if (ii->ii_hdrNums)
1993 	ii->ii_hdrNums = _free(ii->ii_hdrNums);
1994 
1995     ii->ii_hdrNums = xmalloc(sizeof(*ii->ii_hdrNums) * ii->ii_set->count);
1996     for (i = 0; i < ii->ii_set->count; i++) {
1997 	ii->ii_hdrNums[i] = ii->ii_set->recs[i].hdrNum;
1998     }
1999 
2000     return ii->ii_hdrNums;
2001 }
2002 
rpmdbIndexIteratorTagNum(rpmdbIndexIterator ii,unsigned int nr)2003 unsigned int rpmdbIndexIteratorTagNum(rpmdbIndexIterator ii, unsigned int nr)
2004 {
2005     if (!ii || !ii->ii_set)
2006         return 0;
2007     if (dbiIndexSetCount(ii->ii_set) <= nr)
2008         return 0;
2009     return dbiIndexRecordFileNumber(ii->ii_set, nr);
2010 }
2011 
rpmdbIndexIteratorFree(rpmdbIndexIterator ii)2012 rpmdbIndexIterator rpmdbIndexIteratorFree(rpmdbIndexIterator ii)
2013 {
2014     rpmdbIndexIterator * prev, next;
2015 
2016     if (ii == NULL)
2017         return NULL;
2018 
2019     prev = &rpmiiRock;
2020     while ((next = *prev) != NULL && next != ii)
2021         prev = &next->ii_next;
2022     if (next) {
2023         *prev = next->ii_next;
2024         next->ii_next = NULL;
2025     } else
2026 	return NULL;
2027 
2028     ii->ii_dbc = dbiCursorFree(ii->ii_dbi, ii->ii_dbc);
2029     ii->ii_dbi = NULL;
2030     rpmdbClose(ii->ii_db);
2031     ii->ii_set = dbiIndexSetFree(ii->ii_set);
2032 
2033     if (ii->ii_hdrNums)
2034 	ii->ii_hdrNums = _free(ii->ii_hdrNums);
2035 
2036     ii = _free(ii);
2037     return NULL;
2038 }
2039 
logAddRemove(const char * dbiname,int removing,rpmtd tagdata)2040 static void logAddRemove(const char *dbiname, int removing, rpmtd tagdata)
2041 {
2042     rpm_count_t c = rpmtdCount(tagdata);
2043     if (c == 1 && rpmtdType(tagdata) == RPM_STRING_TYPE) {
2044 	rpmlog(RPMLOG_DEBUG, "%s \"%s\" %s %s index.\n",
2045 		removing ? "removing" : "adding", rpmtdGetString(tagdata),
2046 		removing ? "from" : "to", dbiname);
2047     } else if (c > 0) {
2048 	rpmlog(RPMLOG_DEBUG, "%s %d entries %s %s index.\n",
2049 		removing ? "removing" : "adding", c,
2050 		removing ? "from" : "to", dbiname);
2051     }
2052 }
2053 
rpmdbRemove(rpmdb db,unsigned int hdrNum)2054 int rpmdbRemove(rpmdb db, unsigned int hdrNum)
2055 {
2056     dbiIndex dbi = NULL;
2057     dbiCursor dbc = NULL;
2058     Header h;
2059     int ret = 0;
2060 
2061     if (db == NULL)
2062 	return 0;
2063 
2064     h = rpmdbGetHeaderAt(db, hdrNum);
2065 
2066     if (h == NULL) {
2067 	rpmlog(RPMLOG_ERR, _("%s: cannot read header at 0x%x\n"),
2068 	      "rpmdbRemove", hdrNum);
2069 	return 1;
2070     } else {
2071 	char *nevra = headerGetAsString(h, RPMTAG_NEVRA);
2072 	rpmlog(RPMLOG_DEBUG, "  --- h#%8u %s\n", hdrNum, nevra);
2073 	free(nevra);
2074     }
2075 
2076     if (pkgdbOpen(db, 0, &dbi))
2077 	return 1;
2078 
2079     rpmsqBlock(SIG_BLOCK);
2080     dbCtrl(db, DB_CTRL_LOCK_RW);
2081 
2082     /* Remove header from primary index */
2083     dbc = dbiCursorInit(dbi, DBC_WRITE);
2084     ret = pkgdbDel(dbi, dbc, hdrNum);
2085     dbiCursorFree(dbi, dbc);
2086 
2087     /* Remove associated data from secondary indexes */
2088     if (ret == 0) {
2089 	for (int dbix = 0; dbix < db->db_ndbi; dbix++) {
2090 	    rpmDbiTag rpmtag = db->db_tags[dbix];
2091 
2092 	    if (indexOpen(db, rpmtag, 0, &dbi))
2093 		continue;
2094 
2095 	    ret += idxdbDel(dbi, rpmtag, hdrNum, h);
2096 	}
2097     }
2098 
2099     dbCtrl(db, DB_CTRL_INDEXSYNC);
2100     dbCtrl(db, DB_CTRL_UNLOCK_RW);
2101     rpmsqBlock(SIG_UNBLOCK);
2102 
2103     headerFree(h);
2104 
2105     /* XXX return ret; */
2106     return 0;
2107 }
2108 
2109 struct updateRichDepData {
2110     ARGV_t argv;
2111     int nargv;
2112     int neg;
2113     int level;
2114     int *nargv_level;
2115 };
2116 
updateRichDepCB(void * cbdata,rpmrichParseType type,const char * n,int nl,const char * e,int el,rpmsenseFlags sense,rpmrichOp op,char ** emsg)2117 static rpmRC updateRichDepCB(void *cbdata, rpmrichParseType type,
2118 		const char *n, int nl, const char *e, int el, rpmsenseFlags sense,
2119 		rpmrichOp op, char **emsg) {
2120     struct updateRichDepData *data = cbdata;
2121     if (type == RPMRICH_PARSE_ENTER) {
2122 	data->level++;
2123 	data->nargv_level = xrealloc(data->nargv_level, data->level * (sizeof(int)));
2124 	data->nargv_level[data->level - 1] = data->nargv;
2125     }
2126     if (type == RPMRICH_PARSE_LEAVE) {
2127 	data->level--;
2128     }
2129     if (type == RPMRICH_PARSE_SIMPLE && nl && !(nl > 7 && !strncmp(n, "rpmlib(", 7))) {
2130 	char *name = xmalloc(nl + 2);
2131 	*name = data->neg ? '!' : ' ';
2132 	strncpy(name + 1, n, nl);
2133 	name[1 + nl] = 0;
2134 	argvAdd(&data->argv, name);
2135 	data->nargv++;
2136 	_free(name);
2137     }
2138     if (type == RPMRICH_PARSE_OP && (op == RPMRICHOP_IF || op == RPMRICHOP_UNLESS)) {
2139 	/* save nargv in case of ELSE */
2140 	data->nargv_level[data->level - 1] = data->nargv;
2141 	data->neg ^= 1;
2142     }
2143     if (type == RPMRICH_PARSE_OP && op == RPMRICHOP_ELSE) {
2144 	int i, nargv = data->nargv;
2145 	/* copy and invert condition block */
2146 	for (i = data->nargv_level[data->level - 1]; i < nargv; i++) {
2147 	    char *name = data->argv[i];
2148 	    *name ^= ' ' ^ '!';
2149 	    argvAdd(&data->argv, name);
2150 	    *name ^= ' ' ^ '!';
2151 	    data->nargv++;
2152 	}
2153 	data->neg ^= 1;
2154     }
2155     if (type == RPMRICH_PARSE_LEAVE && (op == RPMRICHOP_IF || op == RPMRICHOP_UNLESS)) {
2156 	data->neg ^= 1;
2157     }
2158     return RPMRC_OK;
2159 }
2160 
updateRichDep(dbiIndex dbi,dbiCursor dbc,const char * str,struct dbiIndexItem_s * rec,idxfunc idxupdate)2161 static rpmRC updateRichDep(dbiIndex dbi, dbiCursor dbc, const char *str,
2162                            struct dbiIndexItem_s *rec,
2163                            idxfunc idxupdate)
2164 {
2165     int n, i, rc = 0;
2166     struct updateRichDepData data;
2167 
2168     data.argv = argvNew();
2169     data.neg = 0;
2170     data.nargv = 0;
2171     data.level = 0;
2172     data.nargv_level = xcalloc(1, sizeof(int));
2173     if (rpmrichParse(&str, NULL, updateRichDepCB, &data) == RPMRC_OK) {
2174 	n = argvCount(data.argv);
2175 	if (n) {
2176 	    argvSort(data.argv, NULL);
2177 	    for (i = 0; i < n; i++) {
2178 		char *name = data.argv[i];
2179 		if (i && !strcmp(data.argv[i - 1], name))
2180 		    continue;       /* ignore dups */
2181 		if (*name == ' ')
2182 		    name++;
2183 		rc += idxupdate(dbi, dbc, name, strlen(name), rec);
2184 	    }
2185 	}
2186     }
2187     _free(data.nargv_level);
2188     argvFree(data.argv);
2189     return rc;
2190 }
2191 
tag2index(dbiIndex dbi,rpmTagVal rpmtag,unsigned int hdrNum,Header h,idxfunc idxupdate)2192 rpmRC tag2index(dbiIndex dbi, rpmTagVal rpmtag,
2193 		       unsigned int hdrNum, Header h,
2194 		       idxfunc idxupdate)
2195 {
2196     int i, rc = 0;
2197     struct rpmtd_s tagdata, reqflags, trig_index;
2198     dbiCursor dbc = NULL;
2199 
2200     switch (rpmtag) {
2201     case RPMTAG_REQUIRENAME:
2202 	headerGet(h, RPMTAG_REQUIREFLAGS, &reqflags, HEADERGET_MINMEM);
2203 	break;
2204     case RPMTAG_FILETRIGGERNAME:
2205 	headerGet(h, RPMTAG_FILETRIGGERINDEX, &trig_index, HEADERGET_MINMEM);
2206 	break;
2207     case RPMTAG_TRANSFILETRIGGERNAME:
2208 	headerGet(h, RPMTAG_TRANSFILETRIGGERINDEX, &trig_index, HEADERGET_MINMEM);
2209 	break;
2210     }
2211     headerGet(h, rpmtag, &tagdata, HEADERGET_MINMEM);
2212 
2213     if (rpmtdCount(&tagdata) == 0) {
2214 	if (rpmtag != RPMTAG_GROUP)
2215 	    goto exit;
2216 
2217 	/* XXX preserve legacy behavior */
2218 	tagdata.type = RPM_STRING_TYPE;
2219 	tagdata.data = (const char **) "Unknown";
2220 	tagdata.count = 1;
2221     }
2222 
2223     dbc = dbiCursorInit(dbi, DBC_WRITE);
2224 
2225     logAddRemove(dbiName(dbi), 0, &tagdata);
2226     while ((i = rpmtdNext(&tagdata)) >= 0) {
2227 	const void * key = NULL;
2228 	unsigned int keylen = 0;
2229 	int j;
2230 	struct dbiIndexItem_s rec;
2231 
2232 	switch (rpmtag) {
2233 	/* Include trigger index in db index for triggers */
2234 	case RPMTAG_FILETRIGGERNAME:
2235 	case RPMTAG_TRANSFILETRIGGERNAME:
2236 	    rec.hdrNum = hdrNum;
2237 	    rec.tagNum = *rpmtdNextUint32(&trig_index);
2238 	    break;
2239 
2240 	/* Include the tagNum in the others indices (only files use though) */
2241 	default:
2242 	    rec.hdrNum = hdrNum;
2243 	    rec.tagNum = i;
2244 	    break;
2245 	}
2246 
2247 	switch (rpmtag) {
2248 	case RPMTAG_REQUIRENAME: {
2249 	    /* Filter out install prerequisites. */
2250 	    rpm_flag_t *rflag = rpmtdNextUint32(&reqflags);
2251 	    if (rflag && isTransientReq(*rflag))
2252 		continue;
2253 	    break;
2254 	    }
2255 	case RPMTAG_TRIGGERNAME:
2256 	    if (i > 0) {	/* don't add duplicates */
2257 		const char **tnames = tagdata.data;
2258 		const char *str = rpmtdGetString(&tagdata);
2259 		for (j = 0; j < i; j++) {
2260 		    if (rstreq(str, tnames[j]))
2261 			break;
2262 		}
2263 		if (j < i)
2264 		    continue;
2265 	    }
2266 	    break;
2267 	default:
2268 	    break;
2269 	}
2270 
2271 	if ((key = td2key(&tagdata, &keylen)) == NULL)
2272 	    continue;
2273 
2274 	rc += idxupdate(dbi, dbc, key, keylen, &rec);
2275 
2276 	if (*(char *)key == '(') {
2277 	    switch (rpmtag) {
2278 	    case RPMTAG_REQUIRENAME:
2279 	    case RPMTAG_CONFLICTNAME:
2280 	    case RPMTAG_SUGGESTNAME:
2281 	    case RPMTAG_SUPPLEMENTNAME:
2282 	    case RPMTAG_RECOMMENDNAME:
2283 	    case RPMTAG_ENHANCENAME:
2284 		if (rpmtdType(&tagdata) == RPM_STRING_ARRAY_TYPE) {
2285 		    rc += updateRichDep(dbi, dbc, rpmtdGetString(&tagdata),
2286 			&rec, idxupdate);
2287 		}
2288 	    default:
2289 		break;
2290 	    }
2291 	}
2292     }
2293 
2294     dbiCursorFree(dbi, dbc);
2295 
2296 exit:
2297     rpmtdFreeData(&tagdata);
2298     return (rc == 0) ? RPMRC_OK : RPMRC_FAIL;
2299 }
2300 
rpmdbAdd(rpmdb db,Header h)2301 int rpmdbAdd(rpmdb db, Header h)
2302 {
2303     dbiIndex dbi = NULL;
2304     dbiCursor dbc = NULL;
2305     unsigned int hdrNum = 0;
2306     unsigned int hdrLen = 0;
2307     unsigned char *hdrBlob = NULL;
2308     int ret = 0;
2309 
2310     if (db == NULL)
2311 	return 0;
2312 
2313     hdrBlob = headerExport(h, &hdrLen);
2314     if (hdrBlob == NULL || hdrLen == 0) {
2315 	ret = -1;
2316 	goto exit;
2317     }
2318 
2319     ret = pkgdbOpen(db, 0, &dbi);
2320     if (ret)
2321 	goto exit;
2322 
2323     rpmsqBlock(SIG_BLOCK);
2324     dbCtrl(db, DB_CTRL_LOCK_RW);
2325 
2326     /* Add header to primary index */
2327     dbc = dbiCursorInit(dbi, DBC_WRITE);
2328     ret = pkgdbPut(dbi, dbc, &hdrNum, hdrBlob, hdrLen);
2329     dbiCursorFree(dbi, dbc);
2330 
2331     /* Add associated data to secondary indexes */
2332     if (ret == 0) {
2333 	for (int dbix = 0; dbix < db->db_ndbi; dbix++) {
2334 	    rpmDbiTag rpmtag = db->db_tags[dbix];
2335 
2336 	    if (indexOpen(db, rpmtag, 0, &dbi))
2337 		continue;
2338 
2339 	    ret += idxdbPut(dbi, rpmtag, hdrNum, h);
2340 	}
2341     }
2342 
2343     dbCtrl(db, DB_CTRL_INDEXSYNC);
2344     dbCtrl(db, DB_CTRL_UNLOCK_RW);
2345     rpmsqBlock(SIG_UNBLOCK);
2346 
2347     /* If everything ok, mark header as installed now */
2348     if (ret == 0) {
2349 	headerSetInstance(h, hdrNum);
2350 	/* Purge our verification cache on added public keys */
2351 	if (db->db_checked && headerIsEntry(h, RPMTAG_PUBKEYS)) {
2352 	    dbChkEmpty(db->db_checked);
2353 	}
2354     }
2355 
2356 exit:
2357     free(hdrBlob);
2358 
2359     return ret;
2360 }
2361 
rpmdbRemoveFiles(char * pattern)2362 static int rpmdbRemoveFiles(char * pattern)
2363 {
2364     int rc = 0;
2365     ARGV_t paths = NULL, p;
2366 
2367     if (rpmGlob(pattern, NULL, &paths) == 0) {
2368 	for (p = paths; *p; p++) {
2369 	    rc += unlink(*p);
2370 	}
2371 	argvFree(paths);
2372     }
2373     return rc;
2374 }
2375 
rpmdbRemoveDatabase(const char * dbpath)2376 static int rpmdbRemoveDatabase(const char *dbpath)
2377 {
2378     int rc = 0;
2379     char *pattern;
2380 
2381     pattern = rpmGetPath(dbpath, "/*", NULL);
2382     rc += rpmdbRemoveFiles(pattern);
2383     free(pattern);
2384     pattern = rpmGetPath(dbpath, "/.??*", NULL);
2385     rc += rpmdbRemoveFiles(pattern);
2386     free(pattern);
2387 
2388     rc += rmdir(dbpath);
2389     return rc;
2390 }
2391 
rpmdbMoveDatabase(const char * prefix,const char * srcdbpath,const char * dbpath,const char * tmppath)2392 static int rpmdbMoveDatabase(const char * prefix, const char * srcdbpath,
2393 			     const char * dbpath, const char * tmppath)
2394 {
2395     int rc = -1;
2396     int xx;
2397     char *src = rpmGetPath(prefix, "/", srcdbpath, NULL);
2398     char *old = rpmGetPath(prefix, "/", tmppath, NULL);
2399     char *dest = rpmGetPath(prefix, "/", dbpath, NULL);
2400 
2401     char * oldkeys = rpmGetPath(old, "/", "pubkeys", NULL);
2402     char * destkeys = rpmGetPath(dest, "/", "pubkeys", NULL);
2403 
2404     xx = rename(dest, old);
2405     if (xx) {
2406 	goto exit;
2407     }
2408     xx = rename(src, dest);
2409     if (xx) {
2410 	rpmlog(RPMLOG_ERR, _("could not move new database in place\n"));
2411 	xx = rename(old, dest);
2412 	if (xx) {
2413 	    rpmlog(RPMLOG_ERR, _("could also not restore old database from %s\n"),
2414 		   old);
2415 	    rpmlog(RPMLOG_ERR, _("replace files in %s with files from %s "
2416 				 "to recover\n"), dest, old);
2417 	}
2418 	goto exit;
2419     }
2420 
2421     if (access(oldkeys, F_OK ) != -1) {
2422 	xx = rename(oldkeys, destkeys);
2423 	if (xx) {
2424 	    rpmlog(RPMLOG_ERR, _("Could not get public keys from %s\n"), oldkeys);
2425 	    goto exit;
2426 	}
2427     }
2428 
2429     xx = rpmdbRemoveDatabase(old);
2430     if (xx) {
2431 	rpmlog(RPMLOG_ERR, _("could not delete old database at %s\n"), old);
2432     }
2433 
2434     rc = 0;
2435 
2436  exit:
2437     _free(src);
2438     _free(old);
2439     _free(dest);
2440     _free(oldkeys);
2441     _free(destkeys);
2442     return rc;
2443 }
2444 
rpmdbSetPermissions(char * src,char * dest)2445 static int rpmdbSetPermissions(char * src, char * dest)
2446 {
2447     struct dirent *dp;
2448     DIR *dfd;
2449 
2450     struct stat st;
2451     int xx, rc = -1;
2452     char * filepath;
2453 
2454     if (stat(dest, &st) < 0)
2455 	    goto exit;
2456     if (stat(src, &st) < 0)
2457 	    goto exit;
2458 
2459     if ((dfd = opendir(dest)) == NULL) {
2460 	goto exit;
2461     }
2462 
2463     rc = 0;
2464     while ((dp = readdir(dfd)) != NULL) {
2465 	if (!strcmp(dp->d_name, "..")) {
2466 	    continue;
2467 	}
2468 	filepath = rpmGetPath(dest, "/", dp->d_name, NULL);
2469 	xx = chown(filepath, st.st_uid, st.st_gid);
2470 	rc += xx;
2471 	if (!strcmp(dp->d_name, ".")) {
2472 	    xx = chmod(filepath, (st.st_mode & 07777));
2473 	} else {
2474 	    xx = chmod(filepath, (st.st_mode & 07666));
2475 	}
2476 	rc += xx;
2477 	_free(filepath);
2478     }
2479     closedir(dfd);
2480 
2481  exit:
2482     return rc;
2483 }
2484 
rpmdbRebuild(const char * prefix,rpmts ts,rpmRC (* hdrchk)(rpmts ts,const void * uh,size_t uc,char ** msg),int rebuildflags)2485 int rpmdbRebuild(const char * prefix, rpmts ts,
2486 		rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg),
2487 		int rebuildflags)
2488 {
2489     rpmdb olddb;
2490     char * dbpath = NULL;
2491     char * rootdbpath = NULL;
2492     char * tmppath = NULL;
2493     rpmdb newdb;
2494     char * newdbpath = NULL;
2495     char * newrootdbpath = NULL;
2496     int nocleanup = 1;
2497     int failed = 0;
2498     int rc = 0;
2499 
2500     dbpath = rpmGetPath("%{?_dbpath}", NULL);
2501     if (rstreq(dbpath, "")) {
2502 	rpmlog(RPMLOG_ERR, _("no dbpath has been set"));
2503 	rc = 1;
2504 	goto exit;
2505     }
2506     rootdbpath = rpmGetPath(prefix, dbpath, NULL);
2507 
2508     newdbpath = rpmGetPath("%{?_dbpath_rebuild}", NULL);
2509     if (rstreq(newdbpath, "") || rstreq(newdbpath, dbpath)) {
2510 	newdbpath = _free(newdbpath);
2511 	rasprintf(&newdbpath, "%srebuilddb.%d", dbpath, (int) getpid());
2512 	nocleanup = 0;
2513     }
2514     newrootdbpath = rpmGetPath(prefix, newdbpath, NULL);
2515 
2516     rpmlog(RPMLOG_DEBUG, "rebuilding database %s into %s\n",
2517 	rootdbpath, newrootdbpath);
2518 
2519     if (mkdir(newrootdbpath, 0755)) {
2520 	rpmlog(RPMLOG_ERR, _("failed to create directory %s: %s\n"),
2521 	      newrootdbpath, strerror(errno));
2522 	rc = 1;
2523 	goto exit;
2524     }
2525 
2526     if (openDatabase(prefix, dbpath, &olddb,
2527 		     O_RDONLY, 0644, RPMDB_FLAG_REBUILD |
2528 		     (rebuildflags & RPMDB_REBUILD_FLAG_SALVAGE ?
2529 		         RPMDB_FLAG_SALVAGE : 0))) {
2530 	rc = 1;
2531 	goto exit;
2532     }
2533     if (openDatabase(prefix, newdbpath, &newdb,
2534 		     (O_RDWR | O_CREAT), 0644, RPMDB_FLAG_REBUILD)) {
2535 	rc = 1;
2536 	goto exit;
2537     }
2538 
2539     {	Header h = NULL;
2540 	rpmdbMatchIterator mi;
2541 
2542 	mi = rpmdbInitIterator(olddb, RPMDBI_PACKAGES, NULL, 0);
2543 	if (ts && hdrchk)
2544 	    (void) rpmdbSetHdrChk(mi, ts, hdrchk);
2545 
2546 	while ((h = rpmdbNextIterator(mi)) != NULL) {
2547 
2548 	    /* let's sanity check this record a bit, otherwise just skip it */
2549 	    if (!(headerIsEntry(h, RPMTAG_NAME) &&
2550 		headerIsEntry(h, RPMTAG_VERSION) &&
2551 		headerIsEntry(h, RPMTAG_RELEASE) &&
2552 		headerIsEntry(h, RPMTAG_BUILDTIME)))
2553 	    {
2554 		rpmlog(RPMLOG_ERR,
2555 			_("header #%u in the database is bad -- skipping.\n"),
2556 			rpmdbGetIteratorOffset(mi));
2557 		continue;
2558 	    }
2559 
2560 	    /* Deleted entries are eliminated in legacy headers by copy. */
2561 	    if (headerIsEntry(h, RPMTAG_HEADERIMAGE)) {
2562 		Header nh = headerReload(headerCopy(h), RPMTAG_HEADERIMAGE);
2563 		rc = rpmdbAdd(newdb, nh);
2564 		headerFree(nh);
2565 	    } else {
2566 		rc = rpmdbAdd(newdb, h);
2567 	    }
2568 
2569 	    if (rc) {
2570 		rpmlog(RPMLOG_ERR, _("cannot add record originally at %u\n"),
2571 		       rpmdbGetIteratorOffset(mi));
2572 		failed = 1;
2573 		break;
2574 	    }
2575 	}
2576 
2577 	rpmdbFreeIterator(mi);
2578 
2579     }
2580 
2581     rpmdbClose(olddb);
2582     dbCtrl(newdb, DB_CTRL_INDEXSYNC);
2583     rpmdbClose(newdb);
2584 
2585     if (failed) {
2586 	rpmlog(RPMLOG_WARNING,
2587 		_("failed to rebuild database: original database "
2588 		"remains in place\n"));
2589 
2590 	rpmdbRemoveDatabase(newrootdbpath);
2591 	rc = 1;
2592 	goto exit;
2593     } else {
2594 	rpmdbSetPermissions(dbpath, newdbpath);
2595     }
2596 
2597     if (!nocleanup) {
2598 	rasprintf(&tmppath, "%sold.%d", dbpath, (int) getpid());
2599 	if (rpmdbMoveDatabase(prefix, newdbpath, dbpath, tmppath)) {
2600 	    rpmlog(RPMLOG_ERR, _("failed to replace old database with new "
2601 			"database!\n"));
2602 	    rpmlog(RPMLOG_ERR, _("replace files in %s with files from %s "
2603 			"to recover\n"), dbpath, newdbpath);
2604 	    rc = 1;
2605 	    goto exit;
2606 	}
2607     }
2608     rc = 0;
2609 
2610 exit:
2611     free(newdbpath);
2612     free(dbpath);
2613     free(tmppath);
2614     free(newrootdbpath);
2615     free(rootdbpath);
2616 
2617     return rc;
2618 }
2619 
rpmdbCtrl(rpmdb db,rpmdbCtrlOp ctrl)2620 int rpmdbCtrl(rpmdb db, rpmdbCtrlOp ctrl)
2621 {
2622     dbCtrlOp dbctrl = 0;
2623     switch (ctrl) {
2624     case RPMDB_CTRL_LOCK_RO:
2625 	dbctrl = DB_CTRL_LOCK_RO;
2626 	break;
2627     case RPMDB_CTRL_UNLOCK_RO:
2628 	dbctrl = DB_CTRL_UNLOCK_RO;
2629 	break;
2630     case RPMDB_CTRL_LOCK_RW:
2631 	dbctrl = DB_CTRL_LOCK_RW;
2632 	break;
2633     case RPMDB_CTRL_UNLOCK_RW:
2634 	dbctrl = DB_CTRL_UNLOCK_RW;
2635 	break;
2636     case RPMDB_CTRL_INDEXSYNC:
2637 	dbctrl = DB_CTRL_INDEXSYNC;
2638 	break;
2639     }
2640     return dbctrl ? dbCtrl(db, dbctrl) : 1;
2641 }
2642 
rpmdbCookie(rpmdb db)2643 char *rpmdbCookie(rpmdb db)
2644 {
2645     void *cookie = NULL;
2646     rpmdbIndexIterator ii = rpmdbIndexIteratorInit(db, RPMDBI_NAME);
2647 
2648     if (ii) {
2649 	DIGEST_CTX ctx = rpmDigestInit(PGPHASHALGO_SHA1, RPMDIGEST_NONE);
2650 	const void *key = 0;
2651 	size_t keylen = 0;
2652 	while ((rpmdbIndexIteratorNext(ii, &key, &keylen)) == 0) {
2653 	    const unsigned int *offsets = rpmdbIndexIteratorPkgOffsets(ii);
2654 	    unsigned int npkgs = rpmdbIndexIteratorNumPkgs(ii);
2655 	    rpmDigestUpdate(ctx, key, keylen);
2656 	    rpmDigestUpdate(ctx, offsets, sizeof(*offsets) * npkgs);
2657 	}
2658 	rpmDigestFinal(ctx, &cookie, NULL, 1);
2659     }
2660     rpmdbIndexIteratorFree(ii);
2661     return cookie;
2662 }
2663 
rpmdbFStat(rpmdb db,struct stat * statbuf)2664 int rpmdbFStat(rpmdb db, struct stat *statbuf)
2665 {
2666     int rc = -1;
2667     if (db) {
2668 	const char *dbfile = db->db_ops->path;
2669 	if (dbfile) {
2670 	    char *path = rpmGenPath(rpmdbHome(db), dbfile, NULL);
2671 	    rc = stat(path, statbuf);
2672 	    free(path);
2673 	}
2674     }
2675     return rc;
2676 }
2677 
rpmdbStat(const char * prefix,struct stat * statbuf)2678 int rpmdbStat(const char *prefix, struct stat *statbuf)
2679 {
2680     rpmdb db = NULL;
2681     int flags = RPMDB_FLAG_VERIFYONLY;
2682     int rc = -1;
2683 
2684     if (openDatabase(prefix, NULL, &db, O_RDONLY, 0644, flags) == 0) {
2685 	rc = rpmdbFStat(db, statbuf);
2686 	rpmdbClose(db);
2687     }
2688     return rc;
2689 }
2690