Avoid alloca() in rpmtsInitIterator()
[platform/upstream/rpm.git] / lib / rpmts.c
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 <rpm/rpmtag.h>
8 #include <rpm/rpmlib.h>                 /* rpmReadPackage etc */
9 #include <rpm/rpmurl.h>
10 #include "rpmio/digest.h"
11 #include <rpm/rpmmacro.h>
12 #include <rpm/rpmfileutil.h>            /* rpmtsOpenDB() needs rpmGetPath */
13 #include <rpm/rpmstring.h>
14
15 #include <rpm/rpmdb.h>
16 #include <rpm/rpmal.h>
17 #include <rpm/rpmds.h>
18 #include <rpm/rpmfi.h>
19 #include <rpm/rpmlog.h>
20 #include <rpm/rpmte.h>
21
22 #include "lib/rpmlock.h"
23 #include "lib/rpmts_internal.h"
24
25 /* XXX FIXME: merge with existing (broken?) tests in system.h */
26 /* portability fiddles */
27 #if STATFS_IN_SYS_STATVFS
28 #include <sys/statvfs.h>
29
30 #else
31 # if STATFS_IN_SYS_VFS
32 #  include <sys/vfs.h>
33 # else
34 #  if STATFS_IN_SYS_MOUNT
35 #   include <sys/mount.h>
36 #  else
37 #   if STATFS_IN_SYS_STATFS
38 #    include <sys/statfs.h>
39 #   endif
40 #  endif
41 # endif
42 #endif
43
44 #include "debug.h"
45
46 int _rpmts_debug = 0;
47
48 int _rpmts_stats = 0;
49
50 rpmts rpmtsUnlink(rpmts ts, const char * msg)
51 {
52 if (_rpmts_debug)
53 fprintf(stderr, "--> ts %p -- %d %s\n", ts, ts->nrefs, msg);
54     ts->nrefs--;
55     return NULL;
56 }
57
58 rpmts rpmtsLink(rpmts ts, const char * msg)
59 {
60     ts->nrefs++;
61 if (_rpmts_debug)
62 fprintf(stderr, "--> ts %p ++ %d %s\n", ts, ts->nrefs, msg);
63     return ts;
64 }
65
66 int rpmtsCloseDB(rpmts ts)
67 {
68     int rc = 0;
69
70     if (ts->rdb != NULL) {
71         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBGET), 
72                         rpmdbOp(ts->rdb, RPMDB_OP_DBGET));
73         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBPUT),
74                         rpmdbOp(ts->rdb, RPMDB_OP_DBPUT));
75         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBDEL),
76                         rpmdbOp(ts->rdb, RPMDB_OP_DBDEL));
77         rc = rpmdbClose(ts->rdb);
78         ts->rdb = NULL;
79     }
80     return rc;
81 }
82
83 int rpmtsOpenDB(rpmts ts, int dbmode)
84 {
85     int rc = 0;
86
87     if (ts->rdb != NULL && ts->dbmode == dbmode)
88         return 0;
89
90     (void) rpmtsCloseDB(ts);
91
92     /* XXX there's a potential db lock race here. */
93
94     ts->dbmode = dbmode;
95     rc = rpmdbOpen(ts->rootDir, &ts->rdb, ts->dbmode, 0644);
96     if (rc) {
97         char * dn = rpmGetPath(ts->rootDir, "%{_dbpath}", NULL);
98         rpmlog(RPMLOG_ERR,
99                         _("cannot open Packages database in %s\n"), dn);
100         dn = _free(dn);
101     }
102     return rc;
103 }
104
105 int rpmtsInitDB(rpmts ts, int dbmode)
106 {
107     void *lock = rpmtsAcquireLock(ts);
108     int rc = -1;
109     if (lock)
110             rc = rpmdbInit(ts->rootDir, dbmode);
111     rpmtsFreeLock(lock);
112     return rc;
113 }
114
115 int rpmtsGetDBMode(rpmts ts)
116 {
117     assert(ts != NULL);
118     return (ts->dbmode);
119 }
120
121 int rpmtsSetDBMode(rpmts ts, int dbmode)
122 {
123     int rc = 1;
124     /* mode setting only permitted on non-open db */
125     if (ts != NULL && rpmtsGetRdb(ts) == NULL) {
126         ts->dbmode = dbmode;
127         rc = 0;
128     }
129     return rc;
130 }
131
132
133 int rpmtsRebuildDB(rpmts ts)
134 {
135     int rc;
136     void *lock = rpmtsAcquireLock(ts);
137     if (!lock) return -1;
138     if (!(ts->vsflags & RPMVSF_NOHDRCHK))
139         rc = rpmdbRebuild(ts->rootDir, ts, headerCheck);
140     else
141         rc = rpmdbRebuild(ts->rootDir, NULL, NULL);
142     rpmtsFreeLock(lock);
143     return rc;
144 }
145
146 int rpmtsVerifyDB(rpmts ts)
147 {
148     return rpmdbVerify(ts->rootDir);
149 }
150
151 static int isArch(const char * arch)
152 {
153     const char * const * av;
154     static const char * const arches[] = {
155         "i386", "i486", "i586", "i686", "athlon", "pentium3", "pentium4", "x86_64", "amd64", "ia32e", "geode",
156         "alpha", "alphaev5", "alphaev56", "alphapca56", "alphaev6", "alphaev67",
157         "sparc", "sun4", "sun4m", "sun4c", "sun4d", "sparcv8", "sparcv9", "sparcv9v",
158         "sparc64", "sparc64v", "sun4u",
159         "mips", "mipsel", "IP",
160         "ppc", "ppciseries", "ppcpseries",
161         "ppc64", "ppc64iseries", "ppc64pseries",
162         "m68k",
163         "rs6000",
164         "ia64",
165         "armv3l", "armv4b", "armv4l", "armv4tl", "armv5tel", "armv5tejl", "armv6l",
166         "s390", "i370", "s390x",
167         "sh", "xtensa",
168         "noarch",
169         NULL,
170     };
171
172     for (av = arches; *av != NULL; av++) {
173         if (!strcmp(arch, *av))
174             return 1;
175     }
176     return 0;
177 }
178
179 /* keyp might no be defined. */
180 rpmdbMatchIterator rpmtsInitIterator(const rpmts ts, rpmTag rpmtag,
181                         const void * keyp, size_t keylen)
182 {
183     rpmdbMatchIterator mi = NULL;
184     const char * arch = NULL;
185     char *tmp = NULL;
186     int xx;
187
188     if (ts->rdb == NULL && rpmtsOpenDB(ts, ts->dbmode))
189         return NULL;
190
191     /* Parse out "N(EVR).A" tokens from a label key. */
192     if (rpmtag == RPMDBI_LABEL && keyp != NULL) {
193         const char *se, *s = keyp;
194         char *t;
195         size_t slen = strlen(s);
196         int level = 0;
197         int c;
198
199         tmp = xmalloc(slen+1);
200         keyp = t = tmp;
201         while ((c = *s++) != '\0') {
202             switch (c) {
203             default:
204                 *t++ = c;
205                 break;
206             case '(':
207                 /* XXX Fail if nested parens. */
208                 if (level++ != 0) {
209                     rpmlog(RPMLOG_ERR, _("extra '(' in package label: %s\n"), (const char*)keyp);
210                     goto exit;
211                 }
212                 /* Parse explicit epoch. */
213                 for (se = s; *se && risdigit(*se); se++)
214                     {};
215                 if (*se == ':') {
216                     /* XXX skip explicit epoch's (for now) */
217                     *t++ = '-';
218                     s = se + 1;
219                 } else {
220                     /* No Epoch: found. Convert '(' to '-' and chug. */
221                     *t++ = '-';
222                 }
223                 break;
224             case ')':
225                 /* XXX Fail if nested parens. */
226                 if (--level != 0) {
227                     rpmlog(RPMLOG_ERR, _("missing '(' in package label: %s\n"), (const char*)keyp);
228                     goto exit;
229                 }
230                 /* Don't copy trailing ')' */
231                 break;
232             }
233         }
234         if (level) {
235             rpmlog(RPMLOG_ERR, _("missing ')' in package label: %s\n"), (const char*)keyp);
236             goto exit;
237         }
238         *t = '\0';
239         t = (char *) keyp;
240         t = strrchr(t, '.');
241         /* Is this a valid ".arch" suffix? */
242         if (t != NULL && isArch(t+1)) {
243            *t++ = '\0';
244            arch = t;
245         }
246     }
247
248     mi = rpmdbInitIterator(ts->rdb, rpmtag, keyp, keylen);
249
250     /* Verify header signature/digest during retrieve (if not disabled). */
251     if (mi && !(ts->vsflags & RPMVSF_NOHDRCHK))
252         (void) rpmdbSetHdrChk(mi, ts, headerCheck);
253
254     /* Select specified arch only. */
255     if (arch != NULL)
256         xx = rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_DEFAULT, arch);
257
258 exit:
259     free(tmp);
260
261     return mi;
262 }
263
264 rpmRC rpmtsFindPubkey(rpmts ts)
265 {
266     const void * sig = rpmtsSig(ts);
267     pgpDig dig = rpmtsDig(ts);
268     pgpDigParams sigp = rpmtsSignature(ts);
269     pgpDigParams pubp = rpmtsPubkey(ts);
270     rpmRC res = RPMRC_NOKEY;
271     char * pubkeysource = NULL;
272     int xx;
273
274     if (sig == NULL || dig == NULL || sigp == NULL || pubp == NULL)
275         goto exit;
276
277 #if 0
278 fprintf(stderr, "==> find sig id %08x %08x ts pubkey id %08x %08x\n",
279 pgpGrab(sigp->signid, 4), pgpGrab(sigp->signid+4, 4),
280 pgpGrab(ts->pksignid, 4), pgpGrab(ts->pksignid+4, 4));
281 #endif
282
283     /* Lazy free of previous pubkey if pubkey does not match this signature. */
284     if (memcmp(sigp->signid, ts->pksignid, sizeof(ts->pksignid))) {
285 #if 0
286 fprintf(stderr, "*** free pkt %p[%d] id %08x %08x\n", ts->pkpkt, ts->pkpktlen, pgpGrab(ts->pksignid, 4), pgpGrab(ts->pksignid+4, 4));
287 #endif
288         ts->pkpkt = _free(ts->pkpkt);
289         ts->pkpktlen = 0;
290         memset(ts->pksignid, 0, sizeof(ts->pksignid));
291     }
292
293     /* Try rpmdb keyring lookup. */
294     if (ts->pkpkt == NULL) {
295         int hx = -1;
296         int ix = -1;
297         rpmdbMatchIterator mi;
298         Header h;
299
300         /* Retrieve the pubkey that matches the signature. */
301         mi = rpmtsInitIterator(ts, RPMTAG_PUBKEYS, sigp->signid, sizeof(sigp->signid));
302         while ((h = rpmdbNextIterator(mi)) != NULL) {
303             const char ** pubkeys;
304             rpmTagType pt;
305             rpm_count_t pc;
306
307             if (!headerGetEntry(h, RPMTAG_PUBKEYS, &pt, (rpm_data_t *)&pubkeys, &pc))
308                 continue;
309             hx = rpmdbGetIteratorOffset(mi);
310             ix = rpmdbGetIteratorFileNum(mi);
311             if (ix >= pc
312              || b64decode(pubkeys[ix], (void **) &ts->pkpkt, &ts->pkpktlen))
313                 ix = -1;
314             pubkeys = headerFreeData(pubkeys, pt);
315             break;
316         }
317         mi = rpmdbFreeIterator(mi);
318
319         if (ix >= 0) {
320             rasprintf(&pubkeysource, "h#%d", hx);
321         } else {
322             ts->pkpkt = _free(ts->pkpkt);
323             ts->pkpktlen = 0;
324         }
325     }
326
327     /* Was a matching pubkey found? */
328     if (ts->pkpkt == NULL || ts->pkpktlen == 0)
329         goto exit;
330
331     /* Retrieve parameters from pubkey packet(s). */
332     xx = pgpPrtPkts(ts->pkpkt, ts->pkpktlen, dig, 0);
333
334     /* Do the parameters match the signature? */
335     if (sigp->pubkey_algo == pubp->pubkey_algo
336 #ifdef  NOTYET
337      && sigp->hash_algo == pubp->hash_algo
338 #endif
339      && !memcmp(sigp->signid, pubp->signid, sizeof(sigp->signid)) )
340     {
341
342         /* XXX Verify any pubkey signatures. */
343
344         /* Pubkey packet looks good, save the signer id. */
345         memcpy(ts->pksignid, pubp->signid, sizeof(ts->pksignid));
346
347         if (pubkeysource)
348             rpmlog(RPMLOG_DEBUG, "========== %s pubkey id %08x %08x (%s)\n",
349                 (sigp->pubkey_algo == PGPPUBKEYALGO_DSA ? "DSA" :
350                 (sigp->pubkey_algo == PGPPUBKEYALGO_RSA ? "RSA" : "???")),
351                 pgpGrab(sigp->signid, 4), pgpGrab(sigp->signid+4, 4),
352                 pubkeysource);
353
354         res = RPMRC_OK;
355     }
356
357 exit:
358     pubkeysource = _free(pubkeysource);
359     if (res != RPMRC_OK) {
360         ts->pkpkt = _free(ts->pkpkt);
361         ts->pkpktlen = 0;
362     }
363     return res;
364 }
365
366 rpmRC rpmtsImportPubkey(const rpmts ts, const unsigned char * pkt, size_t pktlen)
367 {
368     static unsigned char zeros[] =
369         { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
370     const char * afmt = "%{pubkeys:armor}";
371     const char * group = "Public Keys";
372     const char * license = "pubkey";
373     const char * buildhost = "localhost";
374     rpmsenseFlags pflags = (RPMSENSE_KEYRING|RPMSENSE_EQUAL);
375     int32_t zero = 0;
376     pgpDig dig = NULL;
377     pgpDigParams pubp = NULL;
378     char * d = NULL;
379     char * enc = NULL;
380     char * n = NULL;
381     char * u = NULL;
382     char * v = NULL;
383     char * r = NULL;
384     char * evr = NULL;
385     Header h = NULL;
386     rpmRC rc = RPMRC_FAIL;              /* assume failure */
387     int xx;
388
389     if (pkt == NULL || pktlen == 0)
390         return RPMRC_FAIL;
391     if (rpmtsOpenDB(ts, (O_RDWR|O_CREAT)))
392         return RPMRC_FAIL;
393
394     if ((enc = b64encode(pkt, pktlen, -1)) == NULL)
395         goto exit;
396
397     dig = pgpNewDig();
398
399     /* Build header elements. */
400     (void) pgpPrtPkts(pkt, pktlen, dig, 0);
401     pubp = &dig->pubkey;
402
403     if (!memcmp(pubp->signid, zeros, sizeof(pubp->signid))
404      || !memcmp(pubp->time, zeros, sizeof(pubp->time))
405      || pubp->userid == NULL)
406         goto exit;
407
408     v = pgpHexStr(pubp->signid, sizeof(pubp->signid)); 
409     r = pgpHexStr(pubp->time, sizeof(pubp->time));
410
411     rasprintf(&n, "gpg(%s)", v+8);
412     rasprintf(&u, "gpg(%s)", pubp->userid ? pubp->userid : "none");
413     rasprintf(&evr, "%d:%s-%s", pubp->version, v, r);
414     /* Check for pre-existing header. */
415
416     /* Build pubkey header. */
417     h = headerNew();
418
419     xx = headerAddOrAppendEntry(h, RPMTAG_PUBKEYS,
420                         RPM_STRING_ARRAY_TYPE, &enc, 1);
421
422     d = headerSprintf(h, afmt, rpmTagTable, rpmHeaderFormats, NULL);
423     if (d == NULL)
424         goto exit;
425
426     xx = headerAddEntry(h, RPMTAG_NAME, RPM_STRING_TYPE, "gpg-pubkey", 1);
427     xx = headerAddEntry(h, RPMTAG_VERSION, RPM_STRING_TYPE, v+8, 1);
428     xx = headerAddEntry(h, RPMTAG_RELEASE, RPM_STRING_TYPE, r, 1);
429     xx = headerAddEntry(h, RPMTAG_DESCRIPTION, RPM_STRING_TYPE, d, 1);
430     xx = headerAddEntry(h, RPMTAG_GROUP, RPM_STRING_TYPE, group, 1);
431     xx = headerAddEntry(h, RPMTAG_LICENSE, RPM_STRING_TYPE, license, 1);
432     xx = headerAddEntry(h, RPMTAG_SUMMARY, RPM_STRING_TYPE, u, 1);
433
434     xx = headerAddEntry(h, RPMTAG_SIZE, RPM_INT32_TYPE, &zero, 1);
435
436     xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME,
437                         RPM_STRING_ARRAY_TYPE, &u, 1);
438     xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION,
439                         RPM_STRING_ARRAY_TYPE, &evr, 1);
440     xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS,
441                         RPM_INT32_TYPE, &pflags, 1);
442
443     xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME,
444                         RPM_STRING_ARRAY_TYPE, &n, 1);
445     xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION,
446                         RPM_STRING_ARRAY_TYPE, &evr, 1);
447     xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS,
448                         RPM_INT32_TYPE, &pflags, 1);
449
450     xx = headerAddEntry(h, RPMTAG_RPMVERSION, RPM_STRING_TYPE, RPMVERSION, 1);
451
452     /* XXX W2DO: tag value inheirited from parent? */
453     xx = headerAddEntry(h, RPMTAG_BUILDHOST, RPM_STRING_TYPE, buildhost, 1);
454     {   rpm_tid_t tid = rpmtsGetTid(ts);
455         xx = headerAddEntry(h, RPMTAG_INSTALLTIME, RPM_INT32_TYPE, &tid, 1);
456         /* XXX W2DO: tag value inheirited from parent? */
457         xx = headerAddEntry(h, RPMTAG_BUILDTIME, RPM_INT32_TYPE, &tid, 1);
458     }
459
460 #ifdef  NOTYET
461     /* XXX W2DO: tag value inheirited from parent? */
462     xx = headerAddEntry(h, RPMTAG_SOURCERPM, RPM_STRING_TYPE, fn, 1);
463 #endif
464
465     /* Add header to database. */
466     xx = rpmdbAdd(rpmtsGetRdb(ts), rpmtsGetTid(ts), h, NULL, NULL);
467     if (xx != 0)
468         goto exit;
469     rc = RPMRC_OK;
470
471 exit:
472     /* Clean up. */
473     h = headerFree(h);
474     dig = pgpFreeDig(dig);
475     n = _free(n);
476     u = _free(u);
477     v = _free(v);
478     r = _free(r);
479     evr = _free(evr);
480     enc = _free(enc);
481     d = _free(d);
482     
483     return rc;
484 }
485
486 int rpmtsCloseSDB(rpmts ts)
487 {
488     int rc = 0;
489
490     if (ts->sdb != NULL) {
491         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBGET), 
492                         rpmdbOp(ts->sdb, RPMDB_OP_DBGET));
493         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBPUT),
494                         rpmdbOp(ts->sdb, RPMDB_OP_DBPUT));
495         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBDEL),
496                         rpmdbOp(ts->sdb, RPMDB_OP_DBDEL));
497         rc = rpmdbClose(ts->sdb);
498         ts->sdb = NULL;
499     }
500     return rc;
501 }
502
503 int rpmtsOpenSDB(rpmts ts, int dbmode)
504 {
505     static int has_sdbpath = -1;
506     int rc = 0;
507
508     if (ts->sdb != NULL && ts->sdbmode == dbmode)
509         return 0;
510
511     if (has_sdbpath < 0)
512         has_sdbpath = rpmExpandNumeric("%{?_solve_dbpath:1}");
513
514     /* If not configured, don't try to open. */
515     if (has_sdbpath <= 0)
516         return 1;
517
518     addMacro(NULL, "_dbpath", NULL, "%{_solve_dbpath}", RMIL_DEFAULT);
519
520     rc = rpmdbOpen(ts->rootDir, &ts->sdb, ts->sdbmode, 0644);
521     if (rc) {
522         char * dn = rpmGetPath(ts->rootDir, "%{_dbpath}", NULL);
523         rpmlog(RPMLOG_WARNING,
524                         _("cannot open Solve database in %s\n"), dn);
525         dn = _free(dn);
526     }
527     delMacro(NULL, "_dbpath");
528
529     return rc;
530 }
531
532 /**
533  * Compare suggested package resolutions (qsort/bsearch).
534  * @param a             1st instance address
535  * @param b             2nd instance address
536  * @return              result of comparison
537  */
538 static int sugcmp(const void * a, const void * b)
539 {
540     const char * astr = *(const char **)a;
541     const char * bstr = *(const char **)b;
542     return strcmp(astr, bstr);
543 }
544
545 int rpmtsSolve(rpmts ts, rpmds ds, const void * data)
546 {
547     const char * errstr;
548     char * str;
549     char * qfmt;
550     rpmdbMatchIterator mi;
551     Header bh;
552     Header h;
553     size_t bhnamelen;
554     time_t bhtime;
555     rpmTag rpmtag;
556     const char * keyp;
557     size_t keylen;
558     int rc = 1; /* assume not found */
559     int xx;
560
561     if (rpmdsTagN(ds) != RPMTAG_REQUIRENAME)
562         return rc;
563
564     keyp = rpmdsN(ds);
565     if (keyp == NULL)
566         return rc;
567
568     if (ts->sdb == NULL) {
569         xx = rpmtsOpenSDB(ts, ts->sdbmode);
570         if (xx) return rc;
571     }
572
573     /* Look for a matching Provides: in suggested universe. */
574     rpmtag = (*keyp == '/' ? RPMTAG_BASENAMES : RPMTAG_PROVIDENAME);
575     keylen = 0;
576     mi = rpmdbInitIterator(ts->sdb, rpmtag, keyp, keylen);
577     bhnamelen = 0;
578     bhtime = 0;
579     bh = NULL;
580     while ((h = rpmdbNextIterator(mi)) != NULL) {
581         const char * hname;
582         size_t hnamelen;
583         time_t htime;
584         int32_t * ip;
585
586         if (rpmtag == RPMTAG_PROVIDENAME && !rpmdsAnyMatchesDep(h, ds, 1))
587             continue;
588
589         /* XXX Prefer the shortest name if given alternatives. */
590         hname = NULL;
591         hnamelen = 0;
592         if (headerGetEntry(h, RPMTAG_NAME, NULL, (rpm_data_t *)&hname, NULL)) {
593             if (hname)
594                 hnamelen = strlen(hname);
595         }
596         if (bhnamelen > 0 && hnamelen > bhnamelen)
597             continue;
598
599         /* XXX Prefer the newest build if given alternatives. */
600         htime = 0;
601         if (headerGetEntry(h, RPMTAG_BUILDTIME, NULL, (rpm_data_t *)&ip, NULL))
602             htime = (time_t)*ip;
603
604         if (htime <= bhtime)
605             continue;
606
607         bh = headerFree(bh);
608         bh = headerLink(h);
609         bhtime = htime;
610         bhnamelen = hnamelen;
611     }
612     mi = rpmdbFreeIterator(mi);
613
614     /* Is there a suggested resolution? */
615     if (bh == NULL)
616         goto exit;
617
618     /* Format the suggestion. */
619     qfmt = rpmExpand("%{?_solve_name_fmt}", NULL);
620     if (qfmt == NULL || *qfmt == '\0')
621         goto exit;
622     str = headerSprintf(bh, qfmt, rpmTagTable, rpmHeaderFormats, &errstr);
623     bh = headerFree(bh);
624     qfmt = _free(qfmt);
625     if (str == NULL) {
626         rpmlog(RPMLOG_ERR, _("incorrect format: %s\n"), errstr);
627         goto exit;
628     }
629
630     if (ts->transFlags & RPMTRANS_FLAG_ADDINDEPS) {
631         FD_t fd;
632         rpmRC rpmrc;
633
634         h = headerFree(h);
635         fd = Fopen(str, "r.ufdio");
636         if (fd == NULL || Ferror(fd)) {
637             rpmlog(RPMLOG_ERR, _("open of %s failed: %s\n"), str,
638                         Fstrerror(fd));
639             if (fd != NULL) {
640                 xx = Fclose(fd);
641                 fd = NULL;
642             }
643             str = _free(str);
644             goto exit;
645         }
646         rpmrc = rpmReadPackageFile(ts, fd, str, &h);
647         xx = Fclose(fd);
648         switch (rpmrc) {
649         default:
650             str = _free(str);
651             break;
652         case RPMRC_NOTTRUSTED:
653         case RPMRC_NOKEY:
654         case RPMRC_OK:
655             if (h != NULL &&
656                 !rpmtsAddInstallElement(ts, h, (fnpyKey)str, 1, NULL))
657             {
658                 rpmlog(RPMLOG_DEBUG, "Adding: %s\n", str);
659                 rc = -1;
660                 /* XXX str memory leak */
661                 break;
662             }
663             str = _free(str);
664             break;
665         }
666         h = headerFree(h);
667         goto exit;
668     }
669
670     rpmlog(RPMLOG_DEBUG, "Suggesting: %s\n", str);
671     /* If suggestion is already present, don't bother. */
672     if (ts->suggests != NULL && ts->nsuggests > 0) {
673         if (bsearch(&str, ts->suggests, ts->nsuggests,
674                         sizeof(*ts->suggests), sugcmp))
675             goto exit;
676     }
677
678     /* Add a new (unique) suggestion. */
679     ts->suggests = xrealloc(ts->suggests,
680                         sizeof(*ts->suggests) * (ts->nsuggests + 2));
681     ts->suggests[ts->nsuggests] = str;
682     ts->nsuggests++;
683     ts->suggests[ts->nsuggests] = NULL;
684
685     if (ts->nsuggests > 1)
686         qsort(ts->suggests, ts->nsuggests, sizeof(*ts->suggests), sugcmp);
687
688 exit:
689 /* FIX: ts->suggests[] may be NULL */
690     return rc;
691 }
692
693 int rpmtsAvailable(rpmts ts, const rpmds ds)
694 {
695     fnpyKey * sugkey;
696     int rc = 1; /* assume not found */
697
698     if (ts->availablePackages == NULL)
699         return rc;
700     sugkey = rpmalAllSatisfiesDepend(ts->availablePackages, ds, NULL);
701     if (sugkey == NULL)
702         return rc;
703
704     /* XXX no alternatives yet */
705     if (sugkey[0] != NULL) {
706         ts->suggests = xrealloc(ts->suggests,
707                         sizeof(*ts->suggests) * (ts->nsuggests + 2));
708         ts->suggests[ts->nsuggests] = sugkey[0];
709         sugkey[0] = NULL;
710         ts->nsuggests++;
711         ts->suggests[ts->nsuggests] = NULL;
712     }
713     sugkey = _free(sugkey);
714 /* FIX: ts->suggests[] may be NULL */
715     return rc;
716 }
717
718 int rpmtsSetSolveCallback(rpmts ts,
719                 int (*solve) (rpmts ts, rpmds key, const void * data),
720                 const void * solveData)
721 {
722     int rc = 0;
723
724     if (ts) {
725         ts->solve = solve;
726         ts->solveData = solveData;
727     }
728     return rc;
729 }
730
731 void rpmtsPrintSuggests(rpmts ts)
732 {
733     if (ts->suggests != NULL && ts->nsuggests > 0) {
734         int i;
735         rpmlog(RPMLOG_NOTICE, _("    Suggested resolutions:\n"));
736         for (i = 0; i < ts->nsuggests; i++) {
737             const char * str = ts->suggests[i];
738
739             if (str == NULL)
740                 break;
741
742             rpmlog(RPMLOG_NOTICE, "\t%s\n", str);
743         }
744     }
745 }
746
747
748 rpmps rpmtsProblems(rpmts ts)
749 {
750     rpmps ps = NULL;
751     if (ts) {
752         if (ts->probs)
753             ps = rpmpsLink(ts->probs, RPMDBG_M("rpmtsProblems"));
754     }
755     return ps;
756 }
757
758 void rpmtsCleanProblems(rpmts ts)
759 {
760     if (ts && ts->probs) {
761         ts->probs = rpmpsFree(ts->probs);
762     }
763 }
764
765 void rpmtsCleanDig(rpmts ts)
766 {
767     ts->sig = headerFreeData(ts->sig, ts->sigtype);
768     ts->dig = pgpFreeDig(ts->dig);
769 }
770
771 void rpmtsClean(rpmts ts)
772 {
773     rpmtsi pi; rpmte p;
774     int i;
775
776     if (ts == NULL)
777         return;
778
779     /* Clean up after dependency checks. */
780     pi = rpmtsiInit(ts);
781     while ((p = rpmtsiNext(pi, 0)) != NULL)
782         rpmteCleanDS(p);
783     pi = rpmtsiFree(pi);
784
785     ts->addedPackages = rpmalFree(ts->addedPackages);
786     ts->numAddedPackages = 0;
787
788     for (i = 0; i < ts->nsuggests; i++) {
789         const char * str = ts->suggests[i];
790         ts->suggests[i] = NULL;
791         _constfree(str);
792     }
793     ts->suggests = _free(ts->suggests);
794     ts->nsuggests = 0;
795
796     rpmtsCleanProblems(ts);
797
798     rpmtsCleanDig(ts);
799 }
800
801 void rpmtsEmpty(rpmts ts)
802 {
803     rpmtsi pi; rpmte p;
804     int oc;
805
806     if (ts == NULL)
807         return;
808
809     rpmtsClean(ts);
810
811     for (pi = rpmtsiInit(ts), oc = 0; (p = rpmtsiNext(pi, 0)) != NULL; oc++) {
812         ts->order[oc] = rpmteFree(ts->order[oc]);
813     }
814     pi = rpmtsiFree(pi);
815
816     ts->orderCount = 0;
817     ts->ntrees = 0;
818     ts->maxDepth = 0;
819
820     ts->numRemovedPackages = 0;
821     return;
822 }
823
824 static void rpmtsPrintStat(const char * name, struct rpmop_s * op)
825 {
826     static const unsigned int scale = (1000 * 1000);
827     if (op != NULL && op->count > 0)
828         fprintf(stderr, "   %s %6d %6lu.%06lu MB %6lu.%06lu secs\n",
829                 name, op->count,
830                 (unsigned long)op->bytes/scale, (unsigned long)op->bytes%scale,
831                 op->usecs/scale, op->usecs%scale);
832 }
833
834 static void rpmtsPrintStats(rpmts ts)
835 {
836     (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_TOTAL), 0);
837
838     rpmtsPrintStat("total:       ", rpmtsOp(ts, RPMTS_OP_TOTAL));
839     rpmtsPrintStat("check:       ", rpmtsOp(ts, RPMTS_OP_CHECK));
840     rpmtsPrintStat("order:       ", rpmtsOp(ts, RPMTS_OP_ORDER));
841     rpmtsPrintStat("fingerprint: ", rpmtsOp(ts, RPMTS_OP_FINGERPRINT));
842     rpmtsPrintStat("install:     ", rpmtsOp(ts, RPMTS_OP_INSTALL));
843     rpmtsPrintStat("erase:       ", rpmtsOp(ts, RPMTS_OP_ERASE));
844     rpmtsPrintStat("scriptlets:  ", rpmtsOp(ts, RPMTS_OP_SCRIPTLETS));
845     rpmtsPrintStat("compress:    ", rpmtsOp(ts, RPMTS_OP_COMPRESS));
846     rpmtsPrintStat("uncompress:  ", rpmtsOp(ts, RPMTS_OP_UNCOMPRESS));
847     rpmtsPrintStat("digest:      ", rpmtsOp(ts, RPMTS_OP_DIGEST));
848     rpmtsPrintStat("signature:   ", rpmtsOp(ts, RPMTS_OP_SIGNATURE));
849     rpmtsPrintStat("dbadd:       ", rpmtsOp(ts, RPMTS_OP_DBADD));
850     rpmtsPrintStat("dbremove:    ", rpmtsOp(ts, RPMTS_OP_DBREMOVE));
851     rpmtsPrintStat("dbget:       ", rpmtsOp(ts, RPMTS_OP_DBGET));
852     rpmtsPrintStat("dbput:       ", rpmtsOp(ts, RPMTS_OP_DBPUT));
853     rpmtsPrintStat("dbdel:       ", rpmtsOp(ts, RPMTS_OP_DBDEL));
854 }
855
856 rpmts rpmtsFree(rpmts ts)
857 {
858     if (ts == NULL)
859         return NULL;
860
861     if (ts->nrefs > 1)
862         return rpmtsUnlink(ts, RPMDBG_M("tsCreate"));
863
864     rpmtsEmpty(ts);
865
866     (void) rpmtsCloseDB(ts);
867
868     (void) rpmtsCloseSDB(ts);
869
870     ts->removedPackages = _free(ts->removedPackages);
871
872     ts->availablePackages = rpmalFree(ts->availablePackages);
873     ts->numAvailablePackages = 0;
874
875     ts->dsi = _free(ts->dsi);
876
877     if (ts->scriptFd != NULL) {
878         ts->scriptFd = fdFree(ts->scriptFd, RPMDBG_M("rpmtsFree"));
879         ts->scriptFd = NULL;
880     }
881     ts->rootDir = _free(ts->rootDir);
882     ts->currDir = _free(ts->currDir);
883
884     ts->order = _free(ts->order);
885     ts->orderAlloced = 0;
886
887     if (ts->pkpkt != NULL)
888         ts->pkpkt = _free(ts->pkpkt);
889     ts->pkpktlen = 0;
890     memset(ts->pksignid, 0, sizeof(ts->pksignid));
891
892     if (_rpmts_stats)
893         rpmtsPrintStats(ts);
894
895     (void) rpmtsUnlink(ts, RPMDBG_M("tsCreate"));
896
897     ts = _free(ts);
898
899     return NULL;
900 }
901
902 rpmVSFlags rpmtsVSFlags(rpmts ts)
903 {
904     rpmVSFlags vsflags = 0;
905     if (ts != NULL)
906         vsflags = ts->vsflags;
907     return vsflags;
908 }
909
910 rpmVSFlags rpmtsSetVSFlags(rpmts ts, rpmVSFlags vsflags)
911 {
912     rpmVSFlags ovsflags = 0;
913     if (ts != NULL) {
914         ovsflags = ts->vsflags;
915         ts->vsflags = vsflags;
916     }
917     return ovsflags;
918 }
919
920 int rpmtsUnorderedSuccessors(rpmts ts, int first)
921 {
922     int unorderedSuccessors = 0;
923     if (ts != NULL) {
924         unorderedSuccessors = ts->unorderedSuccessors;
925         if (first >= 0)
926             ts->unorderedSuccessors = first;
927     }
928     return unorderedSuccessors;
929 }
930
931 const char * rpmtsRootDir(rpmts ts)
932 {
933     const char * rootDir = NULL;
934
935     if (ts != NULL && ts->rootDir != NULL) {
936         urltype ut = urlPath(ts->rootDir, &rootDir);
937         switch (ut) {
938         case URL_IS_UNKNOWN:
939         case URL_IS_PATH:
940             break;
941         /* XXX these shouldn't be allowed as rootdir! */
942         case URL_IS_HTTPS:
943         case URL_IS_HTTP:
944         case URL_IS_HKP:
945         case URL_IS_FTP:
946         case URL_IS_DASH:
947         default:
948             rootDir = "/";
949             break;
950         }
951     }
952     return rootDir;
953 }
954
955 void rpmtsSetRootDir(rpmts ts, const char * rootDir)
956 {
957     if (ts != NULL) {
958         size_t rootLen;
959
960         ts->rootDir = _free(ts->rootDir);
961
962         if (rootDir == NULL) {
963 #ifndef DYING
964             ts->rootDir = xstrdup("");
965 #endif
966             return;
967         }
968         rootLen = strlen(rootDir);
969
970         /* Make sure that rootDir has trailing / */
971         if (!(rootLen && rootDir[rootLen - 1] == '/')) {
972             char * t = alloca(rootLen + 2);
973             *t = '\0';
974             (void) stpcpy( stpcpy(t, rootDir), "/");
975             rootDir = t;
976         }
977         ts->rootDir = xstrdup(rootDir);
978     }
979 }
980
981 const char * rpmtsCurrDir(rpmts ts)
982 {
983     const char * currDir = NULL;
984     if (ts != NULL) {
985         currDir = ts->currDir;
986     }
987     return currDir;
988 }
989
990 void rpmtsSetCurrDir(rpmts ts, const char * currDir)
991 {
992     if (ts != NULL) {
993         ts->currDir = _free(ts->currDir);
994         if (currDir)
995             ts->currDir = xstrdup(currDir);
996     }
997 }
998
999 FD_t rpmtsScriptFd(rpmts ts)
1000 {
1001     FD_t scriptFd = NULL;
1002     if (ts != NULL) {
1003         scriptFd = ts->scriptFd;
1004     }
1005     return scriptFd;
1006 }
1007
1008 void rpmtsSetScriptFd(rpmts ts, FD_t scriptFd)
1009 {
1010
1011     if (ts != NULL) {
1012         if (ts->scriptFd != NULL) {
1013             ts->scriptFd = fdFree(ts->scriptFd, 
1014                                   RPMDBG_M("rpmtsSetScriptFd"));
1015             ts->scriptFd = NULL;
1016         }
1017         if (scriptFd != NULL)
1018             ts->scriptFd = fdLink((void *)scriptFd, 
1019                                   RPMDBG_M("rpmtsSetScriptFd"));
1020     }
1021 }
1022
1023 int rpmtsSELinuxEnabled(rpmts ts)
1024 {
1025     return (ts != NULL ? (ts->selinuxEnabled > 0) : 0);
1026 }
1027
1028 int rpmtsChrootDone(rpmts ts)
1029 {
1030     return (ts != NULL ? ts->chrootDone : 0);
1031 }
1032
1033 int rpmtsSetChrootDone(rpmts ts, int chrootDone)
1034 {
1035     int ochrootDone = 0;
1036     if (ts != NULL) {
1037         ochrootDone = ts->chrootDone;
1038         rpmdbSetChrootDone(rpmtsGetRdb(ts), chrootDone);
1039         ts->chrootDone = chrootDone;
1040     }
1041     return ochrootDone;
1042 }
1043
1044 rpm_tid_t rpmtsGetTid(rpmts ts)
1045 {
1046     rpm_tid_t tid = -1;  /* XXX -1 is time(2) error return. */
1047     if (ts != NULL) {
1048         tid = ts->tid;
1049     }
1050     return tid;
1051 }
1052
1053 rpm_tid_t rpmtsSetTid(rpmts ts, rpm_tid_t tid)
1054 {
1055     rpm_tid_t otid = -1; /* XXX -1 is time(2) error return. */
1056     if (ts != NULL) {
1057         otid = ts->tid;
1058         ts->tid = tid;
1059     }
1060     return otid;
1061 }
1062
1063 rpmSigTag rpmtsSigtag(const rpmts ts)
1064 {
1065     rpmSigTag sigtag = 0;
1066     if (ts != NULL)
1067         sigtag = ts->sigtag;
1068     return sigtag;
1069 }
1070
1071 rpmTagType rpmtsSigtype(const rpmts ts)
1072 {
1073     rpmTagType sigtype = 0;
1074     if (ts != NULL)
1075         sigtype = ts->sigtype;
1076     return sigtype;
1077 }
1078
1079 rpm_constdata_t rpmtsSig(const rpmts ts)
1080 {
1081     rpm_constdata_t sig = NULL;
1082     if (ts != NULL)
1083         sig = ts->sig;
1084     return sig;
1085 }
1086
1087 size_t rpmtsSiglen(const rpmts ts)
1088 {
1089     size_t siglen = 0;
1090     if (ts != NULL)
1091         siglen = ts->siglen;
1092     return siglen;
1093 }
1094
1095 int rpmtsSetSig(rpmts ts, rpmSigTag sigtag, rpmTagType sigtype, 
1096                 rpm_data_t sig, size_t siglen)
1097 {
1098     if (ts != NULL) {
1099         if (ts->sig && ts->sigtype)
1100             ts->sig = headerFreeData(ts->sig, ts->sigtype);
1101         ts->sigtag = sigtag;
1102         ts->sigtype = (sig ? sigtype : 0);
1103         ts->sig = sig;
1104         ts->siglen = siglen;
1105     }
1106     return 0;
1107 }
1108
1109 pgpDig rpmtsDig(rpmts ts)
1110 {
1111 /* FIX: hide lazy malloc for now */
1112     if (ts->dig == NULL)
1113         ts->dig = pgpNewDig();
1114     if (ts->dig == NULL)
1115         return NULL;
1116     return ts->dig;
1117 }
1118
1119 pgpDigParams rpmtsSignature(const rpmts ts)
1120 {
1121     pgpDig dig = rpmtsDig(ts);
1122     if (dig == NULL) return NULL;
1123     return &dig->signature;
1124 }
1125
1126 pgpDigParams rpmtsPubkey(const rpmts ts)
1127 {
1128     pgpDig dig = rpmtsDig(ts);
1129     if (dig == NULL) return NULL;
1130     return &dig->pubkey;
1131 }
1132
1133 rpmdb rpmtsGetRdb(rpmts ts)
1134 {
1135     rpmdb rdb = NULL;
1136     if (ts != NULL) {
1137         rdb = ts->rdb;
1138     }
1139     return rdb;
1140 }
1141
1142 int rpmtsInitDSI(const rpmts ts)
1143 {
1144     rpmDiskSpaceInfo dsi;
1145     struct stat sb;
1146     int rc;
1147     int i;
1148
1149     if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_DISKSPACE)
1150         return 0;
1151
1152     rpmlog(RPMLOG_DEBUG, "mounted filesystems:\n");
1153     rpmlog(RPMLOG_DEBUG,
1154         "    i        dev    bsize       bavail       iavail mount point\n");
1155
1156     rc = rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount);
1157     if (rc || ts->filesystems == NULL || ts->filesystemCount <= 0)
1158         return rc;
1159
1160     /* Get available space on mounted file systems. */
1161
1162     ts->dsi = _free(ts->dsi);
1163     ts->dsi = xcalloc((ts->filesystemCount + 1), sizeof(*ts->dsi));
1164
1165     dsi = ts->dsi;
1166
1167     if (dsi != NULL)
1168     for (i = 0; (i < ts->filesystemCount) && dsi; i++, dsi++) {
1169 #if STATFS_IN_SYS_STATVFS
1170         struct statvfs sfb;
1171         memset(&sfb, 0, sizeof(sfb));
1172         rc = statvfs(ts->filesystems[i], &sfb);
1173 #else
1174         struct statfs sfb;
1175         memset(&sfb, 0, sizeof(sfb));
1176 #  if STAT_STATFS4
1177 /* This platform has the 4-argument version of the statfs call.  The last two
1178  * should be the size of struct statfs and 0, respectively.  The 0 is the
1179  * filesystem type, and is always 0 when statfs is called on a mounted
1180  * filesystem, as we're doing.
1181  */
1182         rc = statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0);
1183 #  else
1184         rc = statfs(ts->filesystems[i], &sfb);
1185 #  endif
1186 #endif
1187         if (rc)
1188             break;
1189
1190         rc = stat(ts->filesystems[i], &sb);
1191         if (rc)
1192             break;
1193         dsi->dev = sb.st_dev;
1194
1195         dsi->bsize = sfb.f_bsize;
1196         dsi->bneeded = 0;
1197         dsi->ineeded = 0;
1198 #ifdef STATFS_HAS_F_BAVAIL
1199         dsi->bavail = sfb.f_bavail;
1200 #else
1201 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
1202  * available for non-superusers.  f_blocks - f_bfree is probably too big, but
1203  * it's about all we can do.
1204  */
1205         dsi->bavail = sfb.f_blocks - sfb.f_bfree;
1206 #endif
1207         /* XXX Avoid FAT and other file systems that have not inodes. */
1208         /* XXX assigning negative value to unsigned type */
1209         dsi->iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
1210                                 ? sfb.f_ffree : -1;
1211         rpmlog(RPMLOG_DEBUG, "%5d 0x%08x %8u %12ld %12ld %s\n",
1212                 i, (unsigned) dsi->dev, (unsigned) dsi->bsize,
1213                 (signed long) dsi->bavail, (signed long) dsi->iavail,
1214                 ts->filesystems[i]);
1215     }
1216     return rc;
1217 }
1218
1219 void rpmtsUpdateDSI(const rpmts ts, dev_t dev,
1220                 rpm_off_t fileSize, rpm_off_t prevSize, rpm_off_t fixupSize,
1221                 rpmFileAction action)
1222 {
1223     rpmDiskSpaceInfo dsi;
1224     uint32_t bneeded;
1225
1226     dsi = ts->dsi;
1227     if (dsi) {
1228         while (dsi->bsize && dsi->dev != dev)
1229             dsi++;
1230         if (dsi->bsize == 0)
1231             dsi = NULL;
1232     }
1233     if (dsi == NULL)
1234         return;
1235
1236     bneeded = BLOCK_ROUND(fileSize, dsi->bsize);
1237
1238     switch (action) {
1239     case FA_BACKUP:
1240     case FA_SAVE:
1241     case FA_ALTNAME:
1242         dsi->ineeded++;
1243         dsi->bneeded += bneeded;
1244         break;
1245
1246     /*
1247      * FIXME: If two packages share a file (same md5sum), and
1248      * that file is being replaced on disk, will dsi->bneeded get
1249      * adjusted twice? Quite probably!
1250      */
1251     case FA_CREATE:
1252         dsi->bneeded += bneeded;
1253         dsi->bneeded -= BLOCK_ROUND(prevSize, dsi->bsize);
1254         break;
1255
1256     case FA_ERASE:
1257         dsi->ineeded--;
1258         dsi->bneeded -= bneeded;
1259         break;
1260
1261     default:
1262         break;
1263     }
1264
1265     if (fixupSize)
1266         dsi->bneeded -= BLOCK_ROUND(fixupSize, dsi->bsize);
1267 }
1268
1269 void rpmtsCheckDSIProblems(const rpmts ts, const rpmte te)
1270 {
1271     rpmDiskSpaceInfo dsi;
1272     rpmps ps;
1273     int fc;
1274     int i;
1275
1276     if (ts->filesystems == NULL || ts->filesystemCount <= 0)
1277         return;
1278
1279     dsi = ts->dsi;
1280     if (dsi == NULL)
1281         return;
1282     fc = rpmfiFC( rpmteFI(te, RPMTAG_BASENAMES) );
1283     if (fc <= 0)
1284         return;
1285
1286     ps = rpmtsProblems(ts);
1287     for (i = 0; i < ts->filesystemCount; i++, dsi++) {
1288
1289         if (dsi->bavail >= 0 && adj_fs_blocks(dsi->bneeded) > dsi->bavail) {
1290             rpmpsAppend(ps, RPMPROB_DISKSPACE,
1291                         rpmteNEVRA(te), rpmteKey(te),
1292                         ts->filesystems[i], NULL, NULL,
1293            (adj_fs_blocks(dsi->bneeded) - dsi->bavail) * dsi->bsize);
1294         }
1295
1296         if (dsi->iavail >= 0 && adj_fs_blocks(dsi->ineeded) > dsi->iavail) {
1297             rpmpsAppend(ps, RPMPROB_DISKNODES,
1298                         rpmteNEVRA(te), rpmteKey(te),
1299                         ts->filesystems[i], NULL, NULL,
1300             (adj_fs_blocks(dsi->ineeded) - dsi->iavail));
1301         }
1302     }
1303     ps = rpmpsFree(ps);
1304 }
1305
1306 void * rpmtsNotify(rpmts ts, rpmte te,
1307                 rpmCallbackType what, rpm_off_t amount, rpm_off_t total)
1308 {
1309     void * ptr = NULL;
1310     if (ts && ts->notify && te) {
1311         Header h = rpmteHeader(te);
1312 assert(!(rpmteType(te) == TR_ADDED && h == NULL));
1313         /* FIX: cast? */
1314         /* FIX: check rc */
1315         ptr = ts->notify(h, what, amount, total,
1316                         rpmteKey(te), ts->notifyData);
1317         headerUnlink(h); /* undo rpmteHeader() ref */
1318     }
1319     return ptr;
1320 }
1321
1322 int rpmtsNElements(rpmts ts)
1323 {
1324     int nelements = 0;
1325     if (ts != NULL && ts->order != NULL) {
1326         nelements = ts->orderCount;
1327     }
1328     return nelements;
1329 }
1330
1331 rpmte rpmtsElement(rpmts ts, int ix)
1332 {
1333     rpmte te = NULL;
1334     if (ts != NULL && ts->order != NULL) {
1335         if (ix >= 0 && ix < ts->orderCount)
1336             te = ts->order[ix];
1337     }
1338     return te;
1339 }
1340
1341 rpmprobFilterFlags rpmtsFilterFlags(rpmts ts)
1342 {
1343     return (ts != NULL ? ts->ignoreSet : 0);
1344 }
1345
1346 rpmtransFlags rpmtsFlags(rpmts ts)
1347 {
1348     return (ts != NULL ? ts->transFlags : 0);
1349 }
1350
1351 rpmtransFlags rpmtsSetFlags(rpmts ts, rpmtransFlags transFlags)
1352 {
1353     rpmtransFlags otransFlags = 0;
1354     if (ts != NULL) {
1355         otransFlags = ts->transFlags;
1356         ts->transFlags = transFlags;
1357     }
1358     return otransFlags;
1359 }
1360
1361 rpmSpec rpmtsSpec(rpmts ts)
1362 {
1363     return ts->spec;
1364 }
1365
1366 rpmSpec rpmtsSetSpec(rpmts ts, rpmSpec spec)
1367 {
1368     rpmSpec ospec = ts->spec;
1369     ts->spec = spec;
1370     return ospec;
1371 }
1372
1373 rpmte rpmtsRelocateElement(rpmts ts)
1374 {
1375     return ts->relocateElement;
1376 }
1377
1378 rpmte rpmtsSetRelocateElement(rpmts ts, rpmte relocateElement)
1379 {
1380     rpmte orelocateElement = ts->relocateElement;
1381     ts->relocateElement = relocateElement;
1382     return orelocateElement;
1383 }
1384
1385 rpm_color_t rpmtsColor(rpmts ts)
1386 {
1387     return (ts != NULL ? ts->color : 0);
1388 }
1389
1390 rpm_color_t rpmtsSetColor(rpmts ts, rpm_color_t color)
1391 {
1392     rpm_color_t ocolor = 0;
1393     if (ts != NULL) {
1394         ocolor = ts->color;
1395         ts->color = color;
1396     }
1397     return ocolor;
1398 }
1399
1400 rpm_color_t rpmtsPrefColor(rpmts ts)
1401 {
1402     return (ts != NULL ? ts->prefcolor : 0);
1403 }
1404
1405 rpmop rpmtsOp(rpmts ts, rpmtsOpX opx)
1406 {
1407     rpmop op = NULL;
1408
1409     if (ts != NULL && opx >= 0 && opx < RPMTS_OP_MAX)
1410         op = ts->ops + opx;
1411     return op;
1412 }
1413
1414 int rpmtsSetNotifyCallback(rpmts ts,
1415                 rpmCallbackFunction notify, rpmCallbackData notifyData)
1416 {
1417     if (ts != NULL) {
1418         ts->notify = notify;
1419         ts->notifyData = notifyData;
1420     }
1421     return 0;
1422 }
1423
1424 int rpmtsGetKeys(const rpmts ts, fnpyKey ** ep, int * nep)
1425 {
1426     int rc = 0;
1427
1428     if (nep) *nep = ts->orderCount;
1429     if (ep) {
1430         rpmtsi pi;      rpmte p;
1431         fnpyKey * e;
1432
1433         *ep = e = xmalloc(ts->orderCount * sizeof(*e));
1434         pi = rpmtsiInit(ts);
1435         while ((p = rpmtsiNext(pi, 0)) != NULL) {
1436             switch (rpmteType(p)) {
1437             case TR_ADDED:
1438                 *e = rpmteKey(p);
1439                 break;
1440             case TR_REMOVED:
1441             default:
1442                 *e = NULL;
1443                 break;
1444             }
1445             e++;
1446         }
1447         pi = rpmtsiFree(pi);
1448     }
1449     return rc;
1450 }
1451
1452 rpmts rpmtsCreate(void)
1453 {
1454     rpmts ts;
1455
1456     ts = xcalloc(1, sizeof(*ts));
1457     memset(&ts->ops, 0, sizeof(ts->ops));
1458     (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_TOTAL), -1);
1459     ts->filesystemCount = 0;
1460     ts->filesystems = NULL;
1461     ts->dsi = NULL;
1462
1463     ts->solve = rpmtsSolve;
1464     ts->solveData = NULL;
1465     ts->nsuggests = 0;
1466     ts->suggests = NULL;
1467     ts->sdb = NULL;
1468     ts->sdbmode = O_RDONLY;
1469
1470     ts->rdb = NULL;
1471     ts->dbmode = O_RDONLY;
1472
1473     ts->scriptFd = NULL;
1474     ts->tid = (rpm_tid_t) time(NULL);
1475     ts->delta = 5;
1476
1477     ts->color = rpmExpandNumeric("%{?_transaction_color}");
1478     ts->prefcolor = rpmExpandNumeric("%{?_prefer_color}")?:2;
1479
1480     ts->numRemovedPackages = 0;
1481     ts->allocedRemovedPackages = ts->delta;
1482     ts->removedPackages = xcalloc(ts->allocedRemovedPackages,
1483                         sizeof(*ts->removedPackages));
1484
1485     ts->rootDir = NULL;
1486     ts->currDir = NULL;
1487     ts->chrootDone = 0;
1488
1489     ts->selinuxEnabled = is_selinux_enabled();
1490
1491     ts->numAddedPackages = 0;
1492     ts->addedPackages = NULL;
1493
1494     ts->numAvailablePackages = 0;
1495     ts->availablePackages = NULL;
1496
1497     ts->orderAlloced = 0;
1498     ts->orderCount = 0;
1499     ts->order = NULL;
1500     ts->ntrees = 0;
1501     ts->maxDepth = 0;
1502
1503     ts->probs = NULL;
1504
1505     ts->sig = NULL;
1506     ts->pkpkt = NULL;
1507     ts->pkpktlen = 0;
1508     memset(ts->pksignid, 0, sizeof(ts->pksignid));
1509     ts->dig = NULL;
1510
1511     ts->nrefs = 0;
1512
1513     return rpmtsLink(ts, RPMDBG_M("tsCreate"));
1514 }
1515