1 /** \ingroup rpmdep
2 * \file lib/rpmts.c
3 * Routine(s) to handle a "rpmts" transaction sets.
4 */
5 #include "system.h"
6
7 #include <inttypes.h>
8 #include <libgen.h>
9 #include <fcntl.h>
10
11 #include <rpm/rpmtypes.h>
12 #include <rpm/rpmlib.h> /* rpmReadPackage etc */
13 #include <rpm/rpmmacro.h>
14 #include <rpm/rpmfileutil.h> /* rpmtsOpenDB() needs rpmGetPath */
15 #include <rpm/rpmstring.h>
16 #include <rpm/rpmkeyring.h>
17 #include <rpm/rpmbase64.h>
18
19 #include <rpm/rpmdb.h>
20 #include <rpm/rpmds.h>
21 #include <rpm/rpmfi.h>
22 #include <rpm/rpmlog.h>
23 #include <rpm/rpmsq.h>
24 #include <rpm/rpmte.h>
25
26 #include "rpmio/digest.h"
27 #include "lib/rpmal.h"
28 #include "lib/rpmchroot.h"
29 #include "lib/rpmplugins.h"
30 #include "lib/rpmts_internal.h"
31 #include "lib/rpmte_internal.h"
32 #include "lib/misc.h"
33 #include "lib/rpmtriggers.h"
34
35 #include "debug.h"
36
37 /**
38 * Iterator across transaction elements, forward on install, backward on erase.
39 */
40 struct rpmtsi_s {
41 rpmts ts; /*!< transaction set. */
42 int oc; /*!< iterator index. */
43 };
44
45 struct rpmtxn_s {
46 rpmlock lock; /* transaction lock */
47 rpmtxnFlags flags; /* transaction flags */
48 rpmts ts; /* parent transaction set reference */
49 };
50
51 static void loadKeyring(rpmts ts);
52
53 int _rpmts_stats = 0;
54
rpmtsUnlink(rpmts ts)55 static rpmts rpmtsUnlink(rpmts ts)
56 {
57 if (ts)
58 ts->nrefs--;
59 return NULL;
60 }
61
rpmtsLink(rpmts ts)62 rpmts rpmtsLink(rpmts ts)
63 {
64 if (ts)
65 ts->nrefs++;
66 return ts;
67 }
68
rpmtsCloseDB(rpmts ts)69 int rpmtsCloseDB(rpmts ts)
70 {
71 int rc = 0;
72
73 if (ts->rdb != NULL) {
74 (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBGET),
75 rpmdbOp(ts->rdb, RPMDB_OP_DBGET));
76 (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBPUT),
77 rpmdbOp(ts->rdb, RPMDB_OP_DBPUT));
78 (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBDEL),
79 rpmdbOp(ts->rdb, RPMDB_OP_DBDEL));
80 rc = rpmdbClose(ts->rdb);
81 ts->rdb = NULL;
82 }
83 return rc;
84 }
85
rpmtsOpenDB(rpmts ts,int dbmode)86 int rpmtsOpenDB(rpmts ts, int dbmode)
87 {
88 int rc = 0;
89
90 if (ts->rdb != NULL && ts->dbmode == dbmode)
91 return 0;
92
93 (void) rpmtsCloseDB(ts);
94
95 /* XXX there's a potential db lock race here. */
96
97 ts->dbmode = dbmode;
98 rc = rpmdbOpen(ts->rootDir, &ts->rdb, ts->dbmode, 0644);
99 if (rc) {
100 char * dn = rpmGetPath(ts->rootDir, "%{_dbpath}", NULL);
101 rpmlog(RPMLOG_ERR, _("cannot open Packages database in %s\n"), dn);
102 free(dn);
103 }
104 return rc;
105 }
106
rpmtsInitDB(rpmts ts,int dbmode)107 int rpmtsInitDB(rpmts ts, int dbmode)
108 {
109 rpmtxn txn = rpmtxnBegin(ts, RPMTXN_WRITE);
110 int rc = -1;
111 if (txn)
112 rc = rpmdbInit(ts->rootDir, dbmode);
113 rpmtxnEnd(txn);
114 return rc;
115 }
116
rpmtsGetDBMode(rpmts ts)117 int rpmtsGetDBMode(rpmts ts)
118 {
119 assert(ts != NULL);
120 return (ts->dbmode);
121 }
122
rpmtsSetDBMode(rpmts ts,int dbmode)123 int rpmtsSetDBMode(rpmts ts, int dbmode)
124 {
125 int rc = 1;
126 /* mode setting only permitted on non-open db */
127 if (ts != NULL && rpmtsGetRdb(ts) == NULL) {
128 ts->dbmode = dbmode;
129 rc = 0;
130 }
131 return rc;
132 }
133
134
rpmtsRebuildDB(rpmts ts)135 int rpmtsRebuildDB(rpmts ts)
136 {
137 int rc = -1;
138 rpmtxn txn = NULL;
139 int rebuildflags = 0;
140
141 /* Cannot do this on a populated transaction set */
142 if (rpmtsNElements(ts) > 0)
143 return -1;
144
145 if (rpmExpandNumeric("%{?_rebuilddb_salvage}"))
146 rebuildflags |= RPMDB_REBUILD_FLAG_SALVAGE;
147
148 txn = rpmtxnBegin(ts, RPMTXN_WRITE);
149 if (txn) {
150 if (!(ts->vsflags & RPMVSF_NOHDRCHK))
151 rc = rpmdbRebuild(ts->rootDir, ts, headerCheck, rebuildflags);
152 else
153 rc = rpmdbRebuild(ts->rootDir, NULL, NULL, rebuildflags);
154 rpmtxnEnd(txn);
155 }
156 return rc;
157 }
158
rpmtsVerifyDB(rpmts ts)159 int rpmtsVerifyDB(rpmts ts)
160 {
161 int rc = -1;
162 rpmtxn txn = rpmtxnBegin(ts, RPMTXN_READ);
163 if (txn) {
164 rc = rpmdbVerify(ts->rootDir);
165 rpmtxnEnd(txn);
166 }
167 return rc;
168 }
169
170 /* keyp might no be defined. */
rpmtsInitIterator(const rpmts ts,rpmDbiTagVal rpmtag,const void * keyp,size_t keylen)171 rpmdbMatchIterator rpmtsInitIterator(const rpmts ts, rpmDbiTagVal rpmtag,
172 const void * keyp, size_t keylen)
173 {
174 rpmdbMatchIterator mi = NULL;
175 char *tmp = NULL;
176
177 if (ts == NULL)
178 return NULL;
179
180 if (ts->rdb == NULL && rpmtsOpenDB(ts, ts->dbmode))
181 return NULL;
182
183 if (ts->keyring == NULL)
184 loadKeyring(ts);
185
186 /* Parse out "N(EVR)" tokens from a label key if present */
187 if (rpmtag == RPMDBI_LABEL && keyp != NULL && strchr(keyp, '(')) {
188 const char *se, *s = keyp;
189 char *t;
190 size_t slen = strlen(s);
191 int level = 0;
192 int c;
193
194 tmp = xmalloc(slen+1);
195 keyp = t = tmp;
196 while ((c = *s++) != '\0') {
197 switch (c) {
198 default:
199 *t++ = c;
200 break;
201 case '(':
202 /* XXX Fail if nested parens. */
203 if (level++ != 0) {
204 rpmlog(RPMLOG_ERR, _("extra '(' in package label: %s\n"), (const char*)keyp);
205 goto exit;
206 }
207 /* Parse explicit epoch. */
208 for (se = s; *se && risdigit(*se); se++)
209 {};
210 if (*se == ':') {
211 /* XXX skip explicit epoch's (for now) */
212 *t++ = '-';
213 s = se + 1;
214 } else {
215 /* No Epoch: found. Convert '(' to '-' and chug. */
216 *t++ = '-';
217 }
218 break;
219 case ')':
220 /* XXX Fail if nested parens. */
221 if (--level != 0) {
222 rpmlog(RPMLOG_ERR, _("missing '(' in package label: %s\n"), (const char*)keyp);
223 goto exit;
224 }
225 /* Don't copy trailing ')' */
226 break;
227 }
228 }
229 if (level) {
230 rpmlog(RPMLOG_ERR, _("missing ')' in package label: %s\n"), (const char*)keyp);
231 goto exit;
232 }
233 *t = '\0';
234 }
235
236 mi = rpmdbInitIterator(ts->rdb, rpmtag, keyp, keylen);
237
238 /* Verify header signature/digest during retrieve (if not disabled). */
239 if (mi && !(ts->vsflags & RPMVSF_NOHDRCHK))
240 (void) rpmdbSetHdrChk(mi, ts, headerCheck);
241
242 exit:
243 free(tmp);
244
245 return mi;
246 }
247
rpmtsGetKeyring(rpmts ts,int autoload)248 rpmKeyring rpmtsGetKeyring(rpmts ts, int autoload)
249 {
250 rpmKeyring keyring = NULL;
251 if (ts) {
252 if (ts->keyring == NULL && autoload) {
253 loadKeyring(ts);
254 }
255 keyring = rpmKeyringLink(ts->keyring);
256 }
257 return keyring;
258 }
259
rpmtsSetKeyring(rpmts ts,rpmKeyring keyring)260 int rpmtsSetKeyring(rpmts ts, rpmKeyring keyring)
261 {
262 if (ts == NULL)
263 return -1;
264
265 rpmKeyringFree(ts->keyring);
266 ts->keyring = rpmKeyringLink(keyring);
267 return 0;
268 }
269
loadKeyringFromFiles(rpmts ts)270 static int loadKeyringFromFiles(rpmts ts)
271 {
272 ARGV_t files = NULL;
273 /* XXX TODO: deal with chroot path issues */
274 char *pkpath = rpmGetPath(ts->rootDir, "%{_keyringpath}/*.key", NULL);
275 int nkeys = 0;
276
277 rpmlog(RPMLOG_DEBUG, "loading keyring from pubkeys in %s\n", pkpath);
278 if (rpmGlob(pkpath, NULL, &files)) {
279 rpmlog(RPMLOG_DEBUG, "couldn't find any keys in %s\n", pkpath);
280 goto exit;
281 }
282
283 for (char **f = files; *f; f++) {
284 int subkeysCount, i;
285 rpmPubkey *subkeys;
286 rpmPubkey key = rpmPubkeyRead(*f);
287
288 if (!key) {
289 rpmlog(RPMLOG_ERR, _("%s: reading of public key failed.\n"), *f);
290 continue;
291 }
292 if (rpmKeyringAddKey(ts->keyring, key) == 0) {
293 nkeys++;
294 rpmlog(RPMLOG_DEBUG, "added key %s to keyring\n", *f);
295 }
296 subkeys = rpmGetSubkeys(key, &subkeysCount);
297 rpmPubkeyFree(key);
298
299 for (i = 0; i < subkeysCount; i++) {
300 rpmPubkey subkey = subkeys[i];
301
302 if (rpmKeyringAddKey(ts->keyring, subkey) == 0) {
303 rpmlog(RPMLOG_DEBUG,
304 "added subkey %d of main key %s to keyring\n",
305 i, *f);
306
307 nkeys++;
308 }
309 rpmPubkeyFree(subkey);
310 }
311 free(subkeys);
312 }
313 exit:
314 free(pkpath);
315 argvFree(files);
316 return nkeys;
317 }
318
loadKeyringFromDB(rpmts ts)319 static int loadKeyringFromDB(rpmts ts)
320 {
321 Header h;
322 rpmdbMatchIterator mi;
323 int nkeys = 0;
324
325 rpmlog(RPMLOG_DEBUG, "loading keyring from rpmdb\n");
326 mi = rpmtsInitIterator(ts, RPMDBI_NAME, "gpg-pubkey", 0);
327 while ((h = rpmdbNextIterator(mi)) != NULL) {
328 struct rpmtd_s pubkeys;
329 const char *key;
330
331 if (!headerGet(h, RPMTAG_PUBKEYS, &pubkeys, HEADERGET_MINMEM))
332 continue;
333
334 while ((key = rpmtdNextString(&pubkeys))) {
335 uint8_t *pkt;
336 size_t pktlen;
337
338 if (rpmBase64Decode(key, (void **) &pkt, &pktlen) == 0) {
339 rpmPubkey key = rpmPubkeyNew(pkt, pktlen);
340 int subkeysCount, i;
341 rpmPubkey *subkeys = rpmGetSubkeys(key, &subkeysCount);
342
343 if (rpmKeyringAddKey(ts->keyring, key) == 0) {
344 char *nvr = headerGetAsString(h, RPMTAG_NVR);
345 rpmlog(RPMLOG_DEBUG, "added key %s to keyring\n", nvr);
346 free(nvr);
347 nkeys++;
348 }
349 rpmPubkeyFree(key);
350
351 for (i = 0; i < subkeysCount; i++) {
352 rpmPubkey subkey = subkeys[i];
353
354 if (rpmKeyringAddKey(ts->keyring, subkey) == 0) {
355 char *nvr = headerGetAsString(h, RPMTAG_NVR);
356 rpmlog(RPMLOG_DEBUG,
357 "added subkey %d of main key %s to keyring\n",
358 i, nvr);
359
360 free(nvr);
361 nkeys++;
362 }
363 rpmPubkeyFree(subkey);
364 }
365 free(subkeys);
366 free(pkt);
367 }
368 }
369 rpmtdFreeData(&pubkeys);
370 }
371 rpmdbFreeIterator(mi);
372
373 return nkeys;
374 }
375
loadKeyring(rpmts ts)376 static void loadKeyring(rpmts ts)
377 {
378 /* Never load the keyring if signature checking is disabled */
379 if ((rpmtsVSFlags(ts) & RPMVSF_MASK_NOSIGNATURES) !=
380 RPMVSF_MASK_NOSIGNATURES) {
381 ts->keyring = rpmKeyringNew();
382 if (loadKeyringFromFiles(ts) == 0) {
383 if (loadKeyringFromDB(ts) > 0) {
384 /* XXX make this a warning someday... */
385 rpmlog(RPMLOG_DEBUG, "Using legacy gpg-pubkey(s) from rpmdb\n");
386 }
387 }
388 }
389 }
390
addGpgProvide(Header h,const char * n,const char * v)391 static void addGpgProvide(Header h, const char *n, const char *v)
392 {
393 rpmsenseFlags pflags = (RPMSENSE_KEYRING|RPMSENSE_EQUAL);
394 char * nsn = rstrscat(NULL, "gpg(", n, ")", NULL);
395
396 headerPutString(h, RPMTAG_PROVIDENAME, nsn);
397 headerPutString(h, RPMTAG_PROVIDEVERSION, v);
398 headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pflags, 1);
399
400 free(nsn);
401 }
402
403 struct pgpdata_s {
404 char *signid;
405 char *timestr;
406 char *verid;
407 const char *userid;
408 const char *shortid;
409 uint32_t time;
410 };
411
initPgpData(pgpDigParams pubp,struct pgpdata_s * pd)412 static void initPgpData(pgpDigParams pubp, struct pgpdata_s *pd)
413 {
414 memset(pd, 0, sizeof(*pd));
415 pd->signid = pgpHexStr(pubp->signid, sizeof(pubp->signid));
416 pd->shortid = pd->signid + 8;
417 pd->userid = pubp->userid ? pubp->userid : "none";
418 pd->time = pubp->time;
419
420 rasprintf(&pd->timestr, "%x", pd->time);
421 rasprintf(&pd->verid, "%d:%s-%s", pubp->version, pd->signid, pd->timestr);
422 }
423
finiPgpData(struct pgpdata_s * pd)424 static void finiPgpData(struct pgpdata_s *pd)
425 {
426 free(pd->timestr);
427 free(pd->verid);
428 free(pd->signid);
429 memset(pd, 0, sizeof(*pd));
430 }
431
makeImmutable(Header h)432 static Header makeImmutable(Header h)
433 {
434 h = headerReload(h, RPMTAG_HEADERIMMUTABLE);
435 if (h != NULL) {
436 char *sha1 = NULL;
437 char *sha256 = NULL;
438 unsigned int blen = 0;
439 void *blob = headerExport(h, &blen);
440
441 /* XXX FIXME: bah, this code is repeated in way too many places */
442 rpmDigestBundle bundle = rpmDigestBundleNew();
443 rpmDigestBundleAdd(bundle, PGPHASHALGO_SHA1, RPMDIGEST_NONE);
444 rpmDigestBundleAdd(bundle, PGPHASHALGO_SHA256, RPMDIGEST_NONE);
445
446 rpmDigestBundleUpdate(bundle, rpm_header_magic, sizeof(rpm_header_magic));
447 rpmDigestBundleUpdate(bundle, blob, blen);
448
449 rpmDigestBundleFinal(bundle, PGPHASHALGO_SHA1, (void **)&sha1, NULL, 1);
450 rpmDigestBundleFinal(bundle, PGPHASHALGO_SHA256, (void **)&sha256, NULL, 1);
451
452 if (sha1 && sha256) {
453 headerPutString(h, RPMTAG_SHA1HEADER, sha1);
454 headerPutString(h, RPMTAG_SHA256HEADER, sha256);
455 } else {
456 h = headerFree(h);
457 }
458 free(sha1);
459 free(sha256);
460 free(blob);
461 rpmDigestBundleFree(bundle);
462 }
463 return h;
464 }
465
466 /* Build pubkey header. */
makePubkeyHeader(rpmts ts,rpmPubkey key,rpmPubkey * subkeys,int subkeysCount,Header * hdrp)467 static int makePubkeyHeader(rpmts ts, rpmPubkey key, rpmPubkey *subkeys,
468 int subkeysCount, Header * hdrp)
469 {
470 Header h = headerNew();
471 const char * afmt = "%{pubkeys:armor}";
472 const char * group = "Public Keys";
473 const char * license = "pubkey";
474 const char * buildhost = "localhost";
475 uint32_t zero = 0;
476 pgpDig dig = NULL;
477 pgpDigParams pubp = NULL;
478 struct pgpdata_s kd;
479 char * d = NULL;
480 char * enc = NULL;
481 char * s = NULL;
482 int rc = -1;
483 int i;
484
485 if ((enc = rpmPubkeyBase64(key)) == NULL)
486 goto exit;
487 if ((dig = rpmPubkeyDig(key)) == NULL)
488 goto exit;
489 if ((pubp = pgpDigGetParams(dig, PGPTAG_PUBLIC_KEY)) == NULL)
490 goto exit;
491
492 /* Build header elements. */
493 initPgpData(pubp, &kd);
494
495 rasprintf(&s, "%s public key", kd.userid);
496 headerPutString(h, RPMTAG_PUBKEYS, enc);
497
498 if ((d = headerFormat(h, afmt, NULL)) == NULL)
499 goto exit;
500
501 headerPutString(h, RPMTAG_NAME, "gpg-pubkey");
502 headerPutString(h, RPMTAG_VERSION, kd.shortid);
503 headerPutString(h, RPMTAG_RELEASE, kd.timestr);
504 headerPutString(h, RPMTAG_DESCRIPTION, d);
505 headerPutString(h, RPMTAG_GROUP, group);
506 headerPutString(h, RPMTAG_LICENSE, license);
507 headerPutString(h, RPMTAG_SUMMARY, s);
508 headerPutString(h, RPMTAG_PACKAGER, kd.userid);
509 headerPutUint32(h, RPMTAG_SIZE, &zero, 1);
510 headerPutString(h, RPMTAG_RPMVERSION, RPMVERSION);
511 headerPutString(h, RPMTAG_BUILDHOST, buildhost);
512 headerPutUint32(h, RPMTAG_BUILDTIME, &kd.time, 1);
513 headerPutString(h, RPMTAG_SOURCERPM, "(none)");
514
515 addGpgProvide(h, kd.userid, kd.verid);
516 addGpgProvide(h, kd.shortid, kd.verid);
517 addGpgProvide(h, kd.signid, kd.verid);
518
519 for (i = 0; i < subkeysCount; i++) {
520 struct pgpdata_s skd;
521 initPgpData(rpmPubkeyPgpDigParams(subkeys[i]), &skd);
522 addGpgProvide(h, skd.shortid, skd.verid);
523 addGpgProvide(h, skd.signid, skd.verid);
524 finiPgpData(&skd);
525 }
526
527 /* Reload it into immutable region and stomp standard digests on it */
528 h = makeImmutable(h);
529 if (h != NULL) {
530 *hdrp = headerLink(h);
531 rc = 0;
532 }
533
534 exit:
535 headerFree(h);
536 pgpFreeDig(dig);
537 finiPgpData(&kd);
538 free(enc);
539 free(d);
540 free(s);
541
542 return rc;
543 }
544
rpmtsImportHeader(rpmtxn txn,Header h,rpmFlags flags)545 rpmRC rpmtsImportHeader(rpmtxn txn, Header h, rpmFlags flags)
546 {
547 rpmRC rc = RPMRC_FAIL;
548
549 if (txn && h && rpmtsOpenDB(txn->ts, (O_RDWR|O_CREAT)) == 0) {
550 if (rpmdbAdd(rpmtsGetRdb(txn->ts), h) == 0) {
551 rc = RPMRC_OK;
552 }
553 }
554 return rc;
555 }
556
rpmtsImportPubkey(const rpmts ts,const unsigned char * pkt,size_t pktlen)557 rpmRC rpmtsImportPubkey(const rpmts ts, const unsigned char * pkt, size_t pktlen)
558 {
559 Header h = NULL;
560 rpmRC rc = RPMRC_FAIL; /* assume failure */
561 rpmPubkey pubkey = NULL;
562 rpmPubkey *subkeys = NULL;
563 int subkeysCount = 0;
564 rpmVSFlags oflags = rpmtsVSFlags(ts);
565 rpmKeyring keyring;
566 rpmtxn txn = rpmtxnBegin(ts, RPMTXN_WRITE);
567 int krc, i;
568
569 if (txn == NULL)
570 return rc;
571
572 /* XXX keyring wont load if sigcheck disabled, force it temporarily */
573 rpmtsSetVSFlags(ts, (oflags & ~RPMVSF_MASK_NOSIGNATURES));
574 keyring = rpmtsGetKeyring(ts, 1);
575 rpmtsSetVSFlags(ts, oflags);
576
577 if ((pubkey = rpmPubkeyNew(pkt, pktlen)) == NULL)
578 goto exit;
579
580 if ((subkeys = rpmGetSubkeys(pubkey, &subkeysCount)) == NULL)
581 goto exit;
582
583 krc = rpmKeyringAddKey(keyring, pubkey);
584 if (krc < 0)
585 goto exit;
586
587 /* If we dont already have the key, make a persistent record of it */
588 if (krc == 0) {
589 rpm_tid_t tid = rpmtsGetTid(ts);
590
591 if (makePubkeyHeader(ts, pubkey, subkeys, subkeysCount, &h) != 0)
592 goto exit;
593
594 headerPutUint32(h, RPMTAG_INSTALLTIME, &tid, 1);
595 headerPutUint32(h, RPMTAG_INSTALLTID, &tid, 1);
596
597 /* Add header to database. */
598 if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_TEST)) {
599 rc = rpmtsImportHeader(txn, h, 0);
600 }
601 }
602 rc = RPMRC_OK;
603
604 exit:
605 /* Clean up. */
606 headerFree(h);
607 rpmPubkeyFree(pubkey);
608 for (i = 0; i < subkeysCount; i++)
609 rpmPubkeyFree(subkeys[i]);
610 free(subkeys);
611
612 rpmKeyringFree(keyring);
613 rpmtxnEnd(txn);
614 return rc;
615 }
616
rpmtsSetSolveCallback(rpmts ts,int (* solve)(rpmts ts,rpmds key,const void * data),const void * solveData)617 int rpmtsSetSolveCallback(rpmts ts,
618 int (*solve) (rpmts ts, rpmds key, const void * data),
619 const void * solveData)
620 {
621 int rc = 0;
622
623 if (ts) {
624 ts->solve = solve;
625 ts->solveData = solveData;
626 }
627 return rc;
628 }
629
rpmtsSolve(rpmts ts,rpmds key)630 int rpmtsSolve(rpmts ts, rpmds key)
631 {
632 int rc = 1; /* assume not found */
633 if (ts && ts->solve) {
634 rc = (*ts->solve)(ts, key, ts->solveData);
635 }
636 return rc;
637 }
638
rpmtsProblems(rpmts ts)639 rpmps rpmtsProblems(rpmts ts)
640 {
641 rpmps ps = rpmpsCreate();
642 rpmtsi pi = rpmtsiInit(ts);
643 rpmte p;
644
645 while ((p = rpmtsiNext(pi, 0)) != NULL) {
646 rpmps teprobs = rpmteProblems(p);
647 rpmpsMerge(ps, teprobs);
648 rpmpsFree(teprobs);
649 }
650 rpmtsiFree(pi);
651
652 /* Return NULL on no problems instead of an empty set */
653 if (rpmpsNumProblems(ps) == 0) {
654 ps = rpmpsFree(ps);
655 }
656
657 return ps;
658 }
659
rpmtsCleanProblems(rpmts ts)660 void rpmtsCleanProblems(rpmts ts)
661 {
662 rpmte p;
663 rpmtsi pi = rpmtsiInit(ts);
664 while ((p = rpmtsiNext(pi, 0)) != NULL)
665 rpmteCleanProblems(p);
666 rpmtsiFree(pi);
667 }
668
rpmtsClean(rpmts ts)669 void rpmtsClean(rpmts ts)
670 {
671 rpmtsi pi; rpmte p;
672 tsMembers tsmem = rpmtsMembers(ts);
673
674 if (ts == NULL)
675 return;
676
677 /* Clean up after dependency checks. */
678 pi = rpmtsiInit(ts);
679 while ((p = rpmtsiNext(pi, 0)) != NULL)
680 rpmteCleanDS(p);
681 rpmtsiFree(pi);
682
683 tsmem->addedPackages = rpmalFree(tsmem->addedPackages);
684 tsmem->rpmlib = rpmdsFree(tsmem->rpmlib);
685
686 rpmtsCleanProblems(ts);
687 }
688
689 /* hash comparison function */
uintCmp(unsigned int a,unsigned int b)690 static int uintCmp(unsigned int a, unsigned int b)
691 {
692 return (a != b);
693 }
694
695 /* "hash"function*/
uintId(unsigned int a)696 static unsigned int uintId(unsigned int a)
697 {
698 return a;
699 }
700
rpmtsEmpty(rpmts ts)701 void rpmtsEmpty(rpmts ts)
702 {
703 tsMembers tsmem = rpmtsMembers(ts);
704 if (ts == NULL)
705 return;
706
707 rpmtsClean(ts);
708
709 for (int oc = 0; oc < tsmem->orderCount; oc++) {
710 tsmem->order[oc] = rpmteFree(tsmem->order[oc]);
711 }
712
713 tsmem->orderCount = 0;
714 /* The pool cannot be emptied, there might be references to its contents */
715 tsmem->pool = rpmstrPoolFree(tsmem->pool);
716 packageHashEmpty(tsmem->removedPackages);
717 return;
718 }
719
rpmtsPrintStat(const char * name,struct rpmop_s * op)720 static void rpmtsPrintStat(const char * name, struct rpmop_s * op)
721 {
722 static const unsigned int scale = (1000 * 1000);
723 if (op != NULL && op->count > 0)
724 fprintf(stderr, " %s %6d %6lu.%06lu MB %6lu.%06lu secs\n",
725 name, op->count,
726 (unsigned long)op->bytes/scale, (unsigned long)op->bytes%scale,
727 op->usecs/scale, op->usecs%scale);
728 }
729
rpmtsPrintStats(rpmts ts)730 static void rpmtsPrintStats(rpmts ts)
731 {
732 (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_TOTAL), 0);
733
734 rpmtsPrintStat("total: ", rpmtsOp(ts, RPMTS_OP_TOTAL));
735 rpmtsPrintStat("check: ", rpmtsOp(ts, RPMTS_OP_CHECK));
736 rpmtsPrintStat("order: ", rpmtsOp(ts, RPMTS_OP_ORDER));
737 rpmtsPrintStat("verify: ", rpmtsOp(ts, RPMTS_OP_VERIFY));
738 rpmtsPrintStat("fingerprint: ", rpmtsOp(ts, RPMTS_OP_FINGERPRINT));
739 rpmtsPrintStat("install: ", rpmtsOp(ts, RPMTS_OP_INSTALL));
740 rpmtsPrintStat("erase: ", rpmtsOp(ts, RPMTS_OP_ERASE));
741 rpmtsPrintStat("scriptlets: ", rpmtsOp(ts, RPMTS_OP_SCRIPTLETS));
742 rpmtsPrintStat("compress: ", rpmtsOp(ts, RPMTS_OP_COMPRESS));
743 rpmtsPrintStat("uncompress: ", rpmtsOp(ts, RPMTS_OP_UNCOMPRESS));
744 rpmtsPrintStat("digest: ", rpmtsOp(ts, RPMTS_OP_DIGEST));
745 rpmtsPrintStat("signature: ", rpmtsOp(ts, RPMTS_OP_SIGNATURE));
746 rpmtsPrintStat("dbadd: ", rpmtsOp(ts, RPMTS_OP_DBADD));
747 rpmtsPrintStat("dbremove: ", rpmtsOp(ts, RPMTS_OP_DBREMOVE));
748 rpmtsPrintStat("dbget: ", rpmtsOp(ts, RPMTS_OP_DBGET));
749 rpmtsPrintStat("dbput: ", rpmtsOp(ts, RPMTS_OP_DBPUT));
750 rpmtsPrintStat("dbdel: ", rpmtsOp(ts, RPMTS_OP_DBDEL));
751 }
752
rpmtsFree(rpmts ts)753 rpmts rpmtsFree(rpmts ts)
754 {
755 tsMembers tsmem = rpmtsMembers(ts);
756 if (ts == NULL)
757 return NULL;
758
759 if (ts->nrefs > 1)
760 return rpmtsUnlink(ts);
761
762 rpmtsEmpty(ts);
763
764 (void) rpmtsCloseDB(ts);
765
766 tsmem->removedPackages = packageHashFree(tsmem->removedPackages);
767 tsmem->installedPackages = packageHashFree(tsmem->installedPackages);
768 tsmem->order = _free(tsmem->order);
769 ts->members = _free(ts->members);
770
771 ts->dsi = _free(ts->dsi);
772
773 if (ts->scriptFd != NULL) {
774 ts->scriptFd = fdFree(ts->scriptFd);
775 ts->scriptFd = NULL;
776 }
777 ts->rootDir = _free(ts->rootDir);
778 ts->lockPath = _free(ts->lockPath);
779 ts->lock = rpmlockFree(ts->lock);
780
781 ts->keyring = rpmKeyringFree(ts->keyring);
782 ts->netsharedPaths = argvFree(ts->netsharedPaths);
783 ts->installLangs = argvFree(ts->installLangs);
784
785 ts->plugins = rpmpluginsFree(ts->plugins);
786
787 rpmtriggersFree(ts->trigs2run);
788
789 if (_rpmts_stats)
790 rpmtsPrintStats(ts);
791
792 (void) rpmtsUnlink(ts);
793
794 ts = _free(ts);
795
796 return NULL;
797 }
798
rpmtsVSFlags(rpmts ts)799 rpmVSFlags rpmtsVSFlags(rpmts ts)
800 {
801 rpmVSFlags vsflags = 0;
802 if (ts != NULL)
803 vsflags = ts->vsflags;
804 return vsflags;
805 }
806
rpmtsSetVSFlags(rpmts ts,rpmVSFlags vsflags)807 rpmVSFlags rpmtsSetVSFlags(rpmts ts, rpmVSFlags vsflags)
808 {
809 rpmVSFlags ovsflags = 0;
810 if (ts != NULL) {
811 ovsflags = ts->vsflags;
812 ts->vsflags = vsflags;
813 }
814 return ovsflags;
815 }
816
rpmtsVfyFlags(rpmts ts)817 rpmVSFlags rpmtsVfyFlags(rpmts ts)
818 {
819 rpmVSFlags vfyflags = 0;
820 if (ts != NULL)
821 vfyflags = ts->vfyflags;
822 return vfyflags;
823 }
824
rpmtsSetVfyFlags(rpmts ts,rpmVSFlags vfyflags)825 rpmVSFlags rpmtsSetVfyFlags(rpmts ts, rpmVSFlags vfyflags)
826 {
827 rpmVSFlags ovfyflags = 0;
828 if (ts != NULL) {
829 ovfyflags = ts->vfyflags;
830 ts->vfyflags = vfyflags;
831 }
832 return ovfyflags;
833 }
834
rpmtsVfyLevel(rpmts ts)835 int rpmtsVfyLevel(rpmts ts)
836 {
837 int vfylevel = 0;
838 if (ts != NULL)
839 vfylevel = ts->vfylevel;
840 return vfylevel;
841 }
842
rpmtsSetVfyLevel(rpmts ts,int vfylevel)843 int rpmtsSetVfyLevel(rpmts ts, int vfylevel)
844 {
845 int ovfylevel = 0;
846 if (ts != NULL) {
847 ovfylevel = ts->vfylevel;
848 ts->vfylevel = vfylevel;
849 }
850 return ovfylevel;
851 }
852
rpmtsRootDir(rpmts ts)853 const char * rpmtsRootDir(rpmts ts)
854 {
855 return ts ? ts->rootDir : NULL;
856 }
857
rpmtsSetRootDir(rpmts ts,const char * rootDir)858 int rpmtsSetRootDir(rpmts ts, const char * rootDir)
859 {
860 if (ts == NULL || (rootDir && rootDir[0] != '/')) {
861 return -1;
862 }
863
864 ts->rootDir = _free(ts->rootDir);
865 /* Ensure clean path with a trailing slash */
866 ts->rootDir = rootDir ? rpmGetPath(rootDir, NULL) : xstrdup("/");
867 if (!rstreq(ts->rootDir, "/")) {
868 rstrcat(&ts->rootDir, "/");
869 }
870 return 0;
871 }
872
rpmtsScriptFd(rpmts ts)873 FD_t rpmtsScriptFd(rpmts ts)
874 {
875 FD_t scriptFd = NULL;
876 if (ts != NULL) {
877 scriptFd = ts->scriptFd;
878 }
879 return scriptFd;
880 }
881
rpmtsSetScriptFd(rpmts ts,FD_t scriptFd)882 void rpmtsSetScriptFd(rpmts ts, FD_t scriptFd)
883 {
884
885 if (ts != NULL) {
886 if (ts->scriptFd != NULL) {
887 ts->scriptFd = fdFree(ts->scriptFd);
888 ts->scriptFd = NULL;
889 }
890 if (scriptFd != NULL)
891 ts->scriptFd = fdLink(scriptFd);
892 }
893 }
894
rpmtsGetTid(rpmts ts)895 rpm_tid_t rpmtsGetTid(rpmts ts)
896 {
897 rpm_tid_t tid = (rpm_tid_t)-1; /* XXX -1 is time(2) error return. */
898 if (ts != NULL) {
899 tid = ts->tid;
900 }
901 return tid;
902 }
903
rpmtsSetTid(rpmts ts,rpm_tid_t tid)904 rpm_tid_t rpmtsSetTid(rpmts ts, rpm_tid_t tid)
905 {
906 rpm_tid_t otid = (rpm_tid_t)-1; /* XXX -1 is time(2) error return. */
907 if (ts != NULL) {
908 otid = ts->tid;
909 ts->tid = tid;
910 }
911 return otid;
912 }
913
rpmtsGetRdb(rpmts ts)914 rpmdb rpmtsGetRdb(rpmts ts)
915 {
916 rpmdb rdb = NULL;
917 if (ts != NULL) {
918 rdb = ts->rdb;
919 }
920 return rdb;
921 }
922
rpmtsNotify(rpmts ts,rpmte te,rpmCallbackType what,rpm_loff_t amount,rpm_loff_t total)923 void * rpmtsNotify(rpmts ts, rpmte te,
924 rpmCallbackType what, rpm_loff_t amount, rpm_loff_t total)
925 {
926 void * ptr = NULL;
927 if (ts && ts->notify) {
928 Header h = NULL;
929 fnpyKey cbkey = NULL;
930 if (te) {
931 h = rpmteHeader(te);
932 cbkey = rpmteKey(te);
933 }
934 ptr = ts->notify(h, what, amount, total, cbkey, ts->notifyData);
935
936 if (h) {
937 headerFree(h); /* undo rpmteHeader() ref */
938 }
939 }
940 return ptr;
941 }
942
rpmtsNElements(rpmts ts)943 int rpmtsNElements(rpmts ts)
944 {
945 int nelements = 0;
946 tsMembers tsmem = rpmtsMembers(ts);
947 if (tsmem != NULL && tsmem->order != NULL) {
948 nelements = tsmem->orderCount;
949 }
950 return nelements;
951 }
952
rpmtsElement(rpmts ts,int ix)953 rpmte rpmtsElement(rpmts ts, int ix)
954 {
955 rpmte te = NULL;
956 tsMembers tsmem = rpmtsMembers(ts);
957 if (tsmem != NULL && tsmem->order != NULL) {
958 if (ix >= 0 && ix < tsmem->orderCount)
959 te = tsmem->order[ix];
960 }
961 return te;
962 }
963
rpmtsFilterFlags(rpmts ts)964 rpmprobFilterFlags rpmtsFilterFlags(rpmts ts)
965 {
966 return (ts != NULL ? ts->ignoreSet : 0);
967 }
968
rpmtsFlags(rpmts ts)969 rpmtransFlags rpmtsFlags(rpmts ts)
970 {
971 return (ts != NULL ? ts->transFlags : 0);
972 }
973
rpmtsSetFlags(rpmts ts,rpmtransFlags transFlags)974 rpmtransFlags rpmtsSetFlags(rpmts ts, rpmtransFlags transFlags)
975 {
976 rpmtransFlags otransFlags = 0;
977 if (ts != NULL) {
978 otransFlags = ts->transFlags;
979 ts->transFlags = transFlags;
980 }
981 return otransFlags;
982 }
983
rpmtsColor(rpmts ts)984 rpm_color_t rpmtsColor(rpmts ts)
985 {
986 return (ts != NULL ? ts->color : 0);
987 }
988
rpmtsSetColor(rpmts ts,rpm_color_t color)989 rpm_color_t rpmtsSetColor(rpmts ts, rpm_color_t color)
990 {
991 rpm_color_t ocolor = 0;
992 if (ts != NULL) {
993 ocolor = ts->color;
994 ts->color = color;
995 }
996 return ocolor;
997 }
998
rpmtsPrefColor(rpmts ts)999 rpm_color_t rpmtsPrefColor(rpmts ts)
1000 {
1001 return (ts != NULL ? ts->prefcolor : 0);
1002 }
1003
rpmtsSetPrefColor(rpmts ts,rpm_color_t color)1004 rpm_color_t rpmtsSetPrefColor(rpmts ts, rpm_color_t color)
1005 {
1006 rpm_color_t ocolor = 0;
1007 if (ts != NULL) {
1008 ocolor = ts->prefcolor;
1009 ts->prefcolor = color;
1010 }
1011 return ocolor;
1012 }
1013
rpmtsOp(rpmts ts,rpmtsOpX opx)1014 rpmop rpmtsOp(rpmts ts, rpmtsOpX opx)
1015 {
1016 rpmop op = NULL;
1017
1018 if (ts != NULL && opx >= 0 && opx < RPMTS_OP_MAX)
1019 op = ts->ops + opx;
1020 return op;
1021 }
1022
rpmtsPlugins(rpmts ts)1023 rpmPlugins rpmtsPlugins(rpmts ts)
1024 {
1025 rpmPlugins plugins = NULL;
1026
1027 if (ts != NULL) {
1028 if (ts->plugins == NULL)
1029 ts->plugins = rpmpluginsNew(ts);
1030 plugins = ts->plugins;
1031 }
1032 return plugins;
1033 }
1034
rpmtsSetNotifyCallback(rpmts ts,rpmCallbackFunction notify,rpmCallbackData notifyData)1035 int rpmtsSetNotifyCallback(rpmts ts,
1036 rpmCallbackFunction notify, rpmCallbackData notifyData)
1037 {
1038 if (ts != NULL) {
1039 ts->notify = notify;
1040 ts->notifyData = notifyData;
1041 }
1042 return 0;
1043 }
1044
rpmtsMembers(rpmts ts)1045 tsMembers rpmtsMembers(rpmts ts)
1046 {
1047 return (ts != NULL) ? ts->members : NULL;
1048 }
1049
rpmtsPool(rpmts ts)1050 rpmstrPool rpmtsPool(rpmts ts)
1051 {
1052 tsMembers tsmem = rpmtsMembers(ts);
1053 rpmstrPool tspool = NULL;
1054
1055 if (tsmem) {
1056 if (tsmem->pool == NULL)
1057 tsmem->pool = rpmstrPoolCreate();
1058 tspool = tsmem->pool;
1059 }
1060 return tspool;
1061 }
1062
vfylevel_init(void)1063 static int vfylevel_init(void)
1064 {
1065 int vfylevel = -1;
1066 char *val = rpmExpand("%{?_pkgverify_level}", NULL);
1067
1068 if (rstreq(val, "all"))
1069 vfylevel = RPMSIG_SIGNATURE_TYPE|RPMSIG_DIGEST_TYPE;
1070 else if (rstreq(val, "signature"))
1071 vfylevel = RPMSIG_SIGNATURE_TYPE;
1072 else if (rstreq(val, "digest"))
1073 vfylevel = RPMSIG_DIGEST_TYPE;
1074 else if (rstreq(val, "none"))
1075 vfylevel = 0;
1076 else if (!rstreq(val, ""))
1077 rpmlog(RPMLOG_WARNING, _("invalid package verify level %s\n"), val);
1078
1079 free(val);
1080 return vfylevel;
1081 }
1082
rpmtsCreate(void)1083 rpmts rpmtsCreate(void)
1084 {
1085 rpmts ts;
1086 tsMembers tsmem;
1087
1088 ts = xcalloc(1, sizeof(*ts));
1089 memset(&ts->ops, 0, sizeof(ts->ops));
1090 (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_TOTAL), -1);
1091 ts->dsi = NULL;
1092
1093 ts->solve = NULL;
1094 ts->solveData = NULL;
1095
1096 ts->rdb = NULL;
1097 ts->dbmode = O_RDONLY;
1098
1099 ts->scriptFd = NULL;
1100 ts->tid = (rpm_tid_t) time(NULL);
1101
1102 ts->color = rpmExpandNumeric("%{?_transaction_color}");
1103 ts->prefcolor = rpmExpandNumeric("%{?_prefer_color}")?:2;
1104
1105 ts->netsharedPaths = NULL;
1106 ts->installLangs = NULL;
1107 { char *tmp = rpmExpand("%{_netsharedpath}", NULL);
1108 if (tmp && *tmp != '%') {
1109 argvSplit(&ts->netsharedPaths, tmp, ":");
1110 }
1111 free(tmp);
1112
1113 tmp = rpmExpand("%{_install_langs}", NULL);
1114 if (tmp && *tmp != '%') {
1115 ARGV_t langs = NULL;
1116 argvSplit(&langs, tmp, ":");
1117 /* If we'll be installing all languages anyway, don't bother */
1118 for (ARGV_t l = langs; *l; l++) {
1119 if (rstreq(*l, "all")) {
1120 langs = argvFree(langs);
1121 break;
1122 }
1123 }
1124 ts->installLangs = langs;
1125 }
1126 free(tmp);
1127 }
1128
1129 tsmem = xcalloc(1, sizeof(*ts->members));
1130 tsmem->pool = NULL;
1131 tsmem->delta = 5;
1132 tsmem->addedPackages = NULL;
1133 tsmem->removedPackages = packageHashCreate(128, uintId, uintCmp, NULL, NULL);
1134 tsmem->installedPackages = packageHashCreate(128, uintId, uintCmp, NULL, NULL);
1135 tsmem->orderAlloced = 0;
1136 tsmem->orderCount = 0;
1137 tsmem->order = NULL;
1138 ts->members = tsmem;
1139
1140 ts->rootDir = NULL;
1141 ts->keyring = NULL;
1142 ts->vfyflags = rpmExpandNumeric("%{?_pkgverify_flags}");
1143 ts->vfylevel = vfylevel_init();
1144
1145 ts->nrefs = 0;
1146
1147 ts->plugins = NULL;
1148
1149 ts->trigs2run = rpmtriggersCreate(10);
1150
1151 ts->min_writes = (rpmExpandNumeric("%{?_minimize_writes}") > 0);
1152
1153 return rpmtsLink(ts);
1154 }
1155
rpmtsiFree(rpmtsi tsi)1156 rpmtsi rpmtsiFree(rpmtsi tsi)
1157 {
1158 /* XXX watchout: a funky recursion segfaults here iff nrefs is wrong. */
1159 if (tsi) {
1160 tsi->ts = rpmtsFree(tsi->ts);
1161 _free(tsi);
1162 }
1163 return NULL;
1164 }
1165
rpmtsiInit(rpmts ts)1166 rpmtsi rpmtsiInit(rpmts ts)
1167 {
1168 rpmtsi tsi = NULL;
1169
1170 tsi = xcalloc(1, sizeof(*tsi));
1171 tsi->ts = rpmtsLink(ts);
1172 tsi->oc = 0;
1173 return tsi;
1174 }
1175
1176 /**
1177 * Return next transaction element.
1178 * @param tsi transaction element iterator
1179 * @return transaction element, NULL on termination
1180 */
1181 static
rpmtsiNextElement(rpmtsi tsi)1182 rpmte rpmtsiNextElement(rpmtsi tsi)
1183 {
1184 rpmte te = NULL;
1185 int oc = -1;
1186
1187 if (tsi == NULL || tsi->ts == NULL || rpmtsNElements(tsi->ts) <= 0)
1188 return te;
1189
1190 if (tsi->oc < rpmtsNElements(tsi->ts)) oc = tsi->oc++;
1191 if (oc != -1)
1192 te = rpmtsElement(tsi->ts, oc);
1193 return te;
1194 }
1195
rpmtsiNext(rpmtsi tsi,rpmElementTypes types)1196 rpmte rpmtsiNext(rpmtsi tsi, rpmElementTypes types)
1197 {
1198 rpmte te;
1199
1200 while ((te = rpmtsiNextElement(tsi)) != NULL) {
1201 if (types == 0 || (rpmteType(te) & types) != 0)
1202 break;
1203 }
1204 return te;
1205 }
1206
1207 #define RPMLOCK_PATH LOCALSTATEDIR "/rpm/.rpm.lock"
rpmtxnBegin(rpmts ts,rpmtxnFlags flags)1208 rpmtxn rpmtxnBegin(rpmts ts, rpmtxnFlags flags)
1209 {
1210 static const char * const rpmlock_path_default = "%{?_rpmlock_path}";
1211 rpmtxn txn = NULL;
1212
1213 if (ts == NULL)
1214 return NULL;
1215
1216 if (ts->lockPath == NULL) {
1217 const char *rootDir = rpmtsRootDir(ts);
1218 char *t;
1219
1220 if (!rootDir || rpmChrootDone())
1221 rootDir = "/";
1222
1223 t = rpmGenPath(rootDir, rpmlock_path_default, NULL);
1224 if (t == NULL || *t == '\0' || *t == '%') {
1225 free(t);
1226 t = xstrdup(RPMLOCK_PATH);
1227 }
1228 ts->lockPath = xstrdup(t);
1229 (void) rpmioMkpath(dirname(t), 0755, getuid(), getgid());
1230 free(t);
1231 }
1232
1233 if (ts->lock == NULL)
1234 ts->lock = rpmlockNew(ts->lockPath, _("transaction"));
1235
1236 if (rpmlockAcquire(ts->lock)) {
1237 txn = xcalloc(1, sizeof(*txn));
1238 txn->lock = ts->lock;
1239 txn->flags = flags;
1240 txn->ts = rpmtsLink(ts);
1241 if (txn->flags & RPMTXN_WRITE)
1242 rpmsqBlock(SIG_BLOCK);
1243 }
1244
1245 return txn;
1246 }
1247
rpmtxnEnd(rpmtxn txn)1248 rpmtxn rpmtxnEnd(rpmtxn txn)
1249 {
1250 if (txn) {
1251 rpmlockRelease(txn->lock);
1252 if (txn->flags & RPMTXN_WRITE)
1253 rpmsqBlock(SIG_UNBLOCK);
1254 rpmtsFree(txn->ts);
1255 free(txn);
1256 }
1257 return NULL;
1258 }
1259