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