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