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