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