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