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