rpmlib.h mass eviction
[platform/upstream/rpm.git] / lib / rpmts.c
1 /** \ingroup rpmdep
2  * \file lib/rpmts.c
3  * Routine(s) to handle a "rpmts" transaction sets.
4  */
5 #include "system.h"
6
7 #include <rpm/rpmtag.h>
8 #include <rpm/rpmlib.h>                 /* rpmReadPackage etc */
9 #include <rpm/rpmurl.h>
10 #include "rpmio/digest.h"
11 #include <rpm/rpmmacro.h>
12 #include <rpm/rpmfileutil.h>            /* rpmtsOpenDB() needs rpmGetPath */
13 #include <rpm/rpmstring.h>
14
15 #include <rpm/rpmdb.h>
16
17 #include <rpm/rpmal.h>
18 #include <rpm/rpmds.h>
19 #include <rpm/rpmfi.h>
20 #include "lib/rpmlock.h"
21 #include <rpm/rpmlog.h>
22
23 #include <rpm/rpmte.h>
24
25 #include "lib/rpmts_internal.h"
26
27 /* XXX FIXME: merge with existing (broken?) tests in system.h */
28 /* portability fiddles */
29 #if STATFS_IN_SYS_STATVFS
30 #include <sys/statvfs.h>
31
32 #else
33 # if STATFS_IN_SYS_VFS
34 #  include <sys/vfs.h>
35 # else
36 #  if STATFS_IN_SYS_MOUNT
37 #   include <sys/mount.h>
38 #  else
39 #   if STATFS_IN_SYS_STATFS
40 #    include <sys/statfs.h>
41 #   endif
42 #  endif
43 # endif
44 #endif
45
46 #include "debug.h"
47
48 int _rpmts_debug = 0;
49
50 int _rpmts_stats = 0;
51
52 rpmts rpmtsUnlink(rpmts ts, const char * msg)
53 {
54 if (_rpmts_debug)
55 fprintf(stderr, "--> ts %p -- %d %s\n", ts, ts->nrefs, msg);
56     ts->nrefs--;
57     return NULL;
58 }
59
60 rpmts rpmtsLink(rpmts ts, const char * msg)
61 {
62     ts->nrefs++;
63 if (_rpmts_debug)
64 fprintf(stderr, "--> ts %p ++ %d %s\n", ts, ts->nrefs, msg);
65     return ts;
66 }
67
68 int rpmtsCloseDB(rpmts ts)
69 {
70     int rc = 0;
71
72     if (ts->rdb != NULL) {
73         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBGET), 
74                         rpmdbOp(ts->rdb, RPMDB_OP_DBGET));
75         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBPUT),
76                         rpmdbOp(ts->rdb, RPMDB_OP_DBPUT));
77         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBDEL),
78                         rpmdbOp(ts->rdb, RPMDB_OP_DBDEL));
79         rc = rpmdbClose(ts->rdb);
80         ts->rdb = NULL;
81     }
82     return rc;
83 }
84
85 int rpmtsOpenDB(rpmts ts, int dbmode)
86 {
87     int rc = 0;
88
89     if (ts->rdb != NULL && ts->dbmode == dbmode)
90         return 0;
91
92     (void) rpmtsCloseDB(ts);
93
94     /* XXX there's a potential db lock race here. */
95
96     ts->dbmode = dbmode;
97     rc = rpmdbOpen(ts->rootDir, &ts->rdb, ts->dbmode, 0644);
98     if (rc) {
99         char * 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", "geode",
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, rpm_tag_t 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     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             rpm_tagtype_t pt;
302             rpm_count_t pc;
303
304             if (!headerGetEntry(h, RPMTAG_PUBKEYS, &pt, (rpm_data_t *)&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         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, size_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     char * d = NULL;
415     char * enc = NULL;
416     char * n = NULL;
417     char * u = NULL;
418     char * v = NULL;
419     char * r = NULL;
420     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         char * dn = rpmGetPath(ts->rootDir, "%{_dbpath}", NULL);
571         rpmlog(RPMLOG_WARNING,
572                         _("cannot open Solve database in %s\n"), dn);
573         dn = _free(dn);
574     }
575     delMacro(NULL, "_dbpath");
576
577     return rc;
578 }
579
580 /**
581  * Compare suggested package resolutions (qsort/bsearch).
582  * @param a             1st instance address
583  * @param b             2nd instance address
584  * @return              result of comparison
585  */
586 static int sugcmp(const void * a, const void * b)
587 {
588     const char * astr = *(const char **)a;
589     const char * bstr = *(const char **)b;
590     return strcmp(astr, bstr);
591 }
592
593 int rpmtsSolve(rpmts ts, rpmds ds, const void * data)
594 {
595     const char * errstr;
596     char * str;
597     char * qfmt;
598     rpmdbMatchIterator mi;
599     Header bh;
600     Header h;
601     size_t bhnamelen;
602     time_t bhtime;
603     rpm_tag_t rpmtag;
604     const char * keyp;
605     size_t keylen;
606     int rc = 1; /* assume not found */
607     int xx;
608
609     if (rpmdsTagN(ds) != RPMTAG_REQUIRENAME)
610         return rc;
611
612     keyp = rpmdsN(ds);
613     if (keyp == NULL)
614         return rc;
615
616     if (ts->sdb == NULL) {
617         xx = rpmtsOpenSDB(ts, ts->sdbmode);
618         if (xx) return rc;
619     }
620
621     /* Look for a matching Provides: in suggested universe. */
622     rpmtag = (*keyp == '/' ? RPMTAG_BASENAMES : RPMTAG_PROVIDENAME);
623     keylen = 0;
624     mi = rpmdbInitIterator(ts->sdb, rpmtag, keyp, keylen);
625     bhnamelen = 0;
626     bhtime = 0;
627     bh = NULL;
628     while ((h = rpmdbNextIterator(mi)) != NULL) {
629         const char * hname;
630         size_t hnamelen;
631         time_t htime;
632         int32_t * ip;
633
634         if (rpmtag == RPMTAG_PROVIDENAME && !rpmdsAnyMatchesDep(h, ds, 1))
635             continue;
636
637         /* XXX Prefer the shortest name if given alternatives. */
638         hname = NULL;
639         hnamelen = 0;
640         if (headerGetEntry(h, RPMTAG_NAME, NULL, (rpm_data_t *)&hname, NULL)) {
641             if (hname)
642                 hnamelen = strlen(hname);
643         }
644         if (bhnamelen > 0 && hnamelen > bhnamelen)
645             continue;
646
647         /* XXX Prefer the newest build if given alternatives. */
648         htime = 0;
649         if (headerGetEntry(h, RPMTAG_BUILDTIME, NULL, (rpm_data_t *)&ip, NULL))
650             htime = (time_t)*ip;
651
652         if (htime <= bhtime)
653             continue;
654
655         bh = headerFree(bh);
656         bh = headerLink(h);
657         bhtime = htime;
658         bhnamelen = hnamelen;
659     }
660     mi = rpmdbFreeIterator(mi);
661
662     /* Is there a suggested resolution? */
663     if (bh == NULL)
664         goto exit;
665
666     /* Format the suggestion. */
667     qfmt = rpmExpand("%{?_solve_name_fmt}", NULL);
668     if (qfmt == NULL || *qfmt == '\0')
669         goto exit;
670     str = headerSprintf(bh, qfmt, rpmTagTable, rpmHeaderFormats, &errstr);
671     bh = headerFree(bh);
672     qfmt = _free(qfmt);
673     if (str == NULL) {
674         rpmlog(RPMLOG_ERR, _("incorrect format: %s\n"), errstr);
675         goto exit;
676     }
677
678     if (ts->transFlags & RPMTRANS_FLAG_ADDINDEPS) {
679         FD_t fd;
680         rpmRC rpmrc;
681
682         h = headerFree(h);
683         fd = Fopen(str, "r.ufdio");
684         if (fd == NULL || Ferror(fd)) {
685             rpmlog(RPMLOG_ERR, _("open of %s failed: %s\n"), str,
686                         Fstrerror(fd));
687             if (fd != NULL) {
688                 xx = Fclose(fd);
689                 fd = NULL;
690             }
691             str = _free(str);
692             goto exit;
693         }
694         rpmrc = rpmReadPackageFile(ts, fd, str, &h);
695         xx = Fclose(fd);
696         switch (rpmrc) {
697         default:
698             str = _free(str);
699             break;
700         case RPMRC_NOTTRUSTED:
701         case RPMRC_NOKEY:
702         case RPMRC_OK:
703             if (h != NULL &&
704                 !rpmtsAddInstallElement(ts, h, (fnpyKey)str, 1, NULL))
705             {
706                 rpmlog(RPMLOG_DEBUG, _("Adding: %s\n"), str);
707                 rc = -1;
708                 /* XXX str memory leak */
709                 break;
710             }
711             str = _free(str);
712             break;
713         }
714         h = headerFree(h);
715         goto exit;
716     }
717
718     rpmlog(RPMLOG_DEBUG, _("Suggesting: %s\n"), str);
719     /* If suggestion is already present, don't bother. */
720     if (ts->suggests != NULL && ts->nsuggests > 0) {
721         if (bsearch(&str, ts->suggests, ts->nsuggests,
722                         sizeof(*ts->suggests), sugcmp))
723             goto exit;
724     }
725
726     /* Add a new (unique) suggestion. */
727     ts->suggests = xrealloc(ts->suggests,
728                         sizeof(*ts->suggests) * (ts->nsuggests + 2));
729     ts->suggests[ts->nsuggests] = str;
730     ts->nsuggests++;
731     ts->suggests[ts->nsuggests] = NULL;
732
733     if (ts->nsuggests > 1)
734         qsort(ts->suggests, ts->nsuggests, sizeof(*ts->suggests), sugcmp);
735
736 exit:
737 /* FIX: ts->suggests[] may be NULL */
738     return rc;
739 }
740
741 int rpmtsAvailable(rpmts ts, const rpmds ds)
742 {
743     fnpyKey * sugkey;
744     int rc = 1; /* assume not found */
745
746     if (ts->availablePackages == NULL)
747         return rc;
748     sugkey = rpmalAllSatisfiesDepend(ts->availablePackages, ds, NULL);
749     if (sugkey == NULL)
750         return rc;
751
752     /* XXX no alternatives yet */
753     if (sugkey[0] != NULL) {
754         ts->suggests = xrealloc(ts->suggests,
755                         sizeof(*ts->suggests) * (ts->nsuggests + 2));
756         ts->suggests[ts->nsuggests] = sugkey[0];
757         sugkey[0] = NULL;
758         ts->nsuggests++;
759         ts->suggests[ts->nsuggests] = NULL;
760     }
761     sugkey = _free(sugkey);
762 /* FIX: ts->suggests[] may be NULL */
763     return rc;
764 }
765
766 int rpmtsSetSolveCallback(rpmts ts,
767                 int (*solve) (rpmts ts, rpmds key, const void * data),
768                 const void * solveData)
769 {
770     int rc = 0;
771
772     if (ts) {
773         ts->solve = solve;
774         ts->solveData = solveData;
775     }
776     return rc;
777 }
778
779 void rpmtsPrintSuggests(rpmts ts)
780 {
781     if (ts->suggests != NULL && ts->nsuggests > 0) {
782         int i;
783         rpmlog(RPMLOG_NOTICE, _("    Suggested resolutions:\n"));
784         for (i = 0; i < ts->nsuggests; i++) {
785             const char * str = ts->suggests[i];
786
787             if (str == NULL)
788                 break;
789
790             rpmlog(RPMLOG_NOTICE, "\t%s\n", str);
791         }
792     }
793 }
794
795
796 rpmps rpmtsProblems(rpmts ts)
797 {
798     rpmps ps = NULL;
799     if (ts) {
800         if (ts->probs)
801             ps = rpmpsLink(ts->probs, RPMDBG_M("rpmtsProblems"));
802     }
803     return ps;
804 }
805
806 void rpmtsCleanProblems(rpmts ts)
807 {
808     if (ts && ts->probs) {
809         ts->probs = rpmpsFree(ts->probs);
810     }
811 }
812
813 void rpmtsCleanDig(rpmts ts)
814 {
815     ts->sig = headerFreeData(ts->sig, ts->sigtype);
816     ts->dig = pgpFreeDig(ts->dig);
817 }
818
819 void rpmtsClean(rpmts ts)
820 {
821     rpmtsi pi; rpmte p;
822     int i;
823
824     if (ts == NULL)
825         return;
826
827     /* Clean up after dependency checks. */
828     pi = rpmtsiInit(ts);
829     while ((p = rpmtsiNext(pi, 0)) != NULL)
830         rpmteCleanDS(p);
831     pi = rpmtsiFree(pi);
832
833     ts->addedPackages = rpmalFree(ts->addedPackages);
834     ts->numAddedPackages = 0;
835
836     for (i = 0; i < ts->nsuggests; i++) {
837         const char * str = ts->suggests[i];
838         ts->suggests[i] = NULL;
839         _constfree(str);
840     }
841     ts->suggests = _free(ts->suggests);
842     ts->nsuggests = 0;
843
844     rpmtsCleanProblems(ts);
845
846     rpmtsCleanDig(ts);
847 }
848
849 void rpmtsEmpty(rpmts ts)
850 {
851     rpmtsi pi; rpmte p;
852     int oc;
853
854     if (ts == NULL)
855         return;
856
857     rpmtsClean(ts);
858
859     for (pi = rpmtsiInit(ts), oc = 0; (p = rpmtsiNext(pi, 0)) != NULL; oc++) {
860         ts->order[oc] = rpmteFree(ts->order[oc]);
861     }
862     pi = rpmtsiFree(pi);
863
864     ts->orderCount = 0;
865     ts->ntrees = 0;
866     ts->maxDepth = 0;
867
868     ts->numRemovedPackages = 0;
869     return;
870 }
871
872 static void rpmtsPrintStat(const char * name, struct rpmop_s * op)
873 {
874     static unsigned int scale = (1000 * 1000);
875     if (op != NULL && op->count > 0)
876         fprintf(stderr, "   %s %6d %6lu.%06lu MB %6lu.%06lu secs\n",
877                 name, op->count,
878                 (unsigned long)op->bytes/scale, (unsigned long)op->bytes%scale,
879                 op->usecs/scale, op->usecs%scale);
880 }
881
882 static void rpmtsPrintStats(rpmts ts)
883 {
884     (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_TOTAL), 0);
885
886     rpmtsPrintStat("total:       ", rpmtsOp(ts, RPMTS_OP_TOTAL));
887     rpmtsPrintStat("check:       ", rpmtsOp(ts, RPMTS_OP_CHECK));
888     rpmtsPrintStat("order:       ", rpmtsOp(ts, RPMTS_OP_ORDER));
889     rpmtsPrintStat("fingerprint: ", rpmtsOp(ts, RPMTS_OP_FINGERPRINT));
890     rpmtsPrintStat("repackage:   ", rpmtsOp(ts, RPMTS_OP_REPACKAGE));
891     rpmtsPrintStat("install:     ", rpmtsOp(ts, RPMTS_OP_INSTALL));
892     rpmtsPrintStat("erase:       ", rpmtsOp(ts, RPMTS_OP_ERASE));
893     rpmtsPrintStat("scriptlets:  ", rpmtsOp(ts, RPMTS_OP_SCRIPTLETS));
894     rpmtsPrintStat("compress:    ", rpmtsOp(ts, RPMTS_OP_COMPRESS));
895     rpmtsPrintStat("uncompress:  ", rpmtsOp(ts, RPMTS_OP_UNCOMPRESS));
896     rpmtsPrintStat("digest:      ", rpmtsOp(ts, RPMTS_OP_DIGEST));
897     rpmtsPrintStat("signature:   ", rpmtsOp(ts, RPMTS_OP_SIGNATURE));
898     rpmtsPrintStat("dbadd:       ", rpmtsOp(ts, RPMTS_OP_DBADD));
899     rpmtsPrintStat("dbremove:    ", rpmtsOp(ts, RPMTS_OP_DBREMOVE));
900     rpmtsPrintStat("dbget:       ", rpmtsOp(ts, RPMTS_OP_DBGET));
901     rpmtsPrintStat("dbput:       ", rpmtsOp(ts, RPMTS_OP_DBPUT));
902     rpmtsPrintStat("dbdel:       ", rpmtsOp(ts, RPMTS_OP_DBDEL));
903 }
904
905 rpmts rpmtsFree(rpmts ts)
906 {
907     if (ts == NULL)
908         return NULL;
909
910     if (ts->nrefs > 1)
911         return rpmtsUnlink(ts, RPMDBG_M("tsCreate"));
912
913     rpmtsEmpty(ts);
914
915     (void) rpmtsCloseDB(ts);
916
917     (void) rpmtsCloseSDB(ts);
918
919     ts->removedPackages = _free(ts->removedPackages);
920
921     ts->availablePackages = rpmalFree(ts->availablePackages);
922     ts->numAvailablePackages = 0;
923
924     ts->dsi = _free(ts->dsi);
925
926     if (ts->scriptFd != NULL) {
927         ts->scriptFd = fdFree(ts->scriptFd, RPMDBG_M("rpmtsFree"));
928         ts->scriptFd = NULL;
929     }
930     ts->rootDir = _free(ts->rootDir);
931     ts->currDir = _free(ts->currDir);
932
933     ts->order = _free(ts->order);
934     ts->orderAlloced = 0;
935
936     if (ts->pkpkt != NULL)
937         ts->pkpkt = _free(ts->pkpkt);
938     ts->pkpktlen = 0;
939     memset(ts->pksignid, 0, sizeof(ts->pksignid));
940
941     if (_rpmts_stats)
942         rpmtsPrintStats(ts);
943
944     /* Free up the memory used by the rpmtsScore */
945     ts->score = rpmtsScoreFree(ts->score);
946
947     (void) rpmtsUnlink(ts, RPMDBG_M("tsCreate"));
948
949     ts = _free(ts);
950
951     return NULL;
952 }
953
954 rpmVSFlags rpmtsVSFlags(rpmts ts)
955 {
956     rpmVSFlags vsflags = 0;
957     if (ts != NULL)
958         vsflags = ts->vsflags;
959     return vsflags;
960 }
961
962 rpmVSFlags rpmtsSetVSFlags(rpmts ts, rpmVSFlags vsflags)
963 {
964     rpmVSFlags ovsflags = 0;
965     if (ts != NULL) {
966         ovsflags = ts->vsflags;
967         ts->vsflags = vsflags;
968     }
969     return ovsflags;
970 }
971
972 /* 
973  * This allows us to mark transactions as being of a certain type.
974  * The three types are:
975  * 
976  *     RPM_TRANS_NORMAL         
977  *     RPM_TRANS_ROLLBACK
978  *     RPM_TRANS_AUTOROLLBACK
979  * 
980  * ROLLBACK and AUTOROLLBACK transactions should always be ran as
981  * a best effort.  In particular this is important to the autorollback 
982  * feature to avoid rolling back a rollback (otherwise known as 
983  * dueling rollbacks (-;).  AUTOROLLBACK's additionally need instance 
984  * counts passed to scriptlets to be altered.
985  */
986 void rpmtsSetType(rpmts ts, rpmtsType type)
987 {
988     if (ts != NULL) {
989         ts->type = type;
990     }    
991 }
992
993 /* Let them know what type of transaction we are */
994 rpmtsType rpmtsGetType(rpmts ts) 
995 {
996     if (ts != NULL) 
997         return ts->type;
998     else
999         return 0;
1000 }
1001
1002 int rpmtsUnorderedSuccessors(rpmts ts, int first)
1003 {
1004     int unorderedSuccessors = 0;
1005     if (ts != NULL) {
1006         unorderedSuccessors = ts->unorderedSuccessors;
1007         if (first >= 0)
1008             ts->unorderedSuccessors = first;
1009     }
1010     return unorderedSuccessors;
1011 }
1012
1013 const char * rpmtsRootDir(rpmts ts)
1014 {
1015     const char * rootDir = NULL;
1016
1017     if (ts != NULL && ts->rootDir != NULL) {
1018         urltype ut = urlPath(ts->rootDir, &rootDir);
1019         switch (ut) {
1020         case URL_IS_UNKNOWN:
1021         case URL_IS_PATH:
1022             break;
1023         /* XXX these shouldn't be allowed as rootdir! */
1024         case URL_IS_HTTPS:
1025         case URL_IS_HTTP:
1026         case URL_IS_HKP:
1027         case URL_IS_FTP:
1028         case URL_IS_DASH:
1029         default:
1030             rootDir = "/";
1031             break;
1032         }
1033     }
1034     return rootDir;
1035 }
1036
1037 void rpmtsSetRootDir(rpmts ts, const char * rootDir)
1038 {
1039     if (ts != NULL) {
1040         size_t rootLen;
1041
1042         ts->rootDir = _free(ts->rootDir);
1043
1044         if (rootDir == NULL) {
1045 #ifndef DYING
1046             ts->rootDir = xstrdup("");
1047 #endif
1048             return;
1049         }
1050         rootLen = strlen(rootDir);
1051
1052         /* Make sure that rootDir has trailing / */
1053         if (!(rootLen && rootDir[rootLen - 1] == '/')) {
1054             char * t = alloca(rootLen + 2);
1055             *t = '\0';
1056             (void) stpcpy( stpcpy(t, rootDir), "/");
1057             rootDir = t;
1058         }
1059         ts->rootDir = xstrdup(rootDir);
1060     }
1061 }
1062
1063 const char * rpmtsCurrDir(rpmts ts)
1064 {
1065     const char * currDir = NULL;
1066     if (ts != NULL) {
1067         currDir = ts->currDir;
1068     }
1069     return currDir;
1070 }
1071
1072 void rpmtsSetCurrDir(rpmts ts, const char * currDir)
1073 {
1074     if (ts != NULL) {
1075         ts->currDir = _free(ts->currDir);
1076         if (currDir)
1077             ts->currDir = xstrdup(currDir);
1078     }
1079 }
1080
1081 FD_t rpmtsScriptFd(rpmts ts)
1082 {
1083     FD_t scriptFd = NULL;
1084     if (ts != NULL) {
1085         scriptFd = ts->scriptFd;
1086     }
1087     return scriptFd;
1088 }
1089
1090 void rpmtsSetScriptFd(rpmts ts, FD_t scriptFd)
1091 {
1092
1093     if (ts != NULL) {
1094         if (ts->scriptFd != NULL) {
1095             ts->scriptFd = fdFree(ts->scriptFd, 
1096                                   RPMDBG_M("rpmtsSetScriptFd"));
1097             ts->scriptFd = NULL;
1098         }
1099         if (scriptFd != NULL)
1100             ts->scriptFd = fdLink((void *)scriptFd, 
1101                                   RPMDBG_M("rpmtsSetScriptFd"));
1102     }
1103 }
1104
1105 int rpmtsSELinuxEnabled(rpmts ts)
1106 {
1107     return (ts != NULL ? (ts->selinuxEnabled > 0) : 0);
1108 }
1109
1110 int rpmtsChrootDone(rpmts ts)
1111 {
1112     return (ts != NULL ? ts->chrootDone : 0);
1113 }
1114
1115 int rpmtsSetChrootDone(rpmts ts, int chrootDone)
1116 {
1117     int ochrootDone = 0;
1118     if (ts != NULL) {
1119         ochrootDone = ts->chrootDone;
1120         rpmdbSetChrootDone(rpmtsGetRdb(ts), chrootDone);
1121         ts->chrootDone = chrootDone;
1122     }
1123     return ochrootDone;
1124 }
1125
1126 int32_t rpmtsGetTid(rpmts ts)
1127 {
1128     int32_t tid = -1;  /* XXX -1 is time(2) error return. */
1129     if (ts != NULL) {
1130         tid = ts->tid;
1131     }
1132     return tid;
1133 }
1134
1135 int32_t rpmtsSetTid(rpmts ts, int32_t tid)
1136 {
1137     int32_t otid = -1; /* XXX -1 is time(2) error return. */
1138     if (ts != NULL) {
1139         otid = ts->tid;
1140         ts->tid = tid;
1141     }
1142     return otid;
1143 }
1144
1145 rpm_tag_t rpmtsSigtag(const rpmts ts)
1146 {
1147     rpm_tag_t sigtag = 0;
1148     if (ts != NULL)
1149         sigtag = ts->sigtag;
1150     return sigtag;
1151 }
1152
1153 rpm_tagtype_t rpmtsSigtype(const rpmts ts)
1154 {
1155     rpm_tagtype_t sigtype = 0;
1156     if (ts != NULL)
1157         sigtype = ts->sigtype;
1158     return sigtype;
1159 }
1160
1161 rpm_constdata_t rpmtsSig(const rpmts ts)
1162 {
1163     rpm_constdata_t sig = NULL;
1164     if (ts != NULL)
1165         sig = ts->sig;
1166     return sig;
1167 }
1168
1169 size_t rpmtsSiglen(const rpmts ts)
1170 {
1171     size_t siglen = 0;
1172     if (ts != NULL)
1173         siglen = ts->siglen;
1174     return siglen;
1175 }
1176
1177 int rpmtsSetSig(rpmts ts, rpm_tag_t sigtag, rpm_tagtype_t sigtype, 
1178                 rpm_data_t sig, size_t siglen)
1179 {
1180     if (ts != NULL) {
1181         if (ts->sig && ts->sigtype)
1182             ts->sig = headerFreeData(ts->sig, ts->sigtype);
1183         ts->sigtag = sigtag;
1184         ts->sigtype = (sig ? sigtype : 0);
1185         ts->sig = sig;
1186         ts->siglen = siglen;
1187     }
1188     return 0;
1189 }
1190
1191 pgpDig rpmtsDig(rpmts ts)
1192 {
1193 /* FIX: hide lazy malloc for now */
1194     if (ts->dig == NULL)
1195         ts->dig = pgpNewDig();
1196     if (ts->dig == NULL)
1197         return NULL;
1198     return ts->dig;
1199 }
1200
1201 pgpDigParams rpmtsSignature(const rpmts ts)
1202 {
1203     pgpDig dig = rpmtsDig(ts);
1204     if (dig == NULL) return NULL;
1205     return &dig->signature;
1206 }
1207
1208 pgpDigParams rpmtsPubkey(const rpmts ts)
1209 {
1210     pgpDig dig = rpmtsDig(ts);
1211     if (dig == NULL) return NULL;
1212     return &dig->pubkey;
1213 }
1214
1215 rpmdb rpmtsGetRdb(rpmts ts)
1216 {
1217     rpmdb rdb = NULL;
1218     if (ts != NULL) {
1219         rdb = ts->rdb;
1220     }
1221     return rdb;
1222 }
1223
1224 int rpmtsInitDSI(const rpmts ts)
1225 {
1226     rpmDiskSpaceInfo dsi;
1227     struct stat sb;
1228     int rc;
1229     int i;
1230
1231     if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_DISKSPACE)
1232         return 0;
1233
1234     rpmlog(RPMLOG_DEBUG, _("mounted filesystems:\n"));
1235     rpmlog(RPMLOG_DEBUG,
1236         _("    i        dev    bsize       bavail       iavail mount point\n"));
1237
1238     rc = rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount);
1239     if (rc || ts->filesystems == NULL || ts->filesystemCount <= 0)
1240         return rc;
1241
1242     /* Get available space on mounted file systems. */
1243
1244     ts->dsi = _free(ts->dsi);
1245     ts->dsi = xcalloc((ts->filesystemCount + 1), sizeof(*ts->dsi));
1246
1247     dsi = ts->dsi;
1248
1249     if (dsi != NULL)
1250     for (i = 0; (i < ts->filesystemCount) && dsi; i++, dsi++) {
1251 #if STATFS_IN_SYS_STATVFS
1252         struct statvfs sfb;
1253         memset(&sfb, 0, sizeof(sfb));
1254         rc = statvfs(ts->filesystems[i], &sfb);
1255 #else
1256         struct statfs sfb;
1257         memset(&sfb, 0, sizeof(sfb));
1258 #  if STAT_STATFS4
1259 /* This platform has the 4-argument version of the statfs call.  The last two
1260  * should be the size of struct statfs and 0, respectively.  The 0 is the
1261  * filesystem type, and is always 0 when statfs is called on a mounted
1262  * filesystem, as we're doing.
1263  */
1264         rc = statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0);
1265 #  else
1266         rc = statfs(ts->filesystems[i], &sfb);
1267 #  endif
1268 #endif
1269         if (rc)
1270             break;
1271
1272         rc = stat(ts->filesystems[i], &sb);
1273         if (rc)
1274             break;
1275         dsi->dev = sb.st_dev;
1276
1277         dsi->bsize = sfb.f_bsize;
1278         dsi->bneeded = 0;
1279         dsi->ineeded = 0;
1280 #ifdef STATFS_HAS_F_BAVAIL
1281         dsi->bavail = sfb.f_bavail;
1282 #else
1283 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
1284  * available for non-superusers.  f_blocks - f_bfree is probably too big, but
1285  * it's about all we can do.
1286  */
1287         dsi->bavail = sfb.f_blocks - sfb.f_bfree;
1288 #endif
1289         /* XXX Avoid FAT and other file systems that have not inodes. */
1290         /* XXX assigning negative value to unsigned type */
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 }