29c34414338b6c851da59cf9a92e6b3edd875760
[tools/librpm-tizen.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 FD_t @*/              /* XXX compared with NULL */
56 /*@access rpmps @*/
57 /*@access rpmDiskSpaceInfo @*/
58 /*@access rpmte @*/
59 /*@access rpmtsi @*/
60 /*@access rpmts @*/
61 /*@access fnpyKey @*/
62 /*@access pgpDig @*/
63 /*@access pgpDigParams @*/
64
65 /*@unchecked@*/
66 int _rpmts_debug = 0;
67
68 char * hGetNEVR(Header h, const char ** np)
69 {
70     const char * n, * v, * r;
71     char * NVR, * t;
72
73     (void) headerNVR(h, &n, &v, &r);
74     NVR = t = xcalloc(1, strlen(n) + strlen(v) + strlen(r) + sizeof("--"));
75 /*@-boundswrite@*/
76     t = stpcpy(t, n);
77     t = stpcpy(t, "-");
78     t = stpcpy(t, v);
79     t = stpcpy(t, "-");
80     t = stpcpy(t, r);
81     if (np)
82         *np = n;
83 /*@=boundswrite@*/
84     return NVR;
85 }
86
87 uint_32 hGetColor(Header h)
88 {
89     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
90     uint_32 hcolor = 0;
91     uint_32 * fcolors;
92     int_32 ncolors;
93     int i;
94
95     fcolors = NULL;
96     ncolors = 0;
97     if (hge(h, RPMTAG_FILECOLORS, NULL, (void **)&fcolors, &ncolors)
98      && fcolors != NULL && ncolors > 0)
99     {
100         for (i = 0; i < ncolors; i++)
101             hcolor |= fcolors[i];
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) {
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         if (scriptFd != NULL)
779             ts->scriptFd = fdLink(scriptFd, "rpmtsSetScriptFd");
780     }
781 }
782
783 int rpmtsChrootDone(rpmts ts)
784 {
785     int chrootDone = 0;
786     if (ts != NULL) {
787         chrootDone = ts->chrootDone;
788     }
789     return chrootDone;
790 }
791
792 int rpmtsSetChrootDone(rpmts ts, int chrootDone)
793 {
794     int ochrootDone = 0;
795     if (ts != NULL) {
796         ochrootDone = ts->chrootDone;
797         if (ts->rdb != NULL)
798             ts->rdb->db_chrootDone = chrootDone;
799         ts->chrootDone = chrootDone;
800     }
801     return ochrootDone;
802 }
803
804 int_32 rpmtsGetTid(rpmts ts)
805 {
806     int_32 tid = 0;
807     if (ts != NULL) {
808         tid = ts->tid;
809     }
810     return tid;
811 }
812
813 int_32 rpmtsSetTid(rpmts ts, int_32 tid)
814 {
815     int_32 otid = 0;
816     if (ts != NULL) {
817         otid = ts->tid;
818         ts->tid = tid;
819     }
820     return otid;
821 }
822
823 int_32 rpmtsSigtag(const rpmts ts)
824 {
825     int_32 sigtag = 0;
826     if (ts != NULL)
827         sigtag = ts->sigtag;
828     return sigtag;
829 }
830
831 int_32 rpmtsSigtype(const rpmts ts)
832 {
833     int_32 sigtag = 0;
834     if (ts != NULL)
835         sigtag = ts->sigtag;
836     return sigtag;
837 }
838
839 const void * rpmtsSig(const rpmts ts)
840 {
841     const void * sig = NULL;
842     if (ts != NULL)
843         sig = ts->sig;
844     return sig;
845 }
846
847 int_32 rpmtsSiglen(const rpmts ts)
848 {
849     int_32 siglen = 0;
850     if (ts != NULL)
851         siglen = ts->siglen;
852     return siglen;
853 }
854
855 int rpmtsSetSig(rpmts ts,
856                 int_32 sigtag, int_32 sigtype, const void * sig, int_32 siglen)
857 {
858     if (ts != NULL) {
859         if (ts->sig && ts->sigtype)
860             ts->sig = headerFreeData(ts->sig, ts->sigtype);
861         ts->sigtag = sigtag;
862         ts->sigtype = (sig ? sigtype : 0);
863 /*@-assignexpose -kepttrans@*/
864         ts->sig = sig;
865 /*@=assignexpose =kepttrans@*/
866         ts->siglen = siglen;
867     }
868     return 0;
869 }
870
871 pgpDig rpmtsDig(rpmts ts)
872 {
873 /*@-mods@*/ /* FIX: hide lazy malloc for now */
874     if (ts->dig == NULL)
875         ts->dig = pgpNewDig();
876 /*@=mods@*/
877     if (ts->dig == NULL)
878         return NULL;
879     return ts->dig;
880 }
881
882 pgpDigParams rpmtsSignature(const rpmts ts)
883 {
884     pgpDig dig = rpmtsDig(ts);
885     if (dig == NULL) return NULL;
886 /*@-immediatetrans@*/
887     return &dig->signature;
888 /*@=immediatetrans@*/
889 }
890
891 pgpDigParams rpmtsPubkey(const rpmts ts)
892 {
893     pgpDig dig = rpmtsDig(ts);
894     if (dig == NULL) return NULL;
895 /*@-immediatetrans@*/
896     return &dig->pubkey;
897 /*@=immediatetrans@*/
898 }
899
900 rpmdb rpmtsGetRdb(rpmts ts)
901 {
902     rpmdb rdb = NULL;
903     if (ts != NULL) {
904         rdb = ts->rdb;
905     }
906 /*@-compdef -refcounttrans -usereleased @*/
907     return rdb;
908 /*@=compdef =refcounttrans =usereleased @*/
909 }
910
911 int rpmtsInitDSI(const rpmts ts)
912 {
913     rpmDiskSpaceInfo dsi;
914     struct stat sb;
915     int rc;
916     int i;
917
918     if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_DISKSPACE)
919         return 0;
920
921     rc = rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount);
922     if (rc || ts->filesystems == NULL || ts->filesystemCount <= 0)
923         return rc;
924
925     /* Get available space on mounted file systems. */
926
927     rpmMessage(RPMMESS_DEBUG, _("getting list of mounted filesystems\n"));
928
929     ts->dsi = _free(ts->dsi);
930     ts->dsi = xcalloc((ts->filesystemCount + 1), sizeof(*ts->dsi));
931
932     dsi = ts->dsi;
933
934     if (dsi != NULL)
935     for (i = 0; (i < ts->filesystemCount) && dsi; i++, dsi++) {
936 #if STATFS_IN_SYS_STATVFS
937         struct statvfs sfb;
938         memset(&sfb, 0, sizeof(sfb));
939         rc = statvfs(ts->filesystems[i], &sfb);
940 #else
941         struct statfs sfb;
942         memset(&sfb, 0, sizeof(sfb));
943 #  if STAT_STATFS4
944 /* This platform has the 4-argument version of the statfs call.  The last two
945  * should be the size of struct statfs and 0, respectively.  The 0 is the
946  * filesystem type, and is always 0 when statfs is called on a mounted
947  * filesystem, as we're doing.
948  */
949         rc = statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0);
950 #  else
951         rc = statfs(ts->filesystems[i], &sfb);
952 #  endif
953 #endif
954         if (rc)
955             break;
956
957         rc = stat(ts->filesystems[i], &sb);
958         if (rc)
959             break;
960         dsi->dev = sb.st_dev;
961
962         dsi->bsize = sfb.f_bsize;
963         dsi->bneeded = 0;
964         dsi->ineeded = 0;
965 #ifdef STATFS_HAS_F_BAVAIL
966         dsi->bavail = sfb.f_bavail;
967 #else
968 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
969  * available for non-superusers.  f_blocks - f_bfree is probably too big, but
970  * it's about all we can do.
971  */
972         dsi->bavail = sfb.f_blocks - sfb.f_bfree;
973 #endif
974         /* XXX Avoid FAT and other file systems that have not inodes. */
975         dsi->iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
976                                 ? sfb.f_ffree : -1;
977     }
978     return rc;
979 }
980
981 void rpmtsUpdateDSI(const rpmts ts, dev_t dev,
982                 uint_32 fileSize, uint_32 prevSize, uint_32 fixupSize,
983                 fileAction action)
984 {
985     rpmDiskSpaceInfo dsi;
986     uint_32 bneeded;
987
988     dsi = ts->dsi;
989     if (dsi) {
990         while (dsi->bsize && dsi->dev != dev)
991             dsi++;
992         if (dsi->bsize == 0)
993             dsi = NULL;
994     }
995     if (dsi == NULL)
996         return;
997
998     bneeded = BLOCK_ROUND(fileSize, dsi->bsize);
999
1000     switch (action) {
1001     case FA_BACKUP:
1002     case FA_SAVE:
1003     case FA_ALTNAME:
1004         dsi->ineeded++;
1005         dsi->bneeded += bneeded;
1006         /*@switchbreak@*/ break;
1007
1008     /*
1009      * FIXME: If two packages share a file (same md5sum), and
1010      * that file is being replaced on disk, will dsi->bneeded get
1011      * adjusted twice? Quite probably!
1012      */
1013     case FA_CREATE:
1014         dsi->bneeded += bneeded;
1015         dsi->bneeded -= BLOCK_ROUND(prevSize, dsi->bsize);
1016         /*@switchbreak@*/ break;
1017
1018     case FA_ERASE:
1019         dsi->ineeded--;
1020         dsi->bneeded -= bneeded;
1021         /*@switchbreak@*/ break;
1022
1023     default:
1024         /*@switchbreak@*/ break;
1025     }
1026
1027     if (fixupSize)
1028         dsi->bneeded -= BLOCK_ROUND(fixupSize, dsi->bsize);
1029 }
1030
1031 void rpmtsCheckDSIProblems(const rpmts ts, const rpmte te)
1032 {
1033     rpmDiskSpaceInfo dsi;
1034     rpmps ps;
1035     int fc;
1036     int i;
1037
1038     if (ts->filesystems == NULL || ts->filesystemCount <= 0)
1039         return;
1040
1041     dsi = ts->dsi;
1042     if (dsi == NULL)
1043         return;
1044     fc = rpmfiFC( rpmteFI(te, RPMTAG_BASENAMES) );
1045     if (fc <= 0)
1046         return;
1047
1048     ps = rpmtsProblems(ts);
1049     for (i = 0; i < ts->filesystemCount; i++, dsi++) {
1050
1051         if (dsi->bavail > 0 && adj_fs_blocks(dsi->bneeded) > dsi->bavail) {
1052             rpmpsAppend(ps, RPMPROB_DISKSPACE,
1053                         rpmteNEVR(te), rpmteKey(te),
1054                         ts->filesystems[i], NULL, NULL,
1055            (adj_fs_blocks(dsi->bneeded) - dsi->bavail) * dsi->bsize);
1056         }
1057
1058         if (dsi->iavail > 0 && adj_fs_blocks(dsi->ineeded) > dsi->iavail) {
1059             rpmpsAppend(ps, RPMPROB_DISKNODES,
1060                         rpmteNEVR(te), rpmteKey(te),
1061                         ts->filesystems[i], NULL, NULL,
1062             (adj_fs_blocks(dsi->ineeded) - dsi->iavail));
1063         }
1064     }
1065     ps = rpmpsFree(ps);
1066 }
1067
1068 void * rpmtsNotify(rpmts ts, rpmte te,
1069                 rpmCallbackType what, unsigned long amount, unsigned long total)
1070 {
1071     void * ptr = NULL;
1072     if (ts && ts->notify && te) {
1073 assert(!(te->type == TR_ADDED && te->h == NULL));
1074         /*@-type@*/ /* FIX: cast? */
1075         /*@-noeffectuncon @*/ /* FIX: check rc */
1076         ptr = ts->notify(te->h, what, amount, total,
1077                         rpmteKey(te), ts->notifyData);
1078         /*@=noeffectuncon @*/
1079         /*@=type@*/
1080     }
1081     return ptr;
1082 }
1083
1084 int rpmtsNElements(rpmts ts)
1085 {
1086     int nelements = 0;
1087     if (ts != NULL && ts->order != NULL) {
1088         nelements = ts->orderCount;
1089     }
1090     return nelements;
1091 }
1092
1093 rpmte rpmtsElement(rpmts ts, int ix)
1094 {
1095     rpmte te = NULL;
1096     if (ts != NULL && ts->order != NULL) {
1097         if (ix >= 0 && ix < ts->orderCount)
1098             te = ts->order[ix];
1099     }
1100     /*@-compdef@*/
1101     return te;
1102     /*@=compdef@*/
1103 }
1104
1105 rpmprobFilterFlags rpmtsFilterFlags(rpmts ts)
1106 {
1107     return (ts != NULL ? ts->ignoreSet : 0);
1108 }
1109
1110 rpmtransFlags rpmtsFlags(rpmts ts)
1111 {
1112     return (ts != NULL ? ts->transFlags : 0);
1113 }
1114
1115 rpmtransFlags rpmtsSetFlags(rpmts ts, rpmtransFlags transFlags)
1116 {
1117     rpmtransFlags otransFlags = 0;
1118     if (ts != NULL) {
1119         otransFlags = ts->transFlags;
1120         ts->transFlags = transFlags;
1121     }
1122     return otransFlags;
1123 }
1124
1125 Spec rpmtsSpec(rpmts ts)
1126 {
1127 /*@-compdef -retexpose -usereleased@*/
1128     return ts->spec;
1129 /*@=compdef =retexpose =usereleased@*/
1130 }
1131
1132 Spec rpmtsSetSpec(rpmts ts, Spec spec)
1133 {
1134     Spec ospec = ts->spec;
1135 /*@-assignexpose -temptrans@*/
1136     ts->spec = spec;
1137 /*@=assignexpose =temptrans@*/
1138     return ospec;
1139 }
1140
1141 rpmte rpmtsRelocateElement(rpmts ts)
1142 {
1143 /*@-compdef -retexpose -usereleased@*/
1144     return ts->relocateElement;
1145 /*@=compdef =retexpose =usereleased@*/
1146 }
1147
1148 rpmte rpmtsSetRelocateElement(rpmts ts, rpmte relocateElement)
1149 {
1150     rpmte orelocateElement = ts->relocateElement;
1151 /*@-assignexpose -temptrans@*/
1152     ts->relocateElement = relocateElement;
1153 /*@=assignexpose =temptrans@*/
1154     return orelocateElement;
1155 }
1156
1157 uint_32 rpmtsColor(rpmts ts)
1158 {
1159     return (ts != NULL ? ts->color : 0);
1160 }
1161
1162 uint_32 rpmtsSetColor(rpmts ts, uint_32 color)
1163 {
1164     uint_32 ocolor = 0;
1165     if (ts != NULL) {
1166         ocolor = ts->color;
1167         ts->color = color;
1168     }
1169     return ocolor;
1170 }
1171
1172 int rpmtsSetNotifyCallback(rpmts ts,
1173                 rpmCallbackFunction notify, rpmCallbackData notifyData)
1174 {
1175     if (ts != NULL) {
1176         ts->notify = notify;
1177         ts->notifyData = notifyData;
1178     }
1179     return 0;
1180 }
1181
1182 int rpmtsGetKeys(const rpmts ts, fnpyKey ** ep, int * nep)
1183 {
1184     int rc = 0;
1185
1186     if (nep) *nep = ts->orderCount;
1187     if (ep) {
1188         rpmtsi pi;      rpmte p;
1189         fnpyKey * e;
1190
1191         *ep = e = xmalloc(ts->orderCount * sizeof(*e));
1192         pi = rpmtsiInit(ts);
1193         while ((p = rpmtsiNext(pi, 0)) != NULL) {
1194             switch (rpmteType(p)) {
1195             case TR_ADDED:
1196                 /*@-dependenttrans@*/
1197                 *e = rpmteKey(p);
1198                 /*@=dependenttrans@*/
1199                 /*@switchbreak@*/ break;
1200             case TR_REMOVED:
1201             default:
1202                 *e = NULL;
1203                 /*@switchbreak@*/ break;
1204             }
1205             e++;
1206         }
1207         pi = rpmtsiFree(pi);
1208     }
1209     return rc;
1210 }
1211
1212 rpmts rpmtsCreate(void)
1213 {
1214     rpmts ts;
1215
1216     ts = xcalloc(1, sizeof(*ts));
1217     ts->goal = TSM_UNKNOWN;
1218     ts->filesystemCount = 0;
1219     ts->filesystems = NULL;
1220     ts->dsi = NULL;
1221
1222     ts->solve = rpmtsSolve;
1223     ts->solveData = NULL;
1224     ts->nsuggests = 0;
1225     ts->suggests = NULL;
1226     ts->sdb = NULL;
1227     ts->sdbmode = O_RDONLY;
1228
1229     ts->rdb = NULL;
1230     ts->dbmode = O_RDONLY;
1231
1232     ts->scriptFd = NULL;
1233     ts->tid = (int_32) time(NULL);
1234     ts->delta = 5;
1235
1236     ts->color = rpmExpandNumeric("%{?_transaction_color}");
1237
1238     ts->numRemovedPackages = 0;
1239     ts->allocedRemovedPackages = ts->delta;
1240     ts->removedPackages = xcalloc(ts->allocedRemovedPackages,
1241                         sizeof(*ts->removedPackages));
1242
1243     ts->rootDir = NULL;
1244     ts->currDir = NULL;
1245     ts->chrootDone = 0;
1246
1247     ts->numAddedPackages = 0;
1248     ts->addedPackages = NULL;
1249
1250     ts->numAvailablePackages = 0;
1251     ts->availablePackages = NULL;
1252
1253     ts->orderAlloced = 0;
1254     ts->orderCount = 0;
1255     ts->order = NULL;
1256
1257     ts->probs = NULL;
1258
1259     ts->sig = NULL;
1260     ts->pkpkt = NULL;
1261     ts->pkpktlen = 0;
1262     memset(ts->pksignid, 0, sizeof(ts->pksignid));
1263     ts->dig = NULL;
1264
1265     ts->nrefs = 0;
1266
1267     return rpmtsLink(ts, "tsCreate");
1268 }