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