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