Sanitize python object -> tag number exception handling
[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 <inttypes.h>
8
9 #include <rpm/rpmtypes.h>
10 #include <rpm/rpmlib.h>                 /* rpmReadPackage etc */
11 #include <rpm/rpmurl.h>
12 #include <rpm/rpmmacro.h>
13 #include <rpm/rpmfileutil.h>            /* rpmtsOpenDB() needs rpmGetPath */
14 #include <rpm/rpmstring.h>
15 #include <rpm/rpmkeyring.h>
16
17 #include <rpm/rpmdb.h>
18 #include <rpm/rpmds.h>
19 #include <rpm/rpmfi.h>
20 #include <rpm/rpmlog.h>
21 #include <rpm/rpmte.h>
22
23 #include "rpmio/digest.h"
24 #include "lib/rpmal.h"
25 #include "lib/rpmlock.h"
26 #include "lib/rpmts_internal.h"
27 #include "lib/misc.h"
28
29 /* XXX FIXME: merge with existing (broken?) tests in system.h */
30 /* portability fiddles */
31 #if STATFS_IN_SYS_STATVFS
32 #include <sys/statvfs.h>
33
34 #else
35 # if STATFS_IN_SYS_VFS
36 #  include <sys/vfs.h>
37 # else
38 #  if STATFS_IN_SYS_MOUNT
39 #   include <sys/mount.h>
40 #  else
41 #   if STATFS_IN_SYS_STATFS
42 #    include <sys/statfs.h>
43 #   endif
44 #  endif
45 # endif
46 #endif
47
48 #include "debug.h"
49
50 static void loadKeyring(rpmts ts);
51
52 int _rpmts_debug = 0;
53
54 int _rpmts_stats = 0;
55
56 rpmts rpmtsUnlink(rpmts ts, const char * msg)
57 {
58 if (_rpmts_debug)
59 fprintf(stderr, "--> ts %p -- %d %s\n", ts, ts->nrefs, msg);
60     ts->nrefs--;
61     return NULL;
62 }
63
64 rpmts rpmtsLink(rpmts ts, const char * msg)
65 {
66     ts->nrefs++;
67 if (_rpmts_debug)
68 fprintf(stderr, "--> ts %p ++ %d %s\n", ts, ts->nrefs, msg);
69     return ts;
70 }
71
72 int rpmtsCloseDB(rpmts ts)
73 {
74     int rc = 0;
75
76     if (ts->rdb != NULL) {
77         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBGET), 
78                         rpmdbOp(ts->rdb, RPMDB_OP_DBGET));
79         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBPUT),
80                         rpmdbOp(ts->rdb, RPMDB_OP_DBPUT));
81         (void) rpmswAdd(rpmtsOp(ts, RPMTS_OP_DBDEL),
82                         rpmdbOp(ts->rdb, RPMDB_OP_DBDEL));
83         rc = rpmdbClose(ts->rdb);
84         ts->rdb = NULL;
85     }
86     return rc;
87 }
88
89 int rpmtsOpenDB(rpmts ts, int dbmode)
90 {
91     int rc = 0;
92
93     if (ts->rdb != NULL && ts->dbmode == dbmode)
94         return 0;
95
96     (void) rpmtsCloseDB(ts);
97
98     /* XXX there's a potential db lock race here. */
99
100     ts->dbmode = dbmode;
101     rc = rpmdbOpen(ts->rootDir, &ts->rdb, ts->dbmode, 0644);
102     if (rc) {
103         char * dn = rpmGetPath(ts->rootDir, "%{_dbpath}", NULL);
104         rpmlog(RPMLOG_ERR,
105                         _("cannot open Packages database in %s\n"), dn);
106         dn = _free(dn);
107     }
108     return rc;
109 }
110
111 int rpmtsInitDB(rpmts ts, int dbmode)
112 {
113     void *lock = rpmtsAcquireLock(ts);
114     int rc = -1;
115     if (lock)
116             rc = rpmdbInit(ts->rootDir, dbmode);
117     rpmtsFreeLock(lock);
118     return rc;
119 }
120
121 int rpmtsGetDBMode(rpmts ts)
122 {
123     assert(ts != NULL);
124     return (ts->dbmode);
125 }
126
127 int rpmtsSetDBMode(rpmts ts, int dbmode)
128 {
129     int rc = 1;
130     /* mode setting only permitted on non-open db */
131     if (ts != NULL && rpmtsGetRdb(ts) == NULL) {
132         ts->dbmode = dbmode;
133         rc = 0;
134     }
135     return rc;
136 }
137
138
139 int rpmtsRebuildDB(rpmts ts)
140 {
141     int rc;
142     void *lock = rpmtsAcquireLock(ts);
143     if (!lock) return -1;
144     if (!(ts->vsflags & RPMVSF_NOHDRCHK))
145         rc = rpmdbRebuild(ts->rootDir, ts, headerCheck);
146     else
147         rc = rpmdbRebuild(ts->rootDir, NULL, NULL);
148     rpmtsFreeLock(lock);
149     return rc;
150 }
151
152 int rpmtsVerifyDB(rpmts ts)
153 {
154     return rpmdbVerify(ts->rootDir);
155 }
156
157 /* keyp might no be defined. */
158 rpmdbMatchIterator rpmtsInitIterator(const rpmts ts, rpmTag rpmtag,
159                         const void * keyp, size_t keylen)
160 {
161     rpmdbMatchIterator mi = NULL;
162     const char * arch = NULL;
163     char *tmp = NULL;
164     int xx;
165
166     if (ts->keyring == NULL)
167         loadKeyring(ts);
168
169     if (ts->rdb == NULL && rpmtsOpenDB(ts, ts->dbmode))
170         return NULL;
171
172     /* Parse out "N(EVR).A" tokens from a label key. */
173     if (rpmtag == RPMDBI_LABEL && keyp != NULL) {
174         const char *se, *s = keyp;
175         char *t;
176         size_t slen = strlen(s);
177         int level = 0;
178         int c;
179
180         tmp = xmalloc(slen+1);
181         keyp = t = tmp;
182         while ((c = *s++) != '\0') {
183             switch (c) {
184             default:
185                 *t++ = c;
186                 break;
187             case '(':
188                 /* XXX Fail if nested parens. */
189                 if (level++ != 0) {
190                     rpmlog(RPMLOG_ERR, _("extra '(' in package label: %s\n"), (const char*)keyp);
191                     goto exit;
192                 }
193                 /* Parse explicit epoch. */
194                 for (se = s; *se && risdigit(*se); se++)
195                     {};
196                 if (*se == ':') {
197                     /* XXX skip explicit epoch's (for now) */
198                     *t++ = '-';
199                     s = se + 1;
200                 } else {
201                     /* No Epoch: found. Convert '(' to '-' and chug. */
202                     *t++ = '-';
203                 }
204                 break;
205             case ')':
206                 /* XXX Fail if nested parens. */
207                 if (--level != 0) {
208                     rpmlog(RPMLOG_ERR, _("missing '(' in package label: %s\n"), (const char*)keyp);
209                     goto exit;
210                 }
211                 /* Don't copy trailing ')' */
212                 break;
213             }
214         }
215         if (level) {
216             rpmlog(RPMLOG_ERR, _("missing ')' in package label: %s\n"), (const char*)keyp);
217             goto exit;
218         }
219         *t = '\0';
220         t = (char *) keyp;
221         t = strrchr(t, '.');
222         /* Is this a valid ".arch" suffix? */
223         if (t != NULL && rpmIsKnownArch(t+1)) {
224            *t++ = '\0';
225            arch = t;
226         }
227     }
228
229     mi = rpmdbInitIterator(ts->rdb, rpmtag, keyp, keylen);
230
231     /* Verify header signature/digest during retrieve (if not disabled). */
232     if (mi && !(ts->vsflags & RPMVSF_NOHDRCHK))
233         (void) rpmdbSetHdrChk(mi, ts, headerCheck);
234
235     /* Select specified arch only. */
236     if (arch != NULL)
237         xx = rpmdbSetIteratorRE(mi, RPMTAG_ARCH, RPMMIRE_DEFAULT, arch);
238
239 exit:
240     free(tmp);
241
242     return mi;
243 }
244
245 rpmKeyring rpmtsGetKeyring(rpmts ts, int autoload)
246 {
247     rpmKeyring keyring = NULL;
248     if (ts) {
249         if (ts->keyring == NULL && autoload) {
250             loadKeyring(ts);
251         }
252         keyring = rpmKeyringLink(ts->keyring);
253     }
254     return keyring;
255 }
256
257 int rpmtsSetKeyring(rpmts ts, rpmKeyring keyring)
258 {
259     /*
260      * Should we permit switching keyring on the fly? For now, require
261      * rpmdb isn't open yet (fairly arbitrary limitation)...
262      */
263     if (ts == NULL || rpmtsGetRdb(ts) != NULL)
264         return -1;
265
266     rpmKeyringFree(ts->keyring);
267     ts->keyring = rpmKeyringLink(keyring);
268     return 0;
269 }
270
271 static int loadKeyringFromFiles(rpmts ts)
272 {
273     ARGV_t files = NULL;
274     /* XXX TODO: deal with chroot path issues */
275     char *pkpath = rpmGetPath(ts->rootDir, "%{_keyringpath}/*.key", NULL);
276     int nkeys = 0;
277
278     rpmlog(RPMLOG_DEBUG, "loading keyring from pubkeys in %s\n", pkpath);
279     if (rpmGlob(pkpath, NULL, &files)) {
280         rpmlog(RPMLOG_DEBUG, "couldn't find any keys in %s\n", pkpath);
281         goto exit;
282     }
283
284     for (char **f = files; *f; f++) {
285         rpmPubkey key = rpmPubkeyRead(*f);
286         if (!key) {
287             rpmlog(RPMLOG_ERR, _("%s: reading of public key failed.\n"), *f);
288             continue;
289         }
290         if (rpmKeyringAddKey(ts->keyring, key) == 0) {
291             nkeys++;
292             rpmlog(RPMLOG_DEBUG, "added key %s to keyring\n", *f);
293         }
294         rpmPubkeyFree(key);
295     }
296 exit:
297     free(pkpath);
298     argvFree(files);
299     return nkeys;
300 }
301
302 static int loadKeyringFromDB(rpmts ts)
303 {
304     Header h;
305     rpmdbMatchIterator mi;
306     int nkeys = 0;
307
308     rpmlog(RPMLOG_DEBUG, "loading keyring from rpmdb\n");
309     mi = rpmtsInitIterator(ts, RPMTAG_NAME, "gpg-pubkey", 0);
310     while ((h = rpmdbNextIterator(mi)) != NULL) {
311         struct rpmtd_s pubkeys;
312         const char *key;
313
314         if (!headerGet(h, RPMTAG_PUBKEYS, &pubkeys, HEADERGET_MINMEM))
315            continue;
316
317         while ((key = rpmtdNextString(&pubkeys))) {
318             uint8_t *pkt;
319             size_t pktlen;
320
321             if (b64decode(key, (void **) &pkt, &pktlen) == 0) {
322                 rpmPubkey key = rpmPubkeyNew(pkt, pktlen);
323                 if (rpmKeyringAddKey(ts->keyring, key) == 0) {
324                     char *nvr = headerGetAsString(h, RPMTAG_NVR);
325                     rpmlog(RPMLOG_DEBUG, "added key %s to keyring\n", nvr);
326                     free(nvr);
327                     nkeys++;
328                 }
329                 rpmPubkeyFree(key);
330                 free(pkt);
331             }
332         }
333         rpmtdFreeData(&pubkeys);
334     }
335     rpmdbFreeIterator(mi);
336
337     return nkeys;
338 }
339
340 static void loadKeyring(rpmts ts)
341 {
342     ts->keyring = rpmKeyringNew();
343     if (loadKeyringFromFiles(ts) == 0) {
344         if (loadKeyringFromDB(ts) > 0) {
345             /* XXX make this a warning someday... */
346             rpmlog(RPMLOG_DEBUG, "Using legacy gpg-pubkey(s) from rpmdb\n");
347         }
348     }
349 }
350
351 rpmRC rpmtsFindPubkey(rpmts ts, pgpDig dig)
352 {
353     rpmRC res = RPMRC_NOKEY;
354
355     if (dig == NULL)
356         goto exit;
357
358     if (ts->keyring == NULL) {
359         loadKeyring(ts);
360     }
361     res = rpmKeyringLookup(ts->keyring, dig);
362
363 exit:
364     return res;
365 }
366
367 /* Build pubkey header. */
368 static int makePubkeyHeader(rpmts ts, rpmPubkey key, Header h)
369 {
370     const char * afmt = "%{pubkeys:armor}";
371     const char * group = "Public Keys";
372     const char * license = "pubkey";
373     const char * buildhost = "localhost";
374     rpmsenseFlags pflags = (RPMSENSE_KEYRING|RPMSENSE_EQUAL);
375     uint32_t zero = 0;
376     pgpDig dig = NULL;
377     pgpDigParams pubp = NULL;
378     char * d = NULL;
379     char * enc = NULL;
380     char * n = NULL;
381     char * u = NULL;
382     char * v = NULL;
383     char * r = NULL;
384     char * evr = NULL;
385     int rc = -1;
386
387     if ((enc = rpmPubkeyBase64(key)) == NULL)
388         goto exit;
389     if ((dig = rpmPubkeyDig(key)) == NULL)
390         goto exit;
391
392     /* Build header elements. */
393     pubp = &dig->pubkey;
394     v = pgpHexStr(pubp->signid, sizeof(pubp->signid)); 
395     r = pgpHexStr(pubp->time, sizeof(pubp->time));
396
397     rasprintf(&n, "gpg(%s)", v+8);
398     rasprintf(&u, "gpg(%s)", pubp->userid ? pubp->userid : "none");
399     rasprintf(&evr, "%d:%s-%s", pubp->version, v, r);
400
401     headerPutString(h, RPMTAG_PUBKEYS, enc);
402
403     if ((d = headerFormat(h, afmt, NULL)) == NULL)
404         goto exit;
405
406     headerPutString(h, RPMTAG_NAME, "gpg-pubkey");
407     headerPutString(h, RPMTAG_VERSION, v+8);
408     headerPutString(h, RPMTAG_RELEASE, r);
409     headerPutString(h, RPMTAG_DESCRIPTION, d);
410     headerPutString(h, RPMTAG_GROUP, group);
411     headerPutString(h, RPMTAG_LICENSE, license);
412     headerPutString(h, RPMTAG_SUMMARY, u);
413
414     headerPutUint32(h, RPMTAG_SIZE, &zero, 1);
415
416     headerPutString(h, RPMTAG_PROVIDENAME, u);
417     headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
418     headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pflags, 1);
419         
420     headerPutString(h, RPMTAG_PROVIDENAME, n);
421     headerPutString(h, RPMTAG_PROVIDEVERSION, evr);
422     headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pflags, 1);
423
424     headerPutString(h, RPMTAG_RPMVERSION, RPMVERSION);
425     headerPutString(h, RPMTAG_BUILDHOST, buildhost);
426     headerPutString(h, RPMTAG_SOURCERPM, "(none)");
427
428     {   rpm_tid_t tid = rpmtsGetTid(ts);
429         headerPutUint32(h, RPMTAG_INSTALLTIME, &tid, 1);
430         headerPutUint32(h, RPMTAG_BUILDTIME, &tid, 1);
431     }
432     rc = 0;
433
434 exit:
435     pgpFreeDig(dig);
436     free(n);
437     free(u);
438     free(v);
439     free(r);
440     free(evr);
441     free(enc);
442     free(d);
443
444     return rc;
445 }
446
447 rpmRC rpmtsImportPubkey(const rpmts ts, const unsigned char * pkt, size_t pktlen)
448 {
449     Header h = headerNew();
450     rpmRC rc = RPMRC_FAIL;              /* assume failure */
451     rpmPubkey pubkey = NULL;
452     rpmKeyring keyring = rpmtsGetKeyring(ts, 1);
453
454     if ((pubkey = rpmPubkeyNew(pkt, pktlen)) == NULL)
455         goto exit;
456     if (rpmKeyringAddKey(keyring, pubkey) != 0)
457         goto exit;
458     if (makePubkeyHeader(ts, pubkey, h) != 0) 
459         goto exit;
460
461     /* Add header to database. */
462     if (rpmtsOpenDB(ts, (O_RDWR|O_CREAT)))
463         goto exit;
464     if (rpmdbAdd(rpmtsGetRdb(ts), rpmtsGetTid(ts), h, NULL, NULL) != 0)
465         goto exit;
466     rc = RPMRC_OK;
467
468 exit:
469     /* Clean up. */
470     headerFree(h);
471     rpmPubkeyFree(pubkey);
472     rpmKeyringFree(keyring);
473     return rc;
474 }
475
476 int rpmtsSetSolveCallback(rpmts ts,
477                 int (*solve) (rpmts ts, rpmds key, const void * data),
478                 const void * solveData)
479 {
480     int rc = 0;
481
482     if (ts) {
483         ts->solve = solve;
484         ts->solveData = solveData;
485     }
486     return rc;
487 }
488
489 rpmps rpmtsProblems(rpmts ts)
490 {
491     rpmps ps = NULL;
492     if (ts) {
493         if (ts->probs)
494             ps = rpmpsLink(ts->probs, RPMDBG_M("rpmtsProblems"));
495     }
496     return ps;
497 }
498
499 void rpmtsCleanProblems(rpmts ts)
500 {
501     if (ts && ts->probs) {
502         ts->probs = rpmpsFree(ts->probs);
503     }
504 }
505
506 void rpmtsClean(rpmts ts)
507 {
508     rpmtsi pi; rpmte p;
509
510     if (ts == NULL)
511         return;
512
513     /* Clean up after dependency checks. */
514     pi = rpmtsiInit(ts);
515     while ((p = rpmtsiNext(pi, 0)) != NULL)
516         rpmteCleanDS(p);
517     pi = rpmtsiFree(pi);
518
519     ts->addedPackages = rpmalFree(ts->addedPackages);
520     ts->numAddedPackages = 0;
521
522     rpmtsCleanProblems(ts);
523 }
524
525 void rpmtsEmpty(rpmts ts)
526 {
527     if (ts == NULL)
528         return;
529
530     rpmtsClean(ts);
531
532     for (int oc = 0; oc < ts->orderCount; oc++) {
533         ts->order[oc] = rpmteFree(ts->order[oc]);
534     }
535
536     ts->orderCount = 0;
537     ts->ntrees = 0;
538     ts->maxDepth = 0;
539
540     ts->numRemovedPackages = 0;
541     return;
542 }
543
544 static void rpmtsPrintStat(const char * name, struct rpmop_s * op)
545 {
546     static const unsigned int scale = (1000 * 1000);
547     if (op != NULL && op->count > 0)
548         fprintf(stderr, "   %s %6d %6lu.%06lu MB %6lu.%06lu secs\n",
549                 name, op->count,
550                 (unsigned long)op->bytes/scale, (unsigned long)op->bytes%scale,
551                 op->usecs/scale, op->usecs%scale);
552 }
553
554 static void rpmtsPrintStats(rpmts ts)
555 {
556     (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_TOTAL), 0);
557
558     rpmtsPrintStat("total:       ", rpmtsOp(ts, RPMTS_OP_TOTAL));
559     rpmtsPrintStat("check:       ", rpmtsOp(ts, RPMTS_OP_CHECK));
560     rpmtsPrintStat("order:       ", rpmtsOp(ts, RPMTS_OP_ORDER));
561     rpmtsPrintStat("fingerprint: ", rpmtsOp(ts, RPMTS_OP_FINGERPRINT));
562     rpmtsPrintStat("install:     ", rpmtsOp(ts, RPMTS_OP_INSTALL));
563     rpmtsPrintStat("erase:       ", rpmtsOp(ts, RPMTS_OP_ERASE));
564     rpmtsPrintStat("scriptlets:  ", rpmtsOp(ts, RPMTS_OP_SCRIPTLETS));
565     rpmtsPrintStat("compress:    ", rpmtsOp(ts, RPMTS_OP_COMPRESS));
566     rpmtsPrintStat("uncompress:  ", rpmtsOp(ts, RPMTS_OP_UNCOMPRESS));
567     rpmtsPrintStat("digest:      ", rpmtsOp(ts, RPMTS_OP_DIGEST));
568     rpmtsPrintStat("signature:   ", rpmtsOp(ts, RPMTS_OP_SIGNATURE));
569     rpmtsPrintStat("dbadd:       ", rpmtsOp(ts, RPMTS_OP_DBADD));
570     rpmtsPrintStat("dbremove:    ", rpmtsOp(ts, RPMTS_OP_DBREMOVE));
571     rpmtsPrintStat("dbget:       ", rpmtsOp(ts, RPMTS_OP_DBGET));
572     rpmtsPrintStat("dbput:       ", rpmtsOp(ts, RPMTS_OP_DBPUT));
573     rpmtsPrintStat("dbdel:       ", rpmtsOp(ts, RPMTS_OP_DBDEL));
574 }
575
576 rpmts rpmtsFree(rpmts ts)
577 {
578     if (ts == NULL)
579         return NULL;
580
581     if (ts->nrefs > 1)
582         return rpmtsUnlink(ts, RPMDBG_M("tsCreate"));
583
584     rpmtsEmpty(ts);
585
586     (void) rpmtsCloseDB(ts);
587
588     ts->removedPackages = _free(ts->removedPackages);
589
590     ts->dsi = _free(ts->dsi);
591
592     if (ts->scriptFd != NULL) {
593         ts->scriptFd = fdFree(ts->scriptFd, RPMDBG_M("rpmtsFree"));
594         ts->scriptFd = NULL;
595     }
596     ts->rootDir = _free(ts->rootDir);
597     ts->currDir = _free(ts->currDir);
598
599     ts->order = _free(ts->order);
600     ts->orderAlloced = 0;
601
602     ts->keyring = rpmKeyringFree(ts->keyring);
603     ts->netsharedPaths = argvFree(ts->netsharedPaths);
604     ts->installLangs = argvFree(ts->installLangs);
605
606     if (_rpmts_stats)
607         rpmtsPrintStats(ts);
608
609     (void) rpmtsUnlink(ts, RPMDBG_M("tsCreate"));
610
611     ts = _free(ts);
612
613     return NULL;
614 }
615
616 rpmVSFlags rpmtsVSFlags(rpmts ts)
617 {
618     rpmVSFlags vsflags = 0;
619     if (ts != NULL)
620         vsflags = ts->vsflags;
621     return vsflags;
622 }
623
624 rpmVSFlags rpmtsSetVSFlags(rpmts ts, rpmVSFlags vsflags)
625 {
626     rpmVSFlags ovsflags = 0;
627     if (ts != NULL) {
628         ovsflags = ts->vsflags;
629         ts->vsflags = vsflags;
630     }
631     return ovsflags;
632 }
633
634 const char * rpmtsRootDir(rpmts ts)
635 {
636     const char * rootDir = NULL;
637
638     if (ts != NULL && ts->rootDir != NULL) {
639         urltype ut = urlPath(ts->rootDir, &rootDir);
640         switch (ut) {
641         case URL_IS_UNKNOWN:
642         case URL_IS_PATH:
643             break;
644         /* XXX these shouldn't be allowed as rootdir! */
645         case URL_IS_HTTPS:
646         case URL_IS_HTTP:
647         case URL_IS_HKP:
648         case URL_IS_FTP:
649         case URL_IS_DASH:
650         default:
651             rootDir = "/";
652             break;
653         }
654     }
655     return rootDir;
656 }
657
658 int rpmtsSetRootDir(rpmts ts, const char * rootDir)
659 {
660     if (ts == NULL || (rootDir && rootDir[0] != '/')) {
661         return -1;
662     }
663
664     ts->rootDir = _free(ts->rootDir);
665     /* Ensure clean path with a trailing slash */
666     ts->rootDir = rootDir ? rpmGetPath(rootDir, NULL) : xstrdup("/");
667     if (!rstreq(ts->rootDir, "/")) {
668         rstrcat(&ts->rootDir, "/");
669     }
670     return 0;
671 }
672
673 const char * rpmtsCurrDir(rpmts ts)
674 {
675     const char * currDir = NULL;
676     if (ts != NULL) {
677         currDir = ts->currDir;
678     }
679     return currDir;
680 }
681
682 void rpmtsSetCurrDir(rpmts ts, const char * currDir)
683 {
684     if (ts != NULL) {
685         ts->currDir = _free(ts->currDir);
686         if (currDir)
687             ts->currDir = xstrdup(currDir);
688     }
689 }
690
691 FD_t rpmtsScriptFd(rpmts ts)
692 {
693     FD_t scriptFd = NULL;
694     if (ts != NULL) {
695         scriptFd = ts->scriptFd;
696     }
697     return scriptFd;
698 }
699
700 void rpmtsSetScriptFd(rpmts ts, FD_t scriptFd)
701 {
702
703     if (ts != NULL) {
704         if (ts->scriptFd != NULL) {
705             ts->scriptFd = fdFree(ts->scriptFd, 
706                                   RPMDBG_M("rpmtsSetScriptFd"));
707             ts->scriptFd = NULL;
708         }
709         if (scriptFd != NULL)
710             ts->scriptFd = fdLink((void *)scriptFd, 
711                                   RPMDBG_M("rpmtsSetScriptFd"));
712     }
713 }
714
715 int rpmtsSELinuxEnabled(rpmts ts)
716 {
717     return (ts != NULL ? (ts->selinuxEnabled > 0) : 0);
718 }
719
720 int rpmtsChrootDone(rpmts ts)
721 {
722     return (ts != NULL ? ts->chrootDone : 0);
723 }
724
725 int rpmtsSetChrootDone(rpmts ts, int chrootDone)
726 {
727     int ochrootDone = 0;
728     if (ts != NULL) {
729         ochrootDone = ts->chrootDone;
730         rpmdbSetChrootDone(rpmtsGetRdb(ts), chrootDone);
731         ts->chrootDone = chrootDone;
732     }
733     return ochrootDone;
734 }
735
736 rpm_tid_t rpmtsGetTid(rpmts ts)
737 {
738     rpm_tid_t tid = (rpm_tid_t)-1;  /* XXX -1 is time(2) error return. */
739     if (ts != NULL) {
740         tid = ts->tid;
741     }
742     return tid;
743 }
744
745 rpm_tid_t rpmtsSetTid(rpmts ts, rpm_tid_t tid)
746 {
747     rpm_tid_t otid = (rpm_tid_t)-1; /* XXX -1 is time(2) error return. */
748     if (ts != NULL) {
749         otid = ts->tid;
750         ts->tid = tid;
751     }
752     return otid;
753 }
754
755 rpmdb rpmtsGetRdb(rpmts ts)
756 {
757     rpmdb rdb = NULL;
758     if (ts != NULL) {
759         rdb = ts->rdb;
760     }
761     return rdb;
762 }
763
764 int rpmtsInitDSI(const rpmts ts)
765 {
766     rpmDiskSpaceInfo dsi;
767     struct stat sb;
768     int rc;
769     int i;
770
771     if (rpmtsFilterFlags(ts) & RPMPROB_FILTER_DISKSPACE)
772         return 0;
773
774     rpmlog(RPMLOG_DEBUG, "mounted filesystems:\n");
775     rpmlog(RPMLOG_DEBUG,
776         "    i        dev    bsize       bavail       iavail mount point\n");
777
778     rc = rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount);
779     if (rc || ts->filesystems == NULL || ts->filesystemCount <= 0)
780         return rc;
781
782     /* Get available space on mounted file systems. */
783
784     ts->dsi = _free(ts->dsi);
785     ts->dsi = xcalloc((ts->filesystemCount + 1), sizeof(*ts->dsi));
786
787     dsi = ts->dsi;
788
789     if (dsi != NULL)
790     for (i = 0; (i < ts->filesystemCount) && dsi; i++, dsi++) {
791 #if STATFS_IN_SYS_STATVFS
792         struct statvfs sfb;
793         memset(&sfb, 0, sizeof(sfb));
794         rc = statvfs(ts->filesystems[i], &sfb);
795 #else
796         struct statfs sfb;
797         memset(&sfb, 0, sizeof(sfb));
798 #  if STAT_STATFS4
799 /* This platform has the 4-argument version of the statfs call.  The last two
800  * should be the size of struct statfs and 0, respectively.  The 0 is the
801  * filesystem type, and is always 0 when statfs is called on a mounted
802  * filesystem, as we're doing.
803  */
804         rc = statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0);
805 #  else
806         rc = statfs(ts->filesystems[i], &sfb);
807 #  endif
808 #endif
809         if (rc)
810             break;
811
812         rc = stat(ts->filesystems[i], &sb);
813         if (rc)
814             break;
815         dsi->dev = sb.st_dev;
816
817         dsi->bsize = sfb.f_bsize;
818         dsi->bneeded = 0;
819         dsi->ineeded = 0;
820 #ifdef STATFS_HAS_F_BAVAIL
821         dsi->bavail = sfb.f_bavail;
822 #else
823 /* FIXME: the statfs struct doesn't have a member to tell how many blocks are
824  * available for non-superusers.  f_blocks - f_bfree is probably too big, but
825  * it's about all we can do.
826  */
827         dsi->bavail = sfb.f_blocks - sfb.f_bfree;
828 #endif
829         /* XXX Avoid FAT and other file systems that have not inodes. */
830         /* XXX assigning negative value to unsigned type */
831         dsi->iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
832                                 ? sfb.f_ffree : -1;
833         rpmlog(RPMLOG_DEBUG, 
834                 "%5d 0x%08x %8" PRId64 " %12" PRId64 " %12" PRId64" %s\n",
835                 i, (unsigned) dsi->dev, dsi->bsize,
836                 dsi->bavail, dsi->iavail,
837                 ts->filesystems[i]);
838     }
839     return rc;
840 }
841
842 void rpmtsUpdateDSI(const rpmts ts, dev_t dev,
843                 rpm_loff_t fileSize, rpm_loff_t prevSize, rpm_loff_t fixupSize,
844                 rpmFileAction action)
845 {
846     rpmDiskSpaceInfo dsi;
847     int64_t bneeded;
848
849     dsi = ts->dsi;
850     if (dsi) {
851         while (dsi->bsize && dsi->dev != dev)
852             dsi++;
853         if (dsi->bsize == 0)
854             dsi = NULL;
855     }
856     if (dsi == NULL)
857         return;
858
859     bneeded = BLOCK_ROUND(fileSize, dsi->bsize);
860
861     switch (action) {
862     case FA_BACKUP:
863     case FA_SAVE:
864     case FA_ALTNAME:
865         dsi->ineeded++;
866         dsi->bneeded += bneeded;
867         break;
868
869     /*
870      * FIXME: If two packages share a file (same md5sum), and
871      * that file is being replaced on disk, will dsi->bneeded get
872      * adjusted twice? Quite probably!
873      */
874     case FA_CREATE:
875         dsi->bneeded += bneeded;
876         dsi->bneeded -= BLOCK_ROUND(prevSize, dsi->bsize);
877         break;
878
879     case FA_ERASE:
880         dsi->ineeded--;
881         dsi->bneeded -= bneeded;
882         break;
883
884     default:
885         break;
886     }
887
888     if (fixupSize)
889         dsi->bneeded -= BLOCK_ROUND(fixupSize, dsi->bsize);
890 }
891
892 void rpmtsCheckDSIProblems(const rpmts ts, const rpmte te)
893 {
894     rpmDiskSpaceInfo dsi;
895     rpmps ps;
896     int fc;
897     int i;
898
899     if (ts->filesystems == NULL || ts->filesystemCount <= 0)
900         return;
901
902     dsi = ts->dsi;
903     if (dsi == NULL)
904         return;
905     fc = rpmfiFC(rpmteFI(te));
906     if (fc <= 0)
907         return;
908
909     ps = rpmtsProblems(ts);
910     for (i = 0; i < ts->filesystemCount; i++, dsi++) {
911
912         if (dsi->bavail >= 0 && adj_fs_blocks(dsi->bneeded) > dsi->bavail) {
913             rpmpsAppend(ps, RPMPROB_DISKSPACE,
914                         rpmteNEVRA(te), rpmteKey(te),
915                         ts->filesystems[i], NULL, NULL,
916            (adj_fs_blocks(dsi->bneeded) - dsi->bavail) * dsi->bsize);
917         }
918
919         if (dsi->iavail >= 0 && adj_fs_blocks(dsi->ineeded) > dsi->iavail) {
920             rpmpsAppend(ps, RPMPROB_DISKNODES,
921                         rpmteNEVRA(te), rpmteKey(te),
922                         ts->filesystems[i], NULL, NULL,
923             (adj_fs_blocks(dsi->ineeded) - dsi->iavail));
924         }
925     }
926     ps = rpmpsFree(ps);
927 }
928
929 void * rpmtsNotify(rpmts ts, rpmte te,
930                 rpmCallbackType what, rpm_loff_t amount, rpm_loff_t total)
931 {
932     void * ptr = NULL;
933     if (ts && ts->notify) {
934         Header h = NULL;
935         fnpyKey cbkey = NULL;
936         if (te) {
937             h = rpmteHeader(te);
938             cbkey = rpmteKey(te);
939         }
940         ptr = ts->notify(h, what, amount, total, cbkey, ts->notifyData);
941
942         if (h) {
943             headerUnlink(h); /* undo rpmteHeader() ref */
944         }
945     }
946     return ptr;
947 }
948
949 int rpmtsNElements(rpmts ts)
950 {
951     int nelements = 0;
952     if (ts != NULL && ts->order != NULL) {
953         nelements = ts->orderCount;
954     }
955     return nelements;
956 }
957
958 rpmte rpmtsElement(rpmts ts, int ix)
959 {
960     rpmte te = NULL;
961     if (ts != NULL && ts->order != NULL) {
962         if (ix >= 0 && ix < ts->orderCount)
963             te = ts->order[ix];
964     }
965     return te;
966 }
967
968 rpmprobFilterFlags rpmtsFilterFlags(rpmts ts)
969 {
970     return (ts != NULL ? ts->ignoreSet : 0);
971 }
972
973 rpmtransFlags rpmtsFlags(rpmts ts)
974 {
975     return (ts != NULL ? ts->transFlags : 0);
976 }
977
978 rpmtransFlags rpmtsSetFlags(rpmts ts, rpmtransFlags transFlags)
979 {
980     rpmtransFlags otransFlags = 0;
981     if (ts != NULL) {
982         otransFlags = ts->transFlags;
983         ts->transFlags = transFlags;
984     }
985     return otransFlags;
986 }
987
988 rpmSpec rpmtsSpec(rpmts ts)
989 {
990     return ts->spec;
991 }
992
993 rpmSpec rpmtsSetSpec(rpmts ts, rpmSpec spec)
994 {
995     rpmSpec ospec = ts->spec;
996     ts->spec = spec;
997     return ospec;
998 }
999
1000 rpm_color_t rpmtsColor(rpmts ts)
1001 {
1002     return (ts != NULL ? ts->color : 0);
1003 }
1004
1005 rpm_color_t rpmtsSetColor(rpmts ts, rpm_color_t color)
1006 {
1007     rpm_color_t ocolor = 0;
1008     if (ts != NULL) {
1009         ocolor = ts->color;
1010         ts->color = color;
1011     }
1012     return ocolor;
1013 }
1014
1015 rpm_color_t rpmtsPrefColor(rpmts ts)
1016 {
1017     return (ts != NULL ? ts->prefcolor : 0);
1018 }
1019
1020 rpmop rpmtsOp(rpmts ts, rpmtsOpX opx)
1021 {
1022     rpmop op = NULL;
1023
1024     if (ts != NULL && opx >= 0 && opx < RPMTS_OP_MAX)
1025         op = ts->ops + opx;
1026     return op;
1027 }
1028
1029 int rpmtsSetNotifyCallback(rpmts ts,
1030                 rpmCallbackFunction notify, rpmCallbackData notifyData)
1031 {
1032     if (ts != NULL) {
1033         ts->notify = notify;
1034         ts->notifyData = notifyData;
1035     }
1036     return 0;
1037 }
1038
1039 int rpmtsGetKeys(const rpmts ts, fnpyKey ** ep, int * nep)
1040 {
1041     int rc = 0;
1042
1043     if (nep) *nep = ts->orderCount;
1044     if (ep) {
1045         rpmtsi pi;      rpmte p;
1046         fnpyKey * e;
1047
1048         *ep = e = xmalloc(ts->orderCount * sizeof(*e));
1049         pi = rpmtsiInit(ts);
1050         while ((p = rpmtsiNext(pi, 0)) != NULL) {
1051             switch (rpmteType(p)) {
1052             case TR_ADDED:
1053                 *e = rpmteKey(p);
1054                 break;
1055             case TR_REMOVED:
1056             default:
1057                 *e = NULL;
1058                 break;
1059             }
1060             e++;
1061         }
1062         pi = rpmtsiFree(pi);
1063     }
1064     return rc;
1065 }
1066
1067 rpmts rpmtsCreate(void)
1068 {
1069     rpmts ts;
1070
1071     ts = xcalloc(1, sizeof(*ts));
1072     memset(&ts->ops, 0, sizeof(ts->ops));
1073     (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_TOTAL), -1);
1074     ts->filesystemCount = 0;
1075     ts->filesystems = NULL;
1076     ts->dsi = NULL;
1077
1078     ts->solve = NULL;
1079     ts->solveData = NULL;
1080
1081     ts->rdb = NULL;
1082     ts->dbmode = O_RDONLY;
1083
1084     ts->scriptFd = NULL;
1085     ts->tid = (rpm_tid_t) time(NULL);
1086     ts->delta = 5;
1087
1088     ts->color = rpmExpandNumeric("%{?_transaction_color}");
1089     ts->prefcolor = rpmExpandNumeric("%{?_prefer_color}")?:2;
1090
1091     ts->netsharedPaths = NULL;
1092     ts->installLangs = NULL;
1093     {   char *tmp = rpmExpand("%{_netsharedpath}", NULL);
1094         if (tmp && *tmp != '%') {
1095             argvSplit(&ts->netsharedPaths, tmp, ":");
1096         }
1097         free(tmp);
1098
1099         tmp = rpmExpand("%{_install_langs}", NULL);
1100         if (tmp && *tmp != '%') {
1101             ARGV_t langs = NULL;
1102             argvSplit(&langs, tmp, ":");        
1103             /* If we'll be installing all languages anyway, don't bother */
1104             for (ARGV_t l = langs; *l; l++) {
1105                 if (rstreq(*l, "all")) {
1106                     langs = argvFree(langs);
1107                     break;
1108                 }
1109             }
1110             ts->installLangs = langs;
1111         }
1112         free(tmp);
1113     }
1114
1115     ts->numRemovedPackages = 0;
1116     ts->allocedRemovedPackages = ts->delta;
1117     ts->removedPackages = xcalloc(ts->allocedRemovedPackages,
1118                         sizeof(*ts->removedPackages));
1119
1120     ts->rootDir = NULL;
1121     ts->currDir = NULL;
1122     ts->chrootDone = 0;
1123
1124     ts->selinuxEnabled = is_selinux_enabled();
1125
1126     ts->numAddedPackages = 0;
1127     ts->addedPackages = NULL;
1128
1129     ts->orderAlloced = 0;
1130     ts->orderCount = 0;
1131     ts->order = NULL;
1132     ts->ntrees = 0;
1133     ts->maxDepth = 0;
1134
1135     ts->probs = NULL;
1136
1137     ts->keyring = NULL;
1138
1139     ts->nrefs = 0;
1140
1141     return rpmtsLink(ts, RPMDBG_M("tsCreate"));
1142 }
1143