fix: prevent segfault if malicious server sends 1 GB of data through ftpNLST.
[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 "rpmio_internal.h"     /* XXX for pgp and beecrypt */
8 #include <rpmlib.h>
9 #include <rpmmacro.h>           /* XXX rpmtsOpenDB() needs rpmGetPath */
10
11 #include "rpmdb.h"              /* XXX stealing db->db_mode. */
12
13 #include "rpmal.h"
14 #include "rpmds.h"
15 #include "rpmfi.h"
16
17 #define _RPMTE_INTERNAL         /* XXX te->h */
18 #include "rpmte.h"
19
20 #define _RPMTS_INTERNAL
21 #include "rpmts.h"
22
23 /* XXX FIXME: merge with existing (broken?) tests in system.h */
24 /* portability fiddles */
25 #if STATFS_IN_SYS_STATVFS
26 /*@-incondefs@*/
27 #if defined(__LCLINT__)
28 /*@-declundef -exportheader -protoparammatch @*/ /* LCL: missing annotation */
29 extern int statvfs (const char * file, /*@out@*/ struct statvfs * buf)
30         /*@globals fileSystem @*/
31         /*@modifies *buf, fileSystem @*/;
32 /*@=declundef =exportheader =protoparammatch @*/
33 /*@=incondefs@*/
34 #else
35 # include <sys/statvfs.h>
36 #endif
37 #else
38 # if STATFS_IN_SYS_VFS
39 #  include <sys/vfs.h>
40 # else
41 #  if STATFS_IN_SYS_MOUNT
42 #   include <sys/mount.h>
43 #  else
44 #   if STATFS_IN_SYS_STATFS
45 #    include <sys/statfs.h>
46 #   endif
47 #  endif
48 # endif
49 #endif
50
51 #include "debug.h"
52
53 /*@access rpmdb @*/             /* XXX db->db_chrootDone, NULL */
54
55 /*@access rpmps @*/
56 /*@access rpmDiskSpaceInfo @*/
57 /*@access rpmte @*/
58 /*@access rpmtsi @*/
59 /*@access fnpyKey @*/
60 /*@access pgpDig @*/
61 /*@access pgpDigParams @*/
62
63 /*@unchecked@*/
64 int _rpmts_debug = 0;
65
66 /*@unchecked@*/
67 int _rpmts_stats = 0;
68
69 char * hGetNEVR(Header h, const char ** np)
70 {
71     const char * n, * v, * r;
72     char * NVR, * t;
73
74     (void) headerNVR(h, &n, &v, &r);
75     NVR = t = xcalloc(1, strlen(n) + strlen(v) + strlen(r) + sizeof("--"));
76 /*@-boundswrite@*/
77     t = stpcpy(t, n);
78     t = stpcpy(t, "-");
79     t = stpcpy(t, v);
80     t = stpcpy(t, "-");
81     t = stpcpy(t, r);
82     if (np)
83         *np = n;
84 /*@=boundswrite@*/
85     return NVR;
86 }
87
88 uint_32 hGetColor(Header h)
89 {
90     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
91     uint_32 hcolor = 0;
92     uint_32 * fcolors;
93     int_32 ncolors;
94     int i;
95
96     fcolors = NULL;
97     ncolors = 0;
98     if (hge(h, RPMTAG_FILECOLORS, NULL, (void **)&fcolors, &ncolors)
99      && fcolors != NULL && ncolors > 0)
100     {
101 /*@-boundsread@*/
102         for (i = 0; i < ncolors; i++)
103             hcolor |= fcolors[i];
104 /*@=boundsread@*/
105     }
106     hcolor &= 0x0f;
107
108     return hcolor;
109 }
110
111 rpmts XrpmtsUnlink(rpmts ts, const char * msg, const char * fn, unsigned ln)
112 {
113 /*@-modfilesys@*/
114 if (_rpmts_debug)
115 fprintf(stderr, "--> ts %p -- %d %s at %s:%u\n", ts, ts->nrefs, msg, fn, ln);
116 /*@=modfilesys@*/
117     ts->nrefs--;
118     return NULL;
119 }
120
121 rpmts XrpmtsLink(rpmts ts, const char * msg, const char * fn, unsigned ln)
122 {
123     ts->nrefs++;
124 /*@-modfilesys@*/
125 if (_rpmts_debug)
126 fprintf(stderr, "--> ts %p ++ %d %s at %s:%u\n", ts, ts->nrefs, msg, fn, ln);
127 /*@=modfilesys@*/
128     /*@-refcounttrans@*/ return ts; /*@=refcounttrans@*/
129 }
130
131 int rpmtsCloseDB(rpmts ts)
132 {
133     int rc = 0;
134
135     if (ts->rdb != NULL) {
136         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBGET), &ts->rdb->db_getops);
137         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBPUT), &ts->rdb->db_putops);
138         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBDEL), &ts->rdb->db_delops);
139         rc = rpmdbClose(ts->rdb);
140         ts->rdb = NULL;
141     }
142     return rc;
143 }
144
145 int rpmtsOpenDB(rpmts ts, int dbmode)
146 {
147     int rc = 0;
148
149     if (ts->rdb != NULL && ts->dbmode == dbmode)
150         return 0;
151
152     (void) rpmtsCloseDB(ts);
153
154     /* XXX there's a potential db lock race here. */
155
156     ts->dbmode = dbmode;
157     rc = rpmdbOpen(ts->rootDir, &ts->rdb, ts->dbmode, 0644);
158     if (rc) {
159         const char * dn;
160         dn = rpmGetPath(ts->rootDir, "%{_dbpath}", NULL);
161         rpmMessage(RPMMESS_ERROR,
162                         _("cannot open Packages database in %s\n"), dn);
163         dn = _free(dn);
164     }
165     return rc;
166 }
167
168 int rpmtsInitDB(rpmts ts, int dbmode)
169 {
170     return rpmdbInit(ts->rootDir, dbmode);
171 }
172
173 int rpmtsRebuildDB(rpmts ts)
174 {
175     int rc;
176     if (!(ts->vsflags & RPMVSF_NOHDRCHK))
177         rc = rpmdbRebuild(ts->rootDir, ts, headerCheck);
178     else
179         rc = rpmdbRebuild(ts->rootDir, NULL, NULL);
180     return rc;
181 }
182
183 int rpmtsVerifyDB(rpmts ts)
184 {
185     return rpmdbVerify(ts->rootDir);
186 }
187
188 rpmdbMatchIterator rpmtsInitIterator(const rpmts ts, rpmTag rpmtag,
189                         const void * keyp, size_t keylen)
190 {
191     rpmdbMatchIterator mi;
192     if (ts->rdb == NULL && rpmtsOpenDB(ts, ts->dbmode))
193         return NULL;
194     mi = rpmdbInitIterator(ts->rdb, rpmtag, keyp, keylen);
195     if (mi && !(ts->vsflags & RPMVSF_NOHDRCHK))
196         (void) rpmdbSetHdrChk(mi, ts, headerCheck);
197     return mi;
198 }
199
200 rpmRC rpmtsFindPubkey(rpmts ts)
201 {
202     const void * sig = rpmtsSig(ts);
203     pgpDig dig = rpmtsDig(ts);
204     pgpDigParams sigp = rpmtsSignature(ts);
205     pgpDigParams pubp = rpmtsSignature(ts);
206     rpmRC res;
207     int xx;
208
209     if (sig == NULL || dig == NULL || sigp == NULL || pubp == NULL) {
210         res = RPMRC_NOKEY;
211         goto exit;
212     }
213
214     if (ts->pkpkt == NULL
215      || memcmp(sigp->signid, ts->pksignid, sizeof(ts->pksignid)))
216     {
217         int ix = -1;
218         rpmdbMatchIterator mi;
219         Header h;
220
221         ts->pkpkt = _free(ts->pkpkt);
222         ts->pkpktlen = 0;
223         memset(ts->pksignid, 0, sizeof(ts->pksignid));
224
225         /* Retrieve the pubkey that matches the signature. */
226         mi = rpmtsInitIterator(ts, RPMTAG_PUBKEYS, sigp->signid, sizeof(sigp->signid));
227         while ((h = rpmdbNextIterator(mi)) != NULL) {
228             const char ** pubkeys;
229             int_32 pt, pc;
230
231             if (!headerGetEntry(h, RPMTAG_PUBKEYS, &pt, (void **)&pubkeys, &pc))
232                 continue;
233             ix = rpmdbGetIteratorFileNum(mi);
234 /*@-boundsread@*/
235             if (ix >= pc
236              || b64decode(pubkeys[ix], (void **) &ts->pkpkt, &ts->pkpktlen))
237                 ix = -1;
238 /*@=boundsread@*/
239             pubkeys = headerFreeData(pubkeys, pt);
240             break;
241         }
242         mi = rpmdbFreeIterator(mi);
243
244         /* Was a matching pubkey found? */
245         if (ix < 0 || ts->pkpkt == NULL) {
246             res = RPMRC_NOKEY;
247             goto exit;
248         }
249
250         /*
251          * Can the pubkey packets be parsed?
252          * Do the parameters match the signature?
253          */
254         if (pgpPrtPkts(ts->pkpkt, ts->pkpktlen, NULL, 0)
255          && sigp->pubkey_algo == pubp->pubkey_algo
256 #ifdef  NOTYET
257          && sigp->hash_algo == pubp->hash_algo
258 #endif
259          && !memcmp(sigp->signid, pubp->signid, sizeof(sigp->signid)))
260         {
261             ts->pkpkt = _free(ts->pkpkt);
262             ts->pkpktlen = 0;
263             res = RPMRC_NOKEY;
264             goto exit;
265         }
266
267         /* XXX Verify the pubkey signature. */
268
269         /* Packet looks good, save the signer id. */
270 /*@-boundsread@*/
271         memcpy(ts->pksignid, sigp->signid, sizeof(ts->pksignid));
272 /*@=boundsread@*/
273
274         rpmMessage(RPMMESS_DEBUG, "========== %s pubkey id %s\n",
275                 (sigp->pubkey_algo == PGPPUBKEYALGO_DSA ? "DSA" :
276                 (sigp->pubkey_algo == PGPPUBKEYALGO_RSA ? "RSA" : "???")),
277                 pgpHexStr(sigp->signid, sizeof(sigp->signid)));
278
279     }
280
281 #ifdef  NOTNOW
282     {
283         if (ts->pkpkt == NULL) {
284             const char * pkfn = rpmExpand("%{_gpg_pubkey}", NULL);
285             if (pgpReadPkts(pkfn, &ts->pkpkt, &ts->pkpktlen) != PGPARMOR_PUBKEY) {
286                 pkfn = _free(pkfn);
287                 res = RPMRC_NOKEY;
288                 goto exit;
289             }
290             pkfn = _free(pkfn);
291         }
292     }
293 #endif
294
295     /* Retrieve parameters from pubkey packet(s). */
296     xx = pgpPrtPkts(ts->pkpkt, ts->pkpktlen, dig, 0);
297
298     /* Do the parameters match the signature? */
299     if (sigp->pubkey_algo == pubp->pubkey_algo
300 #ifdef  NOTYET
301      && sigp->hash_algo == pubp->hash_algo
302 #endif
303      && !memcmp(sigp->signid, pubp->signid, sizeof(sigp->signid)) )
304         res = RPMRC_OK;
305     else
306         res = RPMRC_NOKEY;
307
308     /* XXX Verify the signature signature. */
309
310 exit:
311     return res;
312 }
313
314 int rpmtsCloseSDB(rpmts ts)
315 {
316     int rc = 0;
317
318     if (ts->sdb != NULL) {
319         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBGET), &ts->sdb->db_getops);
320         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBPUT), &ts->sdb->db_putops);
321         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBDEL), &ts->sdb->db_delops);
322         rc = rpmdbClose(ts->sdb);
323         ts->sdb = NULL;
324     }
325     return rc;
326 }
327
328 int rpmtsOpenSDB(rpmts ts, int dbmode)
329 {
330     static int has_sdbpath = -1;
331     int rc = 0;
332
333     if (ts->sdb != NULL && ts->sdbmode == dbmode)
334         return 0;
335
336     if (has_sdbpath < 0)
337         has_sdbpath = rpmExpandNumeric("%{?_solve_dbpath:1}");
338
339     /* If not configured, don't try to open. */
340     if (has_sdbpath <= 0)
341         return 1;
342
343     addMacro(NULL, "_dbpath", NULL, "%{_solve_dbpath}", RMIL_DEFAULT);
344
345     rc = rpmdbOpen(ts->rootDir, &ts->sdb, ts->sdbmode, 0644);
346     if (rc) {
347         const char * dn;
348         dn = rpmGetPath(ts->rootDir, "%{_dbpath}", NULL);
349         rpmMessage(RPMMESS_WARNING,
350                         _("cannot open Solve database in %s\n"), dn);
351         dn = _free(dn);
352     }
353     delMacro(NULL, "_dbpath");
354
355     return rc;
356 }
357
358 /**
359  * Compare suggested package resolutions (qsort/bsearch).
360  * @param a             1st instance address
361  * @param b             2nd instance address
362  * @return              result of comparison
363  */
364 static int sugcmp(const void * a, const void * b)
365         /*@*/
366 {
367 /*@-boundsread@*/
368     const char * astr = *(const char **)a;
369     const char * bstr = *(const char **)b;
370 /*@=boundsread@*/
371     return strcmp(astr, bstr);
372 }
373
374 /*@-bounds@*/
375 int rpmtsSolve(rpmts ts, rpmds ds, /*@unused@*/ const void * data)
376 {
377     const char * errstr;
378     const char * str;
379     const char * qfmt;
380     rpmdbMatchIterator mi;
381     Header bh;
382     Header h;
383     size_t bhnamelen;
384     time_t bhtime;
385     rpmTag rpmtag;
386     const char * keyp;
387     size_t keylen;
388     int rc = 1; /* assume not found */
389     int xx;
390
391     /* Make suggestions only for install Requires: */
392     if (ts->goal != TSM_INSTALL)
393         return rc;
394
395     if (rpmdsTagN(ds) != RPMTAG_REQUIRENAME)
396         return rc;
397
398     keyp = rpmdsN(ds);
399     if (keyp == NULL)
400         return rc;
401
402     if (ts->sdb == NULL) {
403         xx = rpmtsOpenSDB(ts, ts->sdbmode);
404         if (xx) return rc;
405     }
406
407     /* Look for a matching Provides: in suggested universe. */
408     rpmtag = (*keyp == '/' ? RPMTAG_BASENAMES : RPMTAG_PROVIDENAME);
409     keylen = 0;
410     mi = rpmdbInitIterator(ts->sdb, rpmtag, keyp, keylen);
411     bhnamelen = 0;
412     bhtime = 0;
413     bh = NULL;
414     while ((h = rpmdbNextIterator(mi)) != NULL) {
415         const char * hname;
416         size_t hnamelen;
417         time_t htime;
418         int_32 * ip;
419
420         if (rpmtag == RPMTAG_PROVIDENAME && !rpmdsAnyMatchesDep(h, ds, 1))
421             continue;
422
423         /* XXX Prefer the shortest name if given alternatives. */
424         hname = NULL;
425         hnamelen = 0;
426         if (headerGetEntry(h, RPMTAG_NAME, NULL, (void **)&hname, NULL)) {
427             if (hname)
428                 hnamelen = strlen(hname);
429         }
430         if (bhnamelen > 0 && hnamelen > bhnamelen)
431             continue;
432
433         /* XXX Prefer the newest build if given alternatives. */
434         htime = 0;
435         if (headerGetEntry(h, RPMTAG_BUILDTIME, NULL, (void **)&ip, NULL))
436             htime = (time_t)*ip;
437
438         if (htime <= bhtime)
439             continue;
440
441         bh = headerFree(bh);
442         bh = headerLink(h);
443         bhtime = htime;
444         bhnamelen = hnamelen;
445     }
446     mi = rpmdbFreeIterator(mi);
447
448     /* Is there a suggested resolution? */
449     if (bh == NULL)
450         goto exit;
451
452     /* Format the suggestion. */
453     qfmt = rpmExpand("%{?_solve_name_fmt}", NULL);
454     if (qfmt == NULL || *qfmt == '\0')
455         goto exit;
456     str = headerSprintf(bh, qfmt, rpmTagTable, rpmHeaderFormats, &errstr);
457     bh = headerFree(bh);
458     qfmt = _free(qfmt);
459     if (str == NULL) {
460         rpmError(RPMERR_QFMT, _("incorrect format: %s\n"), errstr);
461         goto exit;
462     }
463
464     if (ts->transFlags & RPMTRANS_FLAG_ADDINDEPS) {
465         FD_t fd;
466         rpmRC rpmrc;
467
468         h = headerFree(h);
469         fd = Fopen(str, "r.ufdio");
470         if (fd == NULL || Ferror(fd)) {
471             rpmError(RPMERR_OPEN, _("open of %s failed: %s\n"), str,
472                         Fstrerror(fd));
473             if (fd != NULL) {
474                 xx = Fclose(fd);
475                 fd = NULL;
476             }
477             str = _free(str);
478             goto exit;
479         }
480         rpmrc = rpmReadPackageFile(ts, fd, str, &h);
481         xx = Fclose(fd);
482         switch (rpmrc) {
483         default:
484             str = _free(str);
485             break;
486         case RPMRC_NOTTRUSTED:
487         case RPMRC_NOKEY:
488         case RPMRC_OK:
489             if (h != NULL &&
490                 !rpmtsAddInstallElement(ts, h, (fnpyKey)str, 1, NULL))
491             {
492                 rpmMessage(RPMMESS_DEBUG, _("Adding: %s\n"), str);
493                 rc = -1;
494                 /* XXX str memory leak */
495                 break;
496             }
497             str = _free(str);
498             break;
499         }
500         h = headerFree(h);
501         goto exit;
502     }
503
504     rpmMessage(RPMMESS_DEBUG, _("Suggesting: %s\n"), str);
505     /* If suggestion is already present, don't bother. */
506     if (ts->suggests != NULL && ts->nsuggests > 0) {
507         if (bsearch(&str, ts->suggests, ts->nsuggests,
508                         sizeof(*ts->suggests), sugcmp))
509             goto exit;
510     }
511
512     /* Add a new (unique) suggestion. */
513     ts->suggests = xrealloc(ts->suggests,
514                         sizeof(*ts->suggests) * (ts->nsuggests + 2));
515     ts->suggests[ts->nsuggests] = str;
516     ts->nsuggests++;
517     ts->suggests[ts->nsuggests] = NULL;
518
519     if (ts->nsuggests > 1)
520         qsort(ts->suggests, ts->nsuggests, sizeof(*ts->suggests), sugcmp);
521
522 exit:
523 /*@-nullstate@*/ /* FIX: ts->suggests[] may be NULL */
524     return rc;
525 /*@=nullstate@*/
526 }
527 /*@=bounds@*/
528
529 int rpmtsAvailable(rpmts ts, const rpmds ds)
530 {
531     fnpyKey * sugkey;
532     int rc = 1; /* assume not found */
533
534     if (ts->availablePackages == NULL)
535         return rc;
536     sugkey = rpmalAllSatisfiesDepend(ts->availablePackages, ds, NULL);
537     if (sugkey == NULL)
538         return rc;
539
540     /* XXX no alternatives yet */
541     if (sugkey[0] != NULL) {
542         ts->suggests = xrealloc(ts->suggests,
543                         sizeof(*ts->suggests) * (ts->nsuggests + 2));
544         ts->suggests[ts->nsuggests] = sugkey[0];
545         sugkey[0] = NULL;
546         ts->nsuggests++;
547         ts->suggests[ts->nsuggests] = NULL;
548     }
549     sugkey = _free(sugkey);
550 /*@-nullstate@*/ /* FIX: ts->suggests[] may be NULL */
551     return rc;
552 /*@=nullstate@*/
553 }
554
555 int rpmtsSetSolveCallback(rpmts ts,
556                 int (*solve) (rpmts ts, rpmds key, const void * data),
557                 const void * solveData)
558 {
559     int rc = 0;
560
561 /*@-branchstate@*/
562     if (ts) {
563 /*@-assignexpose -temptrans @*/
564         ts->solve = solve;
565         ts->solveData = solveData;
566 /*@=assignexpose =temptrans @*/
567     }
568 /*@=branchstate@*/
569     return rc;
570 }
571
572 rpmps rpmtsProblems(rpmts ts)
573 {
574     rpmps ps = NULL;
575     if (ts) {
576         if (ts->probs)
577             ps = rpmpsLink(ts->probs, NULL);
578     }
579     return ps;
580 }
581
582 void rpmtsCleanDig(rpmts ts)
583 {
584     ts->sig = headerFreeData(ts->sig, ts->sigtype);
585     ts->dig = pgpFreeDig(ts->dig);
586 }
587
588 void rpmtsClean(rpmts ts)
589 {
590     rpmtsi pi; rpmte p;
591
592     if (ts == NULL)
593         return;
594
595     /* Clean up after dependency checks. */
596     pi = rpmtsiInit(ts);
597     while ((p = rpmtsiNext(pi, 0)) != NULL)
598         rpmteCleanDS(p);
599     pi = rpmtsiFree(pi);
600
601     ts->addedPackages = rpmalFree(ts->addedPackages);
602     ts->numAddedPackages = 0;
603
604     ts->suggests = _free(ts->suggests);
605     ts->nsuggests = 0;
606
607     ts->probs = rpmpsFree(ts->probs);
608
609     rpmtsCleanDig(ts);
610 }
611
612 void rpmtsEmpty(rpmts ts)
613 {
614     rpmtsi pi; rpmte p;
615     int oc;
616
617     if (ts == NULL)
618         return;
619
620 /*@-nullstate@*/        /* FIX: partial annotations */
621     rpmtsClean(ts);
622 /*@=nullstate@*/
623
624     for (pi = rpmtsiInit(ts), oc = 0; (p = rpmtsiNext(pi, 0)) != NULL; oc++) {
625 /*@-type -unqualifiedtrans @*/
626         ts->order[oc] = rpmteFree(ts->order[oc]);
627 /*@=type =unqualifiedtrans @*/
628     }
629     pi = rpmtsiFree(pi);
630
631     ts->orderCount = 0;
632
633     ts->numRemovedPackages = 0;
634 /*@-nullstate@*/        /* FIX: partial annotations */
635     return;
636 /*@=nullstate@*/
637 }
638
639 static void rpmtsPrintStat(const char * name, /*@null@*/ struct rpmop_s * op)
640         /*@globals fileSystem @*/
641         /*@modifies fileSystem @*/
642 {
643     static unsigned int scale = (1000 * 1000);
644     if (op != NULL && op->count > 0)
645         fprintf(stderr, "   %s %6d %6lu.%06lu MB %6lu.%06lu secs\n",
646                 name, op->count,
647                 (unsigned long)op->bytes/scale, (unsigned long)op->bytes%scale,
648                 op->usecs/scale, op->usecs%scale);
649 }
650
651 static void rpmtsPrintStats(rpmts ts)
652         /*@globals fileSystem, internalState @*/
653         /*@modifies fileSystem, internalState @*/
654 {
655     (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_TOTAL), 0);
656
657     rpmtsPrintStat("total:       ", rpmtsOp(ts, RPMTS_OP_TOTAL));
658     rpmtsPrintStat("check:       ", rpmtsOp(ts, RPMTS_OP_CHECK));
659     rpmtsPrintStat("order:       ", rpmtsOp(ts, RPMTS_OP_ORDER));
660     rpmtsPrintStat("fingerprint: ", rpmtsOp(ts, RPMTS_OP_FINGERPRINT));
661     rpmtsPrintStat("repackage:   ", rpmtsOp(ts, RPMTS_OP_REPACKAGE));
662     rpmtsPrintStat("install:     ", rpmtsOp(ts, RPMTS_OP_INSTALL));
663     rpmtsPrintStat("erase:       ", rpmtsOp(ts, RPMTS_OP_ERASE));
664     rpmtsPrintStat("scriptlets:  ", rpmtsOp(ts, RPMTS_OP_SCRIPTLETS));
665     rpmtsPrintStat("compress:    ", rpmtsOp(ts, RPMTS_OP_COMPRESS));
666     rpmtsPrintStat("uncompress:  ", rpmtsOp(ts, RPMTS_OP_UNCOMPRESS));
667     rpmtsPrintStat("digest:      ", rpmtsOp(ts, RPMTS_OP_DIGEST));
668     rpmtsPrintStat("signature:   ", rpmtsOp(ts, RPMTS_OP_SIGNATURE));
669     rpmtsPrintStat("dbadd:       ", rpmtsOp(ts, RPMTS_OP_DBADD));
670     rpmtsPrintStat("dbremove:    ", rpmtsOp(ts, RPMTS_OP_DBREMOVE));
671     rpmtsPrintStat("dbget:       ", rpmtsOp(ts, RPMTS_OP_DBGET));
672     rpmtsPrintStat("dbput:       ", rpmtsOp(ts, RPMTS_OP_DBPUT));
673     rpmtsPrintStat("dbdel:       ", rpmtsOp(ts, RPMTS_OP_DBDEL));
674 }
675
676 rpmts rpmtsFree(rpmts ts)
677 {
678     if (ts == NULL)
679         return NULL;
680
681     if (ts->nrefs > 1)
682         return rpmtsUnlink(ts, "tsCreate");
683
684 /*@-nullstate@*/        /* FIX: partial annotations */
685     rpmtsEmpty(ts);
686 /*@=nullstate@*/
687
688     (void) rpmtsCloseDB(ts);
689
690     (void) rpmtsCloseSDB(ts);
691
692     ts->removedPackages = _free(ts->removedPackages);
693
694     ts->availablePackages = rpmalFree(ts->availablePackages);
695     ts->numAvailablePackages = 0;
696
697     ts->dsi = _free(ts->dsi);
698
699     if (ts->scriptFd != NULL) {
700         ts->scriptFd = fdFree(ts->scriptFd, "rpmtsFree");
701         ts->scriptFd = NULL;
702     }
703     ts->rootDir = _free(ts->rootDir);
704     ts->currDir = _free(ts->currDir);
705
706 /*@-type +voidabstract @*/      /* FIX: double indirection */
707     ts->order = _free(ts->order);
708 /*@=type =voidabstract @*/
709     ts->orderAlloced = 0;
710
711     if (ts->pkpkt != NULL)
712         ts->pkpkt = _free(ts->pkpkt);
713     ts->pkpktlen = 0;
714     memset(ts->pksignid, 0, sizeof(ts->pksignid));
715
716     if (_rpmts_stats)
717         rpmtsPrintStats(ts);
718
719     (void) rpmtsUnlink(ts, "tsCreate");
720
721     /*@-refcounttrans -usereleased @*/
722     ts = _free(ts);
723     /*@=refcounttrans =usereleased @*/
724
725     return NULL;
726 }
727
728 rpmVSFlags rpmtsVSFlags(rpmts ts)
729 {
730     rpmVSFlags vsflags = 0;
731     if (ts != NULL)
732         vsflags = ts->vsflags;
733     return vsflags;
734 }
735
736 rpmVSFlags rpmtsSetVSFlags(rpmts ts, rpmVSFlags vsflags)
737 {
738     rpmVSFlags ovsflags = 0;
739     if (ts != NULL) {
740         ovsflags = ts->vsflags;
741         ts->vsflags = vsflags;
742     }
743     return ovsflags;
744 }
745
746 int rpmtsUnorderedSuccessors(rpmts ts, int first)
747 {
748     int unorderedSuccessors = 0;
749     if (ts != NULL) {
750         unorderedSuccessors = ts->unorderedSuccessors;
751         if (first >= 0)
752             ts->unorderedSuccessors = first;
753     }
754     return unorderedSuccessors;
755 }
756
757 const char * rpmtsRootDir(rpmts ts)
758 {
759     return (ts != NULL ? ts->rootDir : NULL);
760 }
761
762 void rpmtsSetRootDir(rpmts ts, const char * rootDir)
763 {
764     if (ts != NULL) {
765         size_t rootLen;
766
767         ts->rootDir = _free(ts->rootDir);
768
769         if (rootDir == NULL) {
770 #ifndef DYING
771             ts->rootDir = xstrdup("");
772 #endif
773             return;
774         }
775         rootLen = strlen(rootDir);
776
777 /*@-branchstate@*/
778         /* Make sure that rootDir has trailing / */
779         if (!(rootLen && rootDir[rootLen - 1] == '/')) {
780             char * t = alloca(rootLen + 2);
781             *t = '\0';
782             (void) stpcpy( stpcpy(t, rootDir), "/");
783             rootDir = t;
784         }
785 /*@=branchstate@*/
786         ts->rootDir = xstrdup(rootDir);
787     }
788 }
789
790 const char * rpmtsCurrDir(rpmts ts)
791 {
792     const char * currDir = NULL;
793     if (ts != NULL) {
794         currDir = ts->currDir;
795     }
796     return currDir;
797 }
798
799 void rpmtsSetCurrDir(rpmts ts, const char * currDir)
800 {
801     if (ts != NULL) {
802         ts->currDir = _free(ts->currDir);
803         if (currDir)
804             ts->currDir = xstrdup(currDir);
805     }
806 }
807
808 FD_t rpmtsScriptFd(rpmts ts)
809 {
810     FD_t scriptFd = NULL;
811     if (ts != NULL) {
812         scriptFd = ts->scriptFd;
813     }
814 /*@-compdef -refcounttrans -usereleased@*/
815     return scriptFd;
816 /*@=compdef =refcounttrans =usereleased@*/
817 }
818
819 void rpmtsSetScriptFd(rpmts ts, FD_t scriptFd)
820 {
821
822     if (ts != NULL) {
823         if (ts->scriptFd != NULL) {
824             ts->scriptFd = fdFree(ts->scriptFd, "rpmtsSetScriptFd");
825             ts->scriptFd = NULL;
826         }
827 /*@+voidabstract@*/
828         if (scriptFd != NULL)
829             ts->scriptFd = fdLink((void *)scriptFd, "rpmtsSetScriptFd");
830 /*@=voidabstract@*/
831     }
832 }
833
834 int rpmtsChrootDone(rpmts ts)
835 {
836     int chrootDone = 0;
837     if (ts != NULL) {
838         chrootDone = ts->chrootDone;
839     }
840     return chrootDone;
841 }
842
843 int rpmtsSetChrootDone(rpmts ts, int chrootDone)
844 {
845     int ochrootDone = 0;
846     if (ts != NULL) {
847         ochrootDone = ts->chrootDone;
848         if (ts->rdb != NULL)
849             ts->rdb->db_chrootDone = chrootDone;
850         ts->chrootDone = chrootDone;
851     }
852     return ochrootDone;
853 }
854
855 int_32 rpmtsGetTid(rpmts ts)
856 {
857     int_32 tid = 0;
858     if (ts != NULL) {
859         tid = ts->tid;
860     }
861     return tid;
862 }
863
864 int_32 rpmtsSetTid(rpmts ts, int_32 tid)
865 {
866     int_32 otid = 0;
867     if (ts != NULL) {
868         otid = ts->tid;
869         ts->tid = tid;
870     }
871     return otid;
872 }
873
874 int_32 rpmtsSigtag(const rpmts ts)
875 {
876     int_32 sigtag = 0;
877     if (ts != NULL)
878         sigtag = ts->sigtag;
879     return sigtag;
880 }
881
882 int_32 rpmtsSigtype(const rpmts ts)
883 {
884     int_32 sigtype = 0;
885     if (ts != NULL)
886         sigtype = ts->sigtype;
887     return sigtype;
888 }
889
890 const void * rpmtsSig(const rpmts ts)
891 {
892     const void * sig = NULL;
893     if (ts != NULL)
894         sig = ts->sig;
895     return sig;
896 }
897
898 int_32 rpmtsSiglen(const rpmts ts)
899 {
900     int_32 siglen = 0;
901     if (ts != NULL)
902         siglen = ts->siglen;
903     return siglen;
904 }
905
906 int rpmtsSetSig(rpmts ts,
907                 int_32 sigtag, int_32 sigtype, const void * sig, int_32 siglen)
908 {
909     if (ts != NULL) {
910         if (ts->sig && ts->sigtype)
911             ts->sig = headerFreeData(ts->sig, ts->sigtype);
912         ts->sigtag = sigtag;
913         ts->sigtype = (sig ? sigtype : 0);
914 /*@-assignexpose -kepttrans@*/
915         ts->sig = sig;
916 /*@=assignexpose =kepttrans@*/
917         ts->siglen = siglen;
918     }
919     return 0;
920 }
921
922 pgpDig rpmtsDig(rpmts ts)
923 {
924 /*@-mods@*/ /* FIX: hide lazy malloc for now */
925     if (ts->dig == NULL)
926         ts->dig = pgpNewDig();
927 /*@=mods@*/
928     if (ts->dig == NULL)
929         return NULL;
930     return ts->dig;
931 }
932
933 pgpDigParams rpmtsSignature(const rpmts ts)
934 {
935     pgpDig dig = rpmtsDig(ts);
936     if (dig == NULL) return NULL;
937 /*@-immediatetrans@*/
938     return &dig->signature;
939 /*@=immediatetrans@*/
940 }
941
942 pgpDigParams rpmtsPubkey(const rpmts ts)
943 {
944     pgpDig dig = rpmtsDig(ts);
945     if (dig == NULL) return NULL;
946 /*@-immediatetrans@*/
947     return &dig->pubkey;
948 /*@=immediatetrans@*/
949 }
950
951 rpmdb rpmtsGetRdb(rpmts ts)
952 {
953     rpmdb rdb = NULL;
954     if (ts != NULL) {
955         rdb = ts->rdb;
956     }
957 /*@-compdef -refcounttrans -usereleased @*/
958     return rdb;
959 /*@=compdef =refcounttrans =usereleased @*/
960 }
961
962 int rpmtsInitDSI(const rpmts ts)
963 {
964     rpmDiskSpaceInfo dsi;
965     struct stat sb;
966     int rc;
967     int i;
968
969     if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_DISKSPACE)
970         return 0;
971
972     rpmMessage(RPMMESS_DEBUG, _("mounted filesystems:\n"));
973     rpmMessage(RPMMESS_DEBUG,
974         _("    i    dev bsize       bavail       iavail mount point\n"));
975
976     rc = rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount);
977     if (rc || ts->filesystems == NULL || ts->filesystemCount <= 0)
978         return rc;
979
980     /* Get available space on mounted file systems. */
981
982     ts->dsi = _free(ts->dsi);
983     ts->dsi = xcalloc((ts->filesystemCount + 1), sizeof(*ts->dsi));
984
985     dsi = ts->dsi;
986
987     if (dsi != NULL)
988     for (i = 0; (i < ts->filesystemCount) && dsi; i++, dsi++) {
989 #if STATFS_IN_SYS_STATVFS
990         struct statvfs sfb;
991         memset(&sfb, 0, sizeof(sfb));
992         rc = statvfs(ts->filesystems[i], &sfb);
993 #else
994         struct statfs sfb;
995         memset(&sfb, 0, sizeof(sfb));
996 #  if STAT_STATFS4
997 /* This platform has the 4-argument version of the statfs call.  The last two
998  * should be the size of struct statfs and 0, respectively.  The 0 is the
999  * filesystem type, and is always 0 when statfs is called on a mounted
1000  * filesystem, as we're doing.
1001  */
1002         rc = statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0);
1003 #  else
1004         rc = statfs(ts->filesystems[i], &sfb);
1005 #  endif
1006 #endif
1007         if (rc)
1008             break;
1009
1010         rc = stat(ts->filesystems[i], &sb);
1011         if (rc)
1012             break;
1013         dsi->dev = sb.st_dev;
1014
1015         dsi->bsize = sfb.f_bsize;
1016         dsi->bneeded = 0;
1017         dsi->ineeded = 0;
1018 #ifdef STATFS_HAS_F_BAVAIL
1019         dsi->bavail = sfb.f_bavail;
1020 #else
1021 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
1022  * available for non-superusers.  f_blocks - f_bfree is probably too big, but
1023  * it's about all we can do.
1024  */
1025         dsi->bavail = sfb.f_blocks - sfb.f_bfree;
1026 #endif
1027         /* XXX Avoid FAT and other file systems that have not inodes. */
1028         dsi->iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
1029                                 ? sfb.f_ffree : -1;
1030         rpmMessage(RPMMESS_DEBUG, _("%5d 0x%04x %5u %12ld %12ld %s\n"),
1031                 i, (unsigned) dsi->dev, (unsigned) dsi->bsize,
1032                 (signed long) dsi->bavail, (signed long) dsi->iavail,
1033                 ts->filesystems[i]);
1034     }
1035     return rc;
1036 }
1037
1038 void rpmtsUpdateDSI(const rpmts ts, dev_t dev,
1039                 uint_32 fileSize, uint_32 prevSize, uint_32 fixupSize,
1040                 fileAction action)
1041 {
1042     rpmDiskSpaceInfo dsi;
1043     uint_32 bneeded;
1044
1045     dsi = ts->dsi;
1046     if (dsi) {
1047         while (dsi->bsize && dsi->dev != dev)
1048             dsi++;
1049         if (dsi->bsize == 0)
1050             dsi = NULL;
1051     }
1052     if (dsi == NULL)
1053         return;
1054
1055     bneeded = BLOCK_ROUND(fileSize, dsi->bsize);
1056
1057     switch (action) {
1058     case FA_BACKUP:
1059     case FA_SAVE:
1060     case FA_ALTNAME:
1061         dsi->ineeded++;
1062         dsi->bneeded += bneeded;
1063         /*@switchbreak@*/ break;
1064
1065     /*
1066      * FIXME: If two packages share a file (same md5sum), and
1067      * that file is being replaced on disk, will dsi->bneeded get
1068      * adjusted twice? Quite probably!
1069      */
1070     case FA_CREATE:
1071         dsi->bneeded += bneeded;
1072         dsi->bneeded -= BLOCK_ROUND(prevSize, dsi->bsize);
1073         /*@switchbreak@*/ break;
1074
1075     case FA_ERASE:
1076         dsi->ineeded--;
1077         dsi->bneeded -= bneeded;
1078         /*@switchbreak@*/ break;
1079
1080     default:
1081         /*@switchbreak@*/ break;
1082     }
1083
1084     if (fixupSize)
1085         dsi->bneeded -= BLOCK_ROUND(fixupSize, dsi->bsize);
1086 }
1087
1088 void rpmtsCheckDSIProblems(const rpmts ts, const rpmte te)
1089 {
1090     rpmDiskSpaceInfo dsi;
1091     rpmps ps;
1092     int fc;
1093     int i;
1094
1095     if (ts->filesystems == NULL || ts->filesystemCount <= 0)
1096         return;
1097
1098     dsi = ts->dsi;
1099     if (dsi == NULL)
1100         return;
1101     fc = rpmfiFC( rpmteFI(te, RPMTAG_BASENAMES) );
1102     if (fc <= 0)
1103         return;
1104
1105     ps = rpmtsProblems(ts);
1106     for (i = 0; i < ts->filesystemCount; i++, dsi++) {
1107
1108         if (dsi->bavail > 0 && adj_fs_blocks(dsi->bneeded) > dsi->bavail) {
1109             rpmpsAppend(ps, RPMPROB_DISKSPACE,
1110                         rpmteNEVR(te), rpmteKey(te),
1111                         ts->filesystems[i], NULL, NULL,
1112            (adj_fs_blocks(dsi->bneeded) - dsi->bavail) * dsi->bsize);
1113         }
1114
1115         if (dsi->iavail > 0 && adj_fs_blocks(dsi->ineeded) > dsi->iavail) {
1116             rpmpsAppend(ps, RPMPROB_DISKNODES,
1117                         rpmteNEVR(te), rpmteKey(te),
1118                         ts->filesystems[i], NULL, NULL,
1119             (adj_fs_blocks(dsi->ineeded) - dsi->iavail));
1120         }
1121     }
1122     ps = rpmpsFree(ps);
1123 }
1124
1125 void * rpmtsNotify(rpmts ts, rpmte te,
1126                 rpmCallbackType what, unsigned long amount, unsigned long total)
1127 {
1128     void * ptr = NULL;
1129     if (ts && ts->notify && te) {
1130 assert(!(te->type == TR_ADDED && te->h == NULL));
1131         /*@-type@*/ /* FIX: cast? */
1132         /*@-noeffectuncon @*/ /* FIX: check rc */
1133         ptr = ts->notify(te->h, what, amount, total,
1134                         rpmteKey(te), ts->notifyData);
1135         /*@=noeffectuncon @*/
1136         /*@=type@*/
1137     }
1138     return ptr;
1139 }
1140
1141 int rpmtsNElements(rpmts ts)
1142 {
1143     int nelements = 0;
1144     if (ts != NULL && ts->order != NULL) {
1145         nelements = ts->orderCount;
1146     }
1147     return nelements;
1148 }
1149
1150 rpmte rpmtsElement(rpmts ts, int ix)
1151 {
1152     rpmte te = NULL;
1153     if (ts != NULL && ts->order != NULL) {
1154         if (ix >= 0 && ix < ts->orderCount)
1155             te = ts->order[ix];
1156     }
1157     /*@-compdef@*/
1158     return te;
1159     /*@=compdef@*/
1160 }
1161
1162 rpmprobFilterFlags rpmtsFilterFlags(rpmts ts)
1163 {
1164     return (ts != NULL ? ts->ignoreSet : 0);
1165 }
1166
1167 rpmtransFlags rpmtsFlags(rpmts ts)
1168 {
1169     return (ts != NULL ? ts->transFlags : 0);
1170 }
1171
1172 rpmtransFlags rpmtsSetFlags(rpmts ts, rpmtransFlags transFlags)
1173 {
1174     rpmtransFlags otransFlags = 0;
1175     if (ts != NULL) {
1176         otransFlags = ts->transFlags;
1177         ts->transFlags = transFlags;
1178     }
1179     return otransFlags;
1180 }
1181
1182 Spec rpmtsSpec(rpmts ts)
1183 {
1184 /*@-compdef -retexpose -usereleased@*/
1185     return ts->spec;
1186 /*@=compdef =retexpose =usereleased@*/
1187 }
1188
1189 Spec rpmtsSetSpec(rpmts ts, Spec spec)
1190 {
1191     Spec ospec = ts->spec;
1192 /*@-assignexpose -temptrans@*/
1193     ts->spec = spec;
1194 /*@=assignexpose =temptrans@*/
1195     return ospec;
1196 }
1197
1198 rpmte rpmtsRelocateElement(rpmts ts)
1199 {
1200 /*@-compdef -retexpose -usereleased@*/
1201     return ts->relocateElement;
1202 /*@=compdef =retexpose =usereleased@*/
1203 }
1204
1205 rpmte rpmtsSetRelocateElement(rpmts ts, rpmte relocateElement)
1206 {
1207     rpmte orelocateElement = ts->relocateElement;
1208 /*@-assignexpose -temptrans@*/
1209     ts->relocateElement = relocateElement;
1210 /*@=assignexpose =temptrans@*/
1211     return orelocateElement;
1212 }
1213
1214 uint_32 rpmtsColor(rpmts ts)
1215 {
1216     return (ts != NULL ? ts->color : 0);
1217 }
1218
1219 uint_32 rpmtsSetColor(rpmts ts, uint_32 color)
1220 {
1221     uint_32 ocolor = 0;
1222     if (ts != NULL) {
1223         ocolor = ts->color;
1224         ts->color = color;
1225     }
1226     return ocolor;
1227 }
1228
1229 rpmop rpmtsOp(rpmts ts, rpmtsOpX opx)
1230 {
1231     rpmop op = NULL;
1232
1233     if (ts != NULL && opx >= 0 && opx < RPMTS_OP_MAX)
1234         op = ts->ops + opx;
1235 /*@-usereleased -compdef @*/
1236     return op;
1237 /*@=usereleased =compdef @*/
1238 }
1239
1240 int rpmtsSetNotifyCallback(rpmts ts,
1241                 rpmCallbackFunction notify, rpmCallbackData notifyData)
1242 {
1243     if (ts != NULL) {
1244         ts->notify = notify;
1245         ts->notifyData = notifyData;
1246     }
1247     return 0;
1248 }
1249
1250 int rpmtsGetKeys(const rpmts ts, fnpyKey ** ep, int * nep)
1251 {
1252     int rc = 0;
1253
1254     if (nep) *nep = ts->orderCount;
1255     if (ep) {
1256         rpmtsi pi;      rpmte p;
1257         fnpyKey * e;
1258
1259         *ep = e = xmalloc(ts->orderCount * sizeof(*e));
1260         pi = rpmtsiInit(ts);
1261         while ((p = rpmtsiNext(pi, 0)) != NULL) {
1262             switch (rpmteType(p)) {
1263             case TR_ADDED:
1264                 /*@-dependenttrans@*/
1265                 *e = rpmteKey(p);
1266                 /*@=dependenttrans@*/
1267                 /*@switchbreak@*/ break;
1268             case TR_REMOVED:
1269             default:
1270                 *e = NULL;
1271                 /*@switchbreak@*/ break;
1272             }
1273             e++;
1274         }
1275         pi = rpmtsiFree(pi);
1276     }
1277     return rc;
1278 }
1279
1280 rpmts rpmtsCreate(void)
1281 {
1282     rpmts ts;
1283
1284     ts = xcalloc(1, sizeof(*ts));
1285     memset(&ts->ops, 0, sizeof(ts->ops));
1286     (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_TOTAL), -1);
1287     ts->goal = TSM_UNKNOWN;
1288     ts->filesystemCount = 0;
1289     ts->filesystems = NULL;
1290     ts->dsi = NULL;
1291
1292     ts->solve = rpmtsSolve;
1293     ts->solveData = NULL;
1294     ts->nsuggests = 0;
1295     ts->suggests = NULL;
1296     ts->sdb = NULL;
1297     ts->sdbmode = O_RDONLY;
1298
1299     ts->rdb = NULL;
1300     ts->dbmode = O_RDONLY;
1301
1302     ts->scriptFd = NULL;
1303     ts->tid = (int_32) time(NULL);
1304     ts->delta = 5;
1305
1306     ts->color = rpmExpandNumeric("%{?_transaction_color}");
1307
1308     ts->numRemovedPackages = 0;
1309     ts->allocedRemovedPackages = ts->delta;
1310     ts->removedPackages = xcalloc(ts->allocedRemovedPackages,
1311                         sizeof(*ts->removedPackages));
1312
1313     ts->rootDir = NULL;
1314     ts->currDir = NULL;
1315     ts->chrootDone = 0;
1316
1317     ts->numAddedPackages = 0;
1318     ts->addedPackages = NULL;
1319
1320     ts->numAvailablePackages = 0;
1321     ts->availablePackages = NULL;
1322
1323     ts->orderAlloced = 0;
1324     ts->orderCount = 0;
1325     ts->order = NULL;
1326
1327     ts->probs = NULL;
1328
1329     ts->sig = NULL;
1330     ts->pkpkt = NULL;
1331     ts->pkpktlen = 0;
1332     memset(ts->pksignid, 0, sizeof(ts->pksignid));
1333     ts->dig = NULL;
1334
1335     ts->nrefs = 0;
1336
1337     return rpmtsLink(ts, "tsCreate");
1338 }