split DEP_PRE into DEP_PRE_IN and DEP_PRE_UN
[platform/upstream/libsolv.git] / ext / repo_rpmdb.c
1 /*
2  * Copyright (c) 2007-2012, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * repo_rpmdb
10  *
11  * convert rpm db to repo
12  *
13  */
14
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <limits.h>
18 #include <fcntl.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <assert.h>
24 #include <stdint.h>
25 #include <errno.h>
26
27 #include <rpm/rpmio.h>
28 #include <rpm/rpmpgp.h>
29 #ifndef RPM5
30 #include <rpm/header.h>
31 #endif
32 #include <rpm/rpmdb.h>
33
34 #ifndef DB_CREATE
35 # if defined(SUSE) || defined(HAVE_RPM_DB_H)
36 #  include <rpm/db.h>
37 # else
38 #  include <db.h>
39 # endif
40 #endif
41
42 #include "pool.h"
43 #include "repo.h"
44 #include "hash.h"
45 #include "util.h"
46 #include "queue.h"
47 #include "chksum.h"
48 #include "repo_rpmdb.h"
49 #include "repo_solv.h"
50
51 /* 3: added triggers */
52 /* 4: fixed triggers */
53 #define RPMDB_COOKIE_VERSION 4
54
55 #define TAG_NAME                1000
56 #define TAG_VERSION             1001
57 #define TAG_RELEASE             1002
58 #define TAG_EPOCH               1003
59 #define TAG_SUMMARY             1004
60 #define TAG_DESCRIPTION         1005
61 #define TAG_BUILDTIME           1006
62 #define TAG_BUILDHOST           1007
63 #define TAG_INSTALLTIME         1008
64 #define TAG_SIZE                1009
65 #define TAG_DISTRIBUTION        1010
66 #define TAG_VENDOR              1011
67 #define TAG_LICENSE             1014
68 #define TAG_PACKAGER            1015
69 #define TAG_GROUP               1016
70 #define TAG_URL                 1020
71 #define TAG_ARCH                1022
72 #define TAG_FILESIZES           1028
73 #define TAG_FILEMODES           1030
74 #define TAG_FILEMD5S            1035
75 #define TAG_FILELINKTOS         1036
76 #define TAG_FILEFLAGS           1037
77 #define TAG_SOURCERPM           1044
78 #define TAG_PROVIDENAME         1047
79 #define TAG_REQUIREFLAGS        1048
80 #define TAG_REQUIRENAME         1049
81 #define TAG_REQUIREVERSION      1050
82 #define TAG_NOSOURCE            1051
83 #define TAG_NOPATCH             1052
84 #define TAG_CONFLICTFLAGS       1053
85 #define TAG_CONFLICTNAME        1054
86 #define TAG_CONFLICTVERSION     1055
87 #define TAG_TRIGGERNAME         1066
88 #define TAG_TRIGGERVERSION      1067
89 #define TAG_TRIGGERFLAGS        1068
90 #define TAG_CHANGELOGTIME       1080
91 #define TAG_CHANGELOGNAME       1081
92 #define TAG_CHANGELOGTEXT       1082
93 #define TAG_OBSOLETENAME        1090
94 #define TAG_FILEDEVICES         1095
95 #define TAG_FILEINODES          1096
96 #define TAG_SOURCEPACKAGE       1106
97 #define TAG_PROVIDEFLAGS        1112
98 #define TAG_PROVIDEVERSION      1113
99 #define TAG_OBSOLETEFLAGS       1114
100 #define TAG_OBSOLETEVERSION     1115
101 #define TAG_DIRINDEXES          1116
102 #define TAG_BASENAMES           1117
103 #define TAG_DIRNAMES            1118
104 #define TAG_PAYLOADFORMAT       1124
105 #define TAG_PATCHESNAME         1133
106 #define TAG_FILECOLORS          1140
107 #define TAG_SUGGESTSNAME        1156
108 #define TAG_SUGGESTSVERSION     1157
109 #define TAG_SUGGESTSFLAGS       1158
110 #define TAG_ENHANCESNAME        1159
111 #define TAG_ENHANCESVERSION     1160
112 #define TAG_ENHANCESFLAGS       1161
113
114 /* rpm5 tags */
115 #define TAG_DISTEPOCH           1218
116
117 /* rpm4 tags */
118 #define TAG_LONGFILESIZES       5008
119 #define TAG_LONGSIZE            5009
120
121 /* signature tags */
122 #define TAG_SIGBASE             256
123 #define TAG_SIGMD5              (TAG_SIGBASE + 5)
124 #define TAG_SHA1HEADER          (TAG_SIGBASE + 13)
125
126 #define SIGTAG_SIZE             1000
127 #define SIGTAG_PGP              1002    /* RSA signature */
128 #define SIGTAG_MD5              1004    /* header+payload md5 checksum */
129 #define SIGTAG_GPG              1005    /* DSA signature */
130
131 #define DEP_LESS                (1 << 1)
132 #define DEP_GREATER             (1 << 2)
133 #define DEP_EQUAL               (1 << 3)
134 #define DEP_STRONG              (1 << 27)
135 #define DEP_PRE_IN              ((1 << 6) | (1 << 9) | (1 << 10))
136 #define DEP_PRE_UN              ((1 << 6) | (1 << 11) | (1 << 12))
137
138 #define FILEFLAG_GHOST          (1 <<  6)
139
140
141 #ifdef RPM5
142 # define RPM_INDEX_SIZE 4
143 #else
144 # define RPM_INDEX_SIZE 8
145 #endif
146
147
148 typedef struct rpmhead {
149   int cnt;
150   int dcnt;
151   unsigned char *dp;
152   int forcebinary;              /* sigh, see rh#478907 */
153   unsigned char data[1];
154 } RpmHead;
155
156
157 static inline unsigned char *
158 headfindtag(RpmHead *h, int tag)
159 {
160   unsigned int i;
161   unsigned char *d, taga[4];
162   d = h->dp - 16;
163   taga[0] = tag >> 24;
164   taga[1] = tag >> 16;
165   taga[2] = tag >> 8;
166   taga[3] = tag;
167   for (i = 0; i < h->cnt; i++, d -= 16)
168     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
169       return d;
170   return 0;
171 }
172
173 static int
174 headexists(RpmHead *h, int tag)
175 {
176   return headfindtag(h, tag) ? 1 : 0;
177 }
178
179 static unsigned int *
180 headint32array(RpmHead *h, int tag, int *cnt)
181 {
182   unsigned int i, o, *r;
183   unsigned char *d = headfindtag(h, tag);
184
185   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 4)
186     return 0;
187   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
188   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
189   if (o + 4 * i > h->dcnt)
190     return 0;
191   d = h->dp + o;
192   r = solv_calloc(i ? i : 1, sizeof(unsigned int));
193   if (cnt)
194     *cnt = i;
195   for (o = 0; o < i; o++, d += 4)
196     r[o] = d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
197   return r;
198 }
199
200 /* returns the first entry of an integer array */
201 static unsigned int
202 headint32(RpmHead *h, int tag)
203 {
204   unsigned int i, o;
205   unsigned char *d = headfindtag(h, tag);
206
207   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 4)
208     return 0;
209   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
210   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
211   if (i == 0 || o + 4 * i > h->dcnt)
212     return 0;
213   d = h->dp + o;
214   return d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
215 }
216
217 static unsigned long long *
218 headint64array(RpmHead *h, int tag, int *cnt)
219 {
220   unsigned int i, o;
221   unsigned long long *r;
222   unsigned char *d = headfindtag(h, tag);
223
224   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 5)
225     return 0;
226   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
227   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
228   if (o + 8 * i > h->dcnt)
229     return 0;
230   d = h->dp + o;
231   r = solv_calloc(i ? i : 1, sizeof(unsigned long long));
232   if (cnt)
233     *cnt = i;
234   for (o = 0; o < i; o++, d += 8)
235     {
236       unsigned int x = d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
237       r[o] = (unsigned long long)x << 32 | (d[4] << 24 | d[5] << 16 | d[6] << 8 | d[7]);
238     }
239   return r;
240 }
241
242 /* returns the first entry of an 64bit integer array */
243 static unsigned long long
244 headint64(RpmHead *h, int tag)
245 {
246   unsigned int i, o;
247   unsigned char *d = headfindtag(h, tag);
248   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 5)
249     return 0;
250   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
251   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
252   if (i == 0 || o + 8 * i > h->dcnt)
253     return 0;
254   d = h->dp + o;
255   i = d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
256   return (unsigned long long)i << 32 | (d[4] << 24 | d[5] << 16 | d[6] << 8 | d[7]);
257 }
258
259 static unsigned int *
260 headint16array(RpmHead *h, int tag, int *cnt)
261 {
262   unsigned int i, o, *r;
263   unsigned char *d = headfindtag(h, tag);
264
265   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 3)
266     return 0;
267   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
268   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
269   if (o + 4 * i > h->dcnt)
270     return 0;
271   d = h->dp + o;
272   r = solv_calloc(i ? i : 1, sizeof(unsigned int));
273   if (cnt)
274     *cnt = i;
275   for (o = 0; o < i; o++, d += 2)
276     r[o] = d[0] << 8 | d[1];
277   return r;
278 }
279
280 static char *
281 headstring(RpmHead *h, int tag)
282 {
283   unsigned int o;
284   unsigned char *d = headfindtag(h, tag);
285   /* 6: STRING, 9: I18NSTRING */
286   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || (d[7] != 6 && d[7] != 9))
287     return 0;
288   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
289   if (o >= h->dcnt)
290     return 0;
291   return (char *)h->dp + o;
292 }
293
294 static char **
295 headstringarray(RpmHead *h, int tag, int *cnt)
296 {
297   unsigned int i, o;
298   unsigned char *d = headfindtag(h, tag);
299   char **r;
300
301   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 8)
302     return 0;
303   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
304   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
305   r = solv_calloc(i ? i : 1, sizeof(char *));
306   if (cnt)
307     *cnt = i;
308   d = h->dp + o;
309   for (o = 0; o < i; o++)
310     {
311       r[o] = (char *)d;
312       if (o + 1 < i)
313         d += strlen((char *)d) + 1;
314       if (d >= h->dp + h->dcnt)
315         {
316           solv_free(r);
317           return 0;
318         }
319     }
320   return r;
321 }
322
323 static unsigned char *
324 headbinary(RpmHead *h, int tag, unsigned int *sizep)
325 {
326   unsigned int i, o;
327   unsigned char *d = headfindtag(h, tag);
328   if (!d || d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 7)
329     return 0;
330   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
331   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
332   if (o > h->dcnt || o + i < o || o + i > h->dcnt)
333     return 0;
334   if (sizep)
335     *sizep = i;
336   return h->dp + o;
337 }
338
339 static char *headtoevr(RpmHead *h)
340 {
341   unsigned int epoch;
342   char *version, *v;
343   char *release;
344   char *evr;
345   char *distepoch;
346
347   version  = headstring(h, TAG_VERSION);
348   release  = headstring(h, TAG_RELEASE);
349   epoch = headint32(h, TAG_EPOCH);
350   if (!version || !release)
351     {
352       fprintf(stderr, "headtoevr: bad rpm header\n");
353       return 0;
354     }
355   for (v = version; *v >= '0' && *v <= '9'; v++)
356     ;
357   if (epoch || (v != version && *v == ':'))
358     {
359       char epochbuf[11];        /* 32bit decimal will fit in */
360       sprintf(epochbuf, "%u", epoch);
361       evr = solv_malloc(strlen(epochbuf) + 1 + strlen(version) + 1 + strlen(release) + 1);
362       sprintf(evr, "%s:%s-%s", epochbuf, version, release);
363     }
364   else
365     {
366       evr = solv_malloc(strlen(version) + 1 + strlen(release) + 1);
367       sprintf(evr, "%s-%s", version, release);
368     }
369   distepoch = headstring(h, TAG_DISTEPOCH);
370   if (distepoch && *distepoch)
371     {
372       int l = strlen(evr);
373       evr = solv_realloc(evr, l + strlen(distepoch) + 2);
374       evr[l++] = ':';
375       strcpy(evr + l, distepoch);
376     }
377   return evr;
378 }
379
380
381 static void
382 setutf8string(Repodata *repodata, Id handle, Id tag, const char *str)
383 {
384   if (str[solv_validutf8(str)])
385     {
386       char *ustr = solv_latin1toutf8(str);      /* not utf8, assume latin1 */
387       repodata_set_str(repodata, handle, tag, ustr);
388       solv_free(ustr);
389     }
390   else
391     repodata_set_str(repodata, handle, tag, str);
392 }
393
394
395 #define MAKEDEPS_FILTER_WEAK    (1 << 0)
396 #define MAKEDEPS_FILTER_STRONG  (1 << 1)
397 #define MAKEDEPS_NO_RPMLIB      (1 << 2)
398
399 /*
400  * strong: 0: ignore strongness
401  *         1: filter to strong
402  *         2: filter to weak
403  */
404 static unsigned int
405 makedeps(Pool *pool, Repo *repo, RpmHead *rpmhead, int tagn, int tagv, int tagf, int flags)
406 {
407   char **n, **v;
408   unsigned int *f;
409   int i, cc, nc, vc, fc;
410   int haspre, premask;
411   unsigned int olddeps;
412   Id *ida;
413   int strong;
414
415   strong = flags & (MAKEDEPS_FILTER_STRONG|MAKEDEPS_FILTER_WEAK);
416   n = headstringarray(rpmhead, tagn, &nc);
417   if (!n || !nc)
418     return 0;
419   vc = fc = 0;
420   v = headstringarray(rpmhead, tagv, &vc);
421   f = headint32array(rpmhead, tagf, &fc);
422   if (!v || !f || nc != vc || nc != fc)
423     {
424       char *pkgname = rpm_query(rpmhead, 0);
425       pool_error(pool, 0, "bad dependency entries for %s: %d %d %d", pkgname ? pkgname : "<NULL>", nc, vc, fc);
426       solv_free(pkgname);
427       solv_free(n);
428       solv_free(v);
429       solv_free(f);
430       return 0;
431     }
432
433   cc = nc;
434   haspre = 0;   /* add no prereq marker */
435   premask = DEP_PRE_IN | DEP_PRE_UN;
436   if (flags)
437     {
438       /* we do filtering */
439       cc = 0;
440       for (i = 0; i < nc; i++)
441         {
442           if (strong && (f[i] & DEP_STRONG) != (strong == MAKEDEPS_FILTER_WEAK ? 0 : DEP_STRONG))
443             continue;
444           if ((flags & MAKEDEPS_NO_RPMLIB) != 0)
445             if (!strncmp(n[i], "rpmlib(", 7))
446               continue;
447           if ((f[i] & premask) != 0)
448             haspre = 1;
449           cc++;
450         }
451     }
452   else if (tagn == TAG_REQUIRENAME)
453     {
454       /* no filtering, just look for the first prereq */
455       for (i = 0; i < nc; i++)
456         if ((f[i] & premask) != 0)
457           {
458             haspre = 1;
459             break;
460           }
461     }
462   if (cc == 0)
463     {
464       solv_free(n);
465       solv_free(v);
466       solv_free(f);
467       return 0;
468     }
469   cc += haspre;         /* add slot for the prereq marker */
470   olddeps = repo_reserve_ids(repo, 0, cc);
471   ida = repo->idarraydata + olddeps;
472   for (i = 0; ; i++)
473     {
474       if (i == nc)
475         {
476           if (haspre != 1)
477             break;
478           haspre = 2;   /* pass two: prereqs */
479           i = 0;
480           *ida++ = SOLVABLE_PREREQMARKER;
481         }
482       if (strong && (f[i] & DEP_STRONG) != (strong == MAKEDEPS_FILTER_WEAK ? 0 : DEP_STRONG))
483         continue;
484       if (haspre == 1 && (f[i] & premask) != 0)
485         continue;
486       if (haspre == 2 && (f[i] & premask) == 0)
487         continue;
488       if ((flags & MAKEDEPS_NO_RPMLIB) != 0)
489         if (!strncmp(n[i], "rpmlib(", 7))
490           continue;
491       if (f[i] & (DEP_LESS|DEP_GREATER|DEP_EQUAL))
492         {
493           Id name, evr;
494           int flags = 0;
495           if ((f[i] & DEP_LESS) != 0)
496             flags |= REL_LT;
497           if ((f[i] & DEP_EQUAL) != 0)
498             flags |= REL_EQ;
499           if ((f[i] & DEP_GREATER) != 0)
500             flags |= REL_GT;
501           name = pool_str2id(pool, n[i], 1);
502           if (v[i][0] == '0' && v[i][1] == ':' && v[i][2])
503             evr = pool_str2id(pool, v[i] + 2, 1);
504           else
505             evr = pool_str2id(pool, v[i], 1);
506           *ida++ = pool_rel2id(pool, name, evr, flags, 1);
507         }
508       else
509         *ida++ = pool_str2id(pool, n[i], 1);
510     }
511   *ida++ = 0;
512   repo->idarraysize += cc + 1;
513   solv_free(n);
514   solv_free(v);
515   solv_free(f);
516   return olddeps;
517 }
518
519
520 static void
521 adddudata(Repodata *data, Id handle, RpmHead *rpmhead, char **dn, unsigned int *di, int fc, int dc)
522 {
523   Id did;
524   int i, fszc;
525   unsigned int *fkb, *fn, *fsz, *fm, *fino;
526   unsigned long long *fsz64;
527   unsigned int inotest[256], inotestok;
528
529   if (!fc)
530     return;
531   if ((fsz64 = headint64array(rpmhead, TAG_LONGFILESIZES, &fszc)) != 0)
532     {
533       /* convert to kbyte */
534       fsz = solv_malloc2(fszc, sizeof(*fsz));
535       for (i = 0; i < fszc; i++)
536         fsz[i] = fsz64[i] ? fsz64[i] / 1024 + 1 : 0;
537       solv_free(fsz64);
538     }
539   else if ((fsz = headint32array(rpmhead, TAG_FILESIZES, &fszc)) != 0)
540     {
541       /* convert to kbyte */
542       for (i = 0; i < fszc; i++)
543         if (fsz[i])
544           fsz[i] = fsz[i] / 1024 + 1;
545     }
546   else
547     return;
548   if (fc != fszc)
549     {
550       solv_free(fsz);
551       return;
552     }
553
554   /* stupid rpm records sizes of directories, so we have to check the mode */
555   fm = headint16array(rpmhead, TAG_FILEMODES, &fszc);
556   if (!fm || fc != fszc)
557     {
558       solv_free(fsz);
559       solv_free(fm);
560       return;
561     }
562   fino = headint32array(rpmhead, TAG_FILEINODES, &fszc);
563   if (!fino || fc != fszc)
564     {
565       solv_free(fsz);
566       solv_free(fm);
567       solv_free(fino);
568       return;
569     }
570
571   /* kill hardlinked entries */
572   inotestok = 0;
573   if (fc < sizeof(inotest))
574     {
575       /* quick test just hashing the inode numbers */
576       memset(inotest, 0, sizeof(inotest));
577       for (i = 0; i < fc; i++)
578         {
579           int off, bit;
580           if (fsz[i] == 0 || !S_ISREG(fm[i]))
581             continue;   /* does not matter */
582           off = (fino[i] >> 5) & (sizeof(inotest)/sizeof(*inotest) - 1);
583           bit = 1 << (fino[i] & 31);
584           if ((inotest[off] & bit) != 0)
585             break;
586           inotest[off] |= bit;
587         }
588       if (i == fc)
589         inotestok = 1;  /* no conflict found */
590     }
591   if (!inotestok)
592     {
593       /* hardlinked files are possible, check ino/dev pairs */
594       unsigned int *fdev = headint32array(rpmhead, TAG_FILEDEVICES, &fszc);
595       unsigned int *fx, j;
596       unsigned int mask, hash, hh;
597       if (!fdev || fc != fszc)
598         {
599           solv_free(fsz);
600           solv_free(fm);
601           solv_free(fdev);
602           solv_free(fino);
603           return;
604         }
605       mask = fc;
606       while ((mask & (mask - 1)) != 0)
607         mask = mask & (mask - 1);
608       mask <<= 2;
609       if (mask > sizeof(inotest)/sizeof(*inotest))
610         fx = solv_calloc(mask, sizeof(unsigned int));
611       else
612         {
613           fx = inotest;
614           memset(fx, 0, mask * sizeof(unsigned int));
615         }
616       mask--;
617       for (i = 0; i < fc; i++)
618         {
619           if (fsz[i] == 0 || !S_ISREG(fm[i]))
620             continue;
621           hash = (fino[i] + fdev[i] * 31) & mask;
622           hh = 7;
623           while ((j = fx[hash]) != 0)
624             {
625               if (fino[j - 1] == fino[i] && fdev[j - 1] == fdev[i])
626                 {
627                   fsz[i] = 0;   /* kill entry */
628                   break;
629                 }
630               hash = (hash + hh++) & mask;
631             }
632           if (!j)
633             fx[hash] = i + 1;
634         }
635       if (fx != inotest)
636         solv_free(fx);
637       solv_free(fdev);
638     }
639   solv_free(fino);
640
641   /* sum up inode count and kbytes for each directory */
642   fn = solv_calloc(dc, sizeof(unsigned int));
643   fkb = solv_calloc(dc, sizeof(unsigned int));
644   for (i = 0; i < fc; i++)
645     {
646       if (di[i] >= dc)
647         continue;
648       fn[di[i]]++;
649       if (fsz[i] == 0 || !S_ISREG(fm[i]))
650         continue;
651       fkb[di[i]] += fsz[i];
652     }
653   solv_free(fsz);
654   solv_free(fm);
655   /* commit */
656   for (i = 0; i < dc; i++)
657     {
658       if (!fn[i])
659         continue;
660       if (!*dn[i])
661         {
662           Solvable *s = data->repo->pool->solvables + handle;
663           if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
664             did = repodata_str2dir(data, "/usr/src", 1);
665           else
666             continue;   /* work around rpm bug */
667         }
668       else
669         did = repodata_str2dir(data, dn[i], 1);
670       repodata_add_dirnumnum(data, handle, SOLVABLE_DISKUSAGE, did, fkb[i], fn[i]);
671     }
672   solv_free(fn);
673   solv_free(fkb);
674 }
675
676 static void
677 addfilelist(Repodata *data, Id handle, RpmHead *rpmhead)
678 {
679   char **bn;
680   char **dn;
681   unsigned int *di;
682   int bnc, dnc, dic;
683   int i;
684   Id lastdid = 0;
685   int lastdii = -1;
686
687   if (!data)
688     return;
689   bn = headstringarray(rpmhead, TAG_BASENAMES, &bnc);
690   if (!bn)
691     return;
692   dn = headstringarray(rpmhead, TAG_DIRNAMES, &dnc);
693   if (!dn)
694     {
695       solv_free(bn);
696       return;
697     }
698   di = headint32array(rpmhead, TAG_DIRINDEXES, &dic);
699   if (!di)
700     {
701       solv_free(bn);
702       solv_free(dn);
703       return;
704     }
705   if (bnc != dic)
706     {
707       pool_error(data->repo->pool, 0, "bad filelist");
708       return;
709     }
710
711   adddudata(data, handle, rpmhead, dn, di, bnc, dnc);
712
713   for (i = 0; i < bnc; i++)
714     {
715       Id did;
716       char *b = bn[i];
717
718       if (di[i] == lastdii)
719         did = lastdid;
720       else
721         {
722           did = repodata_str2dir(data, dn[di[i]], 1);
723           if (!did)
724             did = repodata_str2dir(data, "/", 1);
725           lastdid = did;
726           lastdii = di[i];
727         }
728       if (b && *b == '/')       /* work around rpm bug */
729         b++;
730       repodata_add_dirstr(data, handle, SOLVABLE_FILELIST, did, b);
731     }
732   solv_free(bn);
733   solv_free(dn);
734   solv_free(di);
735 }
736
737 static void
738 addchangelog(Repodata *data, Id handle, RpmHead *rpmhead)
739 {
740   char **cn;
741   char **cx;
742   unsigned int *ct;
743   int i, cnc, cxc, ctc;
744   Queue hq;
745
746   ct = headint32array(rpmhead, TAG_CHANGELOGTIME, &ctc);
747   cx = headstringarray(rpmhead, TAG_CHANGELOGTEXT, &cxc);
748   cn = headstringarray(rpmhead, TAG_CHANGELOGNAME, &cnc);
749   if (!ct || !cx || !cn || !ctc || ctc != cxc || ctc != cnc)
750     {
751       solv_free(ct);
752       solv_free(cx);
753       solv_free(cn);
754       return;
755     }
756   queue_init(&hq);
757   for (i = 0; i < ctc; i++)
758     {
759       Id h = repodata_new_handle(data);
760       if (ct[i])
761         repodata_set_num(data, h, SOLVABLE_CHANGELOG_TIME, ct[i]);
762       if (cn[i])
763         setutf8string(data, h, SOLVABLE_CHANGELOG_AUTHOR, cn[i]);
764       if (cx[i])
765         setutf8string(data, h, SOLVABLE_CHANGELOG_TEXT, cx[i]);
766       queue_push(&hq, h);
767     }
768   for (i = 0; i < hq.count; i++)
769     repodata_add_flexarray(data, handle, SOLVABLE_CHANGELOG, hq.elements[i]);
770   queue_free(&hq);
771   solv_free(ct);
772   solv_free(cx);
773   solv_free(cn);
774 }
775
776 static void 
777 set_description_author(Repodata *data, Id handle, char *str)
778 {
779   char *aut, *p;
780   for (aut = str; (aut = strchr(aut, '\n')) != 0; aut++)
781     if (!strncmp(aut, "\nAuthors:\n--------\n", 19))
782       break;
783   if (aut)
784     {
785       /* oh my, found SUSE special author section */
786       int l = aut - str;
787       str = solv_strdup(str);
788       aut = str + l;
789       str[l] = 0;
790       while (l > 0 && str[l - 1] == '\n')
791         str[--l] = 0;
792       if (l)
793         setutf8string(data, handle, SOLVABLE_DESCRIPTION, str);
794       p = aut + 19;
795       aut = str;        /* copy over */
796       while (*p == ' ' || *p == '\n')
797         p++;
798       while (*p)
799         {
800           if (*p == '\n')
801             {
802               *aut++ = *p++;
803               while (*p == ' ')
804                 p++;
805               continue;
806             }
807           *aut++ = *p++;
808         }
809       while (aut != str && aut[-1] == '\n')
810         aut--;
811       *aut = 0;
812       if (*str)
813         setutf8string(data, handle, SOLVABLE_AUTHORS, str);
814       free(str);
815     }
816   else if (*str)
817     setutf8string(data, handle, SOLVABLE_DESCRIPTION, str);
818 }
819
820 static int
821 rpm2solv(Pool *pool, Repo *repo, Repodata *data, Solvable *s, RpmHead *rpmhead, int flags)
822 {
823   char *name;
824   char *evr;
825   char *sourcerpm;
826
827   name = headstring(rpmhead, TAG_NAME);
828   if (!name)
829     {
830       pool_error(pool, 0, "package has no name");
831       return 0;
832     }
833   if (!strcmp(name, "gpg-pubkey"))
834     return 0;
835   s->name = pool_str2id(pool, name, 1);
836   sourcerpm = headstring(rpmhead, TAG_SOURCERPM);
837   if (sourcerpm || (rpmhead->forcebinary && !headexists(rpmhead, TAG_SOURCEPACKAGE)))
838     s->arch = pool_str2id(pool, headstring(rpmhead, TAG_ARCH), 1);
839   else
840     {
841       if (headexists(rpmhead, TAG_NOSOURCE) || headexists(rpmhead, TAG_NOPATCH))
842         s->arch = ARCH_NOSRC;
843       else
844         s->arch = ARCH_SRC;
845     }
846   if (!s->arch)
847     s->arch = ARCH_NOARCH;
848   evr = headtoevr(rpmhead);
849   s->evr = pool_str2id(pool, evr, 1);
850   s->vendor = pool_str2id(pool, headstring(rpmhead, TAG_VENDOR), 1);
851
852   s->provides = makedeps(pool, repo, rpmhead, TAG_PROVIDENAME, TAG_PROVIDEVERSION, TAG_PROVIDEFLAGS, 0);
853   if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
854     s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
855   s->requires = makedeps(pool, repo, rpmhead, TAG_REQUIRENAME, TAG_REQUIREVERSION, TAG_REQUIREFLAGS, (flags & RPM_ADD_NO_RPMLIBREQS) ? MAKEDEPS_NO_RPMLIB : 0);
856   s->conflicts = makedeps(pool, repo, rpmhead, TAG_CONFLICTNAME, TAG_CONFLICTVERSION, TAG_CONFLICTFLAGS, 0);
857   s->obsoletes = makedeps(pool, repo, rpmhead, TAG_OBSOLETENAME, TAG_OBSOLETEVERSION, TAG_OBSOLETEFLAGS, 0);
858
859   s->recommends = makedeps(pool, repo, rpmhead, TAG_SUGGESTSNAME, TAG_SUGGESTSVERSION, TAG_SUGGESTSFLAGS, MAKEDEPS_FILTER_STRONG);
860   s->suggests = makedeps(pool, repo, rpmhead, TAG_SUGGESTSNAME, TAG_SUGGESTSVERSION, TAG_SUGGESTSFLAGS, MAKEDEPS_FILTER_WEAK);
861   s->supplements = makedeps(pool, repo, rpmhead, TAG_ENHANCESNAME, TAG_ENHANCESVERSION, TAG_ENHANCESFLAGS, MAKEDEPS_FILTER_STRONG);
862   s->enhances  = makedeps(pool, repo, rpmhead, TAG_ENHANCESNAME, TAG_ENHANCESVERSION, TAG_ENHANCESFLAGS, MAKEDEPS_FILTER_WEAK);
863   s->supplements = repo_fix_supplements(repo, s->provides, s->supplements, 0);
864   s->conflicts = repo_fix_conflicts(repo, s->conflicts);
865
866   if (data)
867     {
868       Id handle;
869       char *str;
870       unsigned int u32;
871       unsigned long long u64;
872
873       handle = s - pool->solvables;
874       str = headstring(rpmhead, TAG_SUMMARY);
875       if (str)
876         setutf8string(data, handle, SOLVABLE_SUMMARY, str);
877       str = headstring(rpmhead, TAG_DESCRIPTION);
878       if (str)
879         set_description_author(data, handle, str);
880       str = headstring(rpmhead, TAG_GROUP);
881       if (str)
882         repodata_set_poolstr(data, handle, SOLVABLE_GROUP, str);
883       str = headstring(rpmhead, TAG_LICENSE);
884       if (str)
885         repodata_set_poolstr(data, handle, SOLVABLE_LICENSE, str);
886       str = headstring(rpmhead, TAG_URL);
887       if (str)
888         repodata_set_str(data, handle, SOLVABLE_URL, str);
889       str = headstring(rpmhead, TAG_DISTRIBUTION);
890       if (str)
891         repodata_set_poolstr(data, handle, SOLVABLE_DISTRIBUTION, str);
892       str = headstring(rpmhead, TAG_PACKAGER);
893       if (str)
894         repodata_set_poolstr(data, handle, SOLVABLE_PACKAGER, str);
895       if ((flags & RPM_ADD_WITH_PKGID) != 0)
896         {
897           unsigned char *chksum;
898           unsigned int chksumsize;
899           chksum = headbinary(rpmhead, TAG_SIGMD5, &chksumsize);
900           if (chksum && chksumsize == 16)
901             repodata_set_bin_checksum(data, handle, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, chksum);
902         }
903       if ((flags & RPM_ADD_WITH_HDRID) != 0)
904         {
905           str = headstring(rpmhead, TAG_SHA1HEADER);
906           if (str && strlen(str) == 40)
907             repodata_set_checksum(data, handle, SOLVABLE_HDRID, REPOKEY_TYPE_SHA1, str);
908           else if (str && strlen(str) == 64)
909             repodata_set_checksum(data, handle, SOLVABLE_HDRID, REPOKEY_TYPE_SHA256, str);
910         }
911       u32 = headint32(rpmhead, TAG_BUILDTIME);
912       if (u32)
913         repodata_set_num(data, handle, SOLVABLE_BUILDTIME, u32);
914       u32 = headint32(rpmhead, TAG_INSTALLTIME);
915       if (u32)
916         repodata_set_num(data, handle, SOLVABLE_INSTALLTIME, u32);
917       u64 = headint64(rpmhead, TAG_LONGSIZE);
918       if (u64)
919         repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, u64);
920       else
921         {
922           u32 = headint32(rpmhead, TAG_SIZE);
923           if (u32)
924             repodata_set_num(data, handle, SOLVABLE_INSTALLSIZE, u32);
925         }
926       if (sourcerpm)
927         repodata_set_sourcepkg(data, handle, sourcerpm);
928       if ((flags & RPM_ADD_TRIGGERS) != 0)
929         {
930           Id id, lastid;
931           unsigned int ida = makedeps(pool, repo, rpmhead, TAG_TRIGGERNAME, TAG_TRIGGERVERSION, TAG_TRIGGERFLAGS, 0);
932
933           lastid = 0;
934           for (; (id = repo->idarraydata[ida]) != 0; ida++)
935             {
936               /* we currently do not support rel ids in incore data, so
937                * strip off versioning information */
938               while (ISRELDEP(id))
939                 {
940                   Reldep *rd = GETRELDEP(pool, id);
941                   id = rd->name;
942                 }
943               if (id == lastid)
944                 continue;
945               repodata_add_idarray(data, handle, SOLVABLE_TRIGGERS, id);
946               lastid = id;
947             }
948         }
949       if ((flags & RPM_ADD_NO_FILELIST) == 0)
950         addfilelist(data, handle, rpmhead);
951       if ((flags & RPM_ADD_WITH_CHANGELOG) != 0)
952         addchangelog(data, handle, rpmhead);
953     }
954   solv_free(evr);
955   return 1;
956 }
957
958
959 /******************************************************************/
960 /*  Rpm Database stuff
961  */
962
963 struct rpmdbstate {
964   Pool *pool; 
965   char *rootdir;
966
967   RpmHead *rpmhead;     /* header storage space */
968   int rpmheadsize;
969
970   int dbopened;
971   DB_ENV *dbenv;        /* database environment */
972   DB *db;               /* packages database */
973   int byteswapped;      /* endianess of packages database */
974 };
975
976 struct rpmdbentry {
977   Id rpmdbid;
978   Id nameoff;
979 };
980
981 #define ENTRIES_BLOCK 255
982 #define NAMEDATA_BLOCK 1023
983
984
985 static inline Id db2rpmdbid(unsigned char *db, int byteswapped)
986 {
987 #ifdef RPM5
988   return db[0] << 24 | db[1] << 16 | db[2] << 8 | db[3];
989 #else
990 # if defined(WORDS_BIGENDIAN)
991   if (!byteswapped)
992 # else
993   if (byteswapped)
994 # endif
995     return db[0] << 24 | db[1] << 16 | db[2] << 8 | db[3];
996   else
997     return db[3] << 24 | db[2] << 16 | db[1] << 8 | db[0];
998 #endif
999 }
1000
1001 static inline void rpmdbid2db(unsigned char *db, Id id, int byteswapped)
1002 {
1003 #ifdef RPM5
1004   db[0] = id >> 24, db[1] = id >> 16, db[2] = id >> 8, db[3] = id;
1005 #else
1006 # if defined(WORDS_BIGENDIAN)
1007   if (!byteswapped)
1008 # else
1009   if (byteswapped)
1010 # endif
1011     db[0] = id >> 24, db[1] = id >> 16, db[2] = id >> 8, db[3] = id;
1012   else
1013     db[3] = id >> 24, db[2] = id >> 16, db[1] = id >> 8, db[0] = id;
1014 #endif
1015 }
1016
1017 /* should look in /usr/lib/rpm/macros instead, but we want speed... */
1018 static int
1019 opendbenv(struct rpmdbstate *state, const char *rootdir)
1020 {
1021   char dbpath[PATH_MAX];
1022   DB_ENV *dbenv = 0;
1023   int r;
1024
1025   if (db_env_create(&dbenv, 0))
1026     return pool_error(state->pool, 0, "db_env_create: %s", strerror(errno));
1027 #if defined(FEDORA) && (DB_VERSION_MAJOR >= 5 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 5))
1028   dbenv->set_thread_count(dbenv, 8);
1029 #endif
1030   snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm", rootdir ? rootdir : "");
1031   if (access(dbpath, W_OK) == -1)
1032     {
1033       r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
1034     }
1035   else
1036     {
1037 #ifdef FEDORA
1038       r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_INIT_CDB|DB_INIT_MPOOL, 0644);
1039 #else
1040       r = dbenv->open(dbenv, dbpath, DB_CREATE|DB_PRIVATE|DB_INIT_MPOOL, 0);
1041 #endif
1042     }
1043   if (r)
1044     {
1045       pool_error(state->pool, 0, "dbenv->open: %s", strerror(errno));
1046       dbenv->close(dbenv, 0);
1047       return 0;
1048     }
1049   state->dbenv = dbenv;
1050   return 1;
1051 }
1052
1053 static int
1054 openpkgdb(struct rpmdbstate *state)
1055 {
1056   if (state->dbopened)
1057     return state->dbopened > 0 ? 1 : 0;
1058   state->dbopened = -1;
1059   if (!state->dbenv && !opendbenv(state, state->rootdir))
1060     return 0;
1061   if (db_create(&state->db, state->dbenv, 0))
1062     {
1063       pool_error(state->pool, 0, "db_create: %s", strerror(errno));
1064       state->db = 0;
1065       state->dbenv->close(state->dbenv, 0);
1066       state->dbenv = 0;
1067       return 0;
1068     }
1069   if (state->db->open(state->db, 0, "Packages", 0, DB_UNKNOWN, DB_RDONLY, 0664))
1070     {
1071       pool_error(state->pool, 0, "db->open Packages: %s", strerror(errno));
1072       state->db->close(state->db, 0);
1073       state->db = 0;
1074       state->dbenv->close(state->dbenv, 0);
1075       state->dbenv = 0;
1076       return 0;
1077     }
1078   if (state->db->get_byteswapped(state->db, &state->byteswapped))
1079     {
1080       pool_error(state->pool, 0, "db->get_byteswapped: %s", strerror(errno));
1081       state->db->close(state->db, 0);
1082       state->db = 0;
1083       state->dbenv->close(state->dbenv, 0);
1084       state->dbenv = 0;
1085       return 0;
1086     }
1087   state->dbopened = 1;
1088   return 1;
1089 }
1090
1091 /* get the rpmdbids of all installed packages from the Name index database.
1092  * This is much faster then querying the big Packages database */
1093 static struct rpmdbentry *
1094 getinstalledrpmdbids(struct rpmdbstate *state, const char *index, const char *match, int *nentriesp, char **namedatap)
1095 {
1096   DB_ENV *dbenv = 0;
1097   DB *db = 0;
1098   DBC *dbc = 0;
1099   int byteswapped;
1100   DBT dbkey;
1101   DBT dbdata;
1102   unsigned char *dp;
1103   int dl;
1104   Id nameoff;
1105
1106   char *namedata = 0;
1107   int namedatal = 0;
1108   struct rpmdbentry *entries = 0;
1109   int nentries = 0;
1110
1111   *nentriesp = 0;
1112   if (namedatap)
1113     *namedatap = 0;
1114
1115   if (!state->dbenv && !opendbenv(state, state->rootdir))
1116     return 0;
1117   dbenv = state->dbenv;
1118   if (db_create(&db, dbenv, 0))
1119     {
1120       pool_error(state->pool, 0, "db_create: %s", strerror(errno));
1121       return 0;
1122     }
1123   if (db->open(db, 0, index, 0, DB_UNKNOWN, DB_RDONLY, 0664))
1124     {
1125       pool_error(state->pool, 0, "db->open %s: %s", index, strerror(errno));
1126       db->close(db, 0);
1127       return 0;
1128     }
1129   if (db->get_byteswapped(db, &byteswapped))
1130     {
1131       pool_error(state->pool, 0, "db->get_byteswapped: %s", strerror(errno));
1132       db->close(db, 0);
1133       return 0;
1134     }
1135   if (db->cursor(db, NULL, &dbc, 0))
1136     {
1137       pool_error(state->pool, 0, "db->cursor: %s", strerror(errno));
1138       db->close(db, 0);
1139       return 0;
1140     }
1141   memset(&dbkey, 0, sizeof(dbkey));
1142   memset(&dbdata, 0, sizeof(dbdata));
1143   if (match)
1144     {
1145       dbkey.data = (void *)match;
1146       dbkey.size = strlen(match);
1147     }
1148   while (dbc->c_get(dbc, &dbkey, &dbdata, match ? DB_SET : DB_NEXT) == 0)
1149     {
1150       if (!match && dbkey.size == 10 && !memcmp(dbkey.data, "gpg-pubkey", 10))
1151         continue;
1152       dl = dbdata.size;
1153       dp = dbdata.data;
1154       nameoff = namedatal;
1155       if (namedatap)
1156         {
1157           namedata = solv_extend(namedata, namedatal, dbkey.size + 1, 1, NAMEDATA_BLOCK);
1158           memcpy(namedata + namedatal, dbkey.data, dbkey.size);
1159           namedata[namedatal + dbkey.size] = 0;
1160           namedatal += dbkey.size + 1;
1161         }
1162       while(dl >= RPM_INDEX_SIZE)
1163         {
1164           entries = solv_extend(entries, nentries, 1, sizeof(*entries), ENTRIES_BLOCK);
1165           entries[nentries].rpmdbid = db2rpmdbid(dp, byteswapped);
1166           entries[nentries].nameoff = nameoff;
1167           nentries++;
1168           dp += RPM_INDEX_SIZE;
1169           dl -= RPM_INDEX_SIZE;
1170         }
1171       if (match)
1172         break;
1173     }
1174   dbc->c_close(dbc);
1175   db->close(db, 0);
1176   /* make sure that enteries is != 0 if there was no error */
1177   if (!entries)
1178     entries = solv_extend(entries, 1, 1, sizeof(*entries), ENTRIES_BLOCK);
1179   *nentriesp = nentries;
1180   if (namedatap)
1181     *namedatap = namedata;
1182   return entries;
1183 }
1184
1185 /* retrive header by rpmdbid */
1186 static int
1187 getrpmdbid(struct rpmdbstate *state, Id rpmdbid)
1188 {
1189   unsigned char buf[16];
1190   DBT dbkey;
1191   DBT dbdata;
1192   RpmHead *rpmhead;
1193
1194   if (!rpmdbid)
1195     {
1196       pool_error(state->pool, 0, "illegal rpmdbid");
1197       return -1;
1198     }
1199   if (state->dbopened != 1 && !openpkgdb(state))
1200     return -1;
1201   rpmdbid2db(buf, rpmdbid, state->byteswapped);
1202   memset(&dbkey, 0, sizeof(dbkey));
1203   memset(&dbdata, 0, sizeof(dbdata));
1204   dbkey.data = buf;
1205   dbkey.size = 4;
1206   dbdata.data = 0;
1207   dbdata.size = 0;
1208   if (state->db->get(state->db, NULL, &dbkey, &dbdata, 0))
1209     return 0;
1210   if (dbdata.size < 8)
1211     {
1212       pool_error(state->pool, 0, "corrupt rpm database (size)");
1213       return -1;
1214     }
1215   if (dbdata.size > state->rpmheadsize)
1216     {
1217       state->rpmheadsize = dbdata.size + 128;
1218       state->rpmhead = solv_realloc(state->rpmhead, sizeof(*rpmhead) + state->rpmheadsize);
1219     }
1220   rpmhead = state->rpmhead;
1221   memcpy(buf, dbdata.data, 8);
1222   rpmhead->forcebinary = 1;
1223   rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
1224   rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
1225   if (8 + rpmhead->cnt * 16 + rpmhead->dcnt > dbdata.size)
1226     {
1227       pool_error(state->pool, 0, "corrupt rpm database (data size)");
1228       return -1;
1229     }
1230   memcpy(rpmhead->data, (unsigned char *)dbdata.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
1231   rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
1232   return 1;
1233 }
1234
1235 /* retrive header by berkeleydb cursor */
1236 static Id
1237 getrpmcursor(struct rpmdbstate *state, DBC *dbc)
1238 {
1239   unsigned char buf[16];
1240   DBT dbkey;
1241   DBT dbdata;
1242   RpmHead *rpmhead;
1243   Id dbid;
1244
1245   memset(&dbkey, 0, sizeof(dbkey));
1246   memset(&dbdata, 0, sizeof(dbdata));
1247   while (dbc->c_get(dbc, &dbkey, &dbdata, DB_NEXT) == 0)
1248     {
1249       if (dbkey.size != 4)
1250         return pool_error(state->pool, -1, "corrupt Packages database (key size)");
1251       dbid = db2rpmdbid(dbkey.data, state->byteswapped);
1252       if (dbid == 0)            /* the join key */
1253         continue;
1254       if (dbdata.size < 8)
1255         return pool_error(state->pool, -1, "corrupt rpm database (size %u)\n", dbdata.size);
1256       if (dbdata.size > state->rpmheadsize)
1257         {
1258           state->rpmheadsize = dbdata.size + 128;
1259           state->rpmhead = solv_realloc(state->rpmhead, sizeof(*state->rpmhead) + state->rpmheadsize);
1260         }
1261       rpmhead = state->rpmhead;
1262       memcpy(buf, dbdata.data, 8);
1263       rpmhead->forcebinary = 1;
1264       rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
1265       rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
1266       if (8 + rpmhead->cnt * 16 + rpmhead->dcnt > dbdata.size)
1267         return pool_error(state->pool, -1, "corrupt rpm database (data size)\n");
1268       memcpy(rpmhead->data, (unsigned char *)dbdata.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
1269       rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
1270       return dbid;
1271     }
1272   return 0;
1273 }
1274
1275 static void
1276 freestate(struct rpmdbstate *state)
1277 {
1278   /* close down */
1279   if (!state)
1280     return;
1281   if (state->rootdir)
1282     solv_free(state->rootdir);
1283   if (state->db)
1284     state->db->close(state->db, 0);
1285   if (state->dbenv)
1286     state->dbenv->close(state->dbenv, 0);
1287   solv_free(state->rpmhead);
1288 }
1289
1290 void *
1291 rpm_state_create(Pool *pool, const char *rootdir)
1292 {
1293   struct rpmdbstate *state;
1294   state = solv_calloc(1, sizeof(*state));
1295   state->pool = pool;
1296   if (rootdir)
1297     state->rootdir = solv_strdup(rootdir);
1298   return state;
1299 }
1300
1301 void *
1302 rpm_state_free(void *state)
1303 {
1304   freestate(state);
1305   return solv_free(state);
1306 }
1307
1308 static int
1309 count_headers(Pool *pool, const char *rootdir, DB_ENV *dbenv)
1310 {
1311   char dbpath[PATH_MAX];
1312   struct stat statbuf;
1313   DB *db = 0;
1314   DBC *dbc = 0;
1315   int count = 0;
1316   DBT dbkey;
1317   DBT dbdata;
1318
1319   snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm/Name", rootdir ? rootdir : "");
1320   if (stat(dbpath, &statbuf))
1321     return 0;
1322   memset(&dbkey, 0, sizeof(dbkey));
1323   memset(&dbdata, 0, sizeof(dbdata));
1324   if (db_create(&db, dbenv, 0))
1325     {
1326       pool_error(pool, 0, "db_create: %s", strerror(errno));
1327       return 0;
1328     }
1329   if (db->open(db, 0, "Name", 0, DB_UNKNOWN, DB_RDONLY, 0664))
1330     {
1331       pool_error(pool, 0, "db->open Name: %s", strerror(errno));
1332       db->close(db, 0);
1333       return 0;
1334     }
1335   if (db->cursor(db, NULL, &dbc, 0))
1336     {
1337       db->close(db, 0);
1338       pool_error(pool, 0, "db->cursor: %s", strerror(errno));
1339       return 0;
1340     }
1341   while (dbc->c_get(dbc, &dbkey, &dbdata, DB_NEXT) == 0)
1342     count += dbdata.size / RPM_INDEX_SIZE;
1343   dbc->c_close(dbc);
1344   db->close(db, 0);
1345   return count;
1346 }
1347
1348 /******************************************************************/
1349
1350 static Offset
1351 copydeps(Pool *pool, Repo *repo, Offset fromoff, Repo *fromrepo)
1352 {
1353   int cc;
1354   Id *ida, *from;
1355   Offset ido;
1356
1357   if (!fromoff)
1358     return 0;
1359   from = fromrepo->idarraydata + fromoff;
1360   for (ida = from, cc = 0; *ida; ida++, cc++)
1361     ;
1362   if (cc == 0)
1363     return 0;
1364   ido = repo_reserve_ids(repo, 0, cc);
1365   ida = repo->idarraydata + ido;
1366   memcpy(ida, from, (cc + 1) * sizeof(Id));
1367   repo->idarraysize += cc + 1;
1368   return ido;
1369 }
1370
1371 #define COPYDIR_DIRCACHE_SIZE 512
1372
1373 static Id copydir_complex(Pool *pool, Repodata *data, Repodata *fromdata, Id did, Id *cache);
1374
1375 static inline Id
1376 copydir(Pool *pool, Repodata *data, Repodata *fromdata, Id did, Id *cache)
1377 {
1378   if (cache && cache[did & 255] == did)
1379     return cache[(did & 255) + 256];
1380   return copydir_complex(pool, data, fromdata, did, cache);
1381 }
1382
1383 static Id
1384 copydir_complex(Pool *pool, Repodata *data, Repodata *fromdata, Id did, Id *cache)
1385 {
1386   Id parent = dirpool_parent(&fromdata->dirpool, did);
1387   Id compid = dirpool_compid(&fromdata->dirpool, did);
1388   if (parent)
1389     parent = copydir(pool, data, fromdata, parent, cache);
1390   if (data->localpool || fromdata->localpool)
1391     compid = repodata_translate_id(data, fromdata, compid, 1);
1392   compid = dirpool_add_dir(&data->dirpool, parent, compid, 1);
1393   if (cache)
1394     {
1395       cache[did & 255] = did;
1396       cache[(did & 255) + 256] = compid;
1397     }
1398   return compid;
1399 }
1400
1401 struct solvable_copy_cbdata {
1402   Repodata *data;
1403   Id handle;
1404   Id subhandle;
1405   Id *dircache;
1406 };
1407
1408 static int
1409 solvable_copy_cb(void *vcbdata, Solvable *r, Repodata *fromdata, Repokey *key, KeyValue *kv)
1410 {
1411   struct solvable_copy_cbdata *cbdata = vcbdata;
1412   Id id, keyname;
1413   Repodata *data = cbdata->data;
1414   Id handle = cbdata->handle;
1415   Pool *pool = data->repo->pool;
1416
1417   keyname = key->name;
1418   switch(key->type)
1419     {
1420     case REPOKEY_TYPE_ID:
1421     case REPOKEY_TYPE_CONSTANTID:
1422     case REPOKEY_TYPE_IDARRAY:  /* used for triggers */
1423       id = kv->id;
1424       if (data->localpool || fromdata->localpool)
1425         id = repodata_translate_id(data, fromdata, id, 1);
1426       if (key->type == REPOKEY_TYPE_ID)
1427         repodata_set_id(data, handle, keyname, id);
1428       else if (key->type == REPOKEY_TYPE_CONSTANTID)
1429         repodata_set_constantid(data, handle, keyname, id);
1430       else
1431         repodata_add_idarray(data, handle, keyname, id);
1432       break;
1433     case REPOKEY_TYPE_STR:
1434       repodata_set_str(data, handle, keyname, kv->str);
1435       break;
1436     case REPOKEY_TYPE_VOID:
1437       repodata_set_void(data, handle, keyname);
1438       break;
1439     case REPOKEY_TYPE_NUM:
1440       repodata_set_num(data, handle, keyname, SOLV_KV_NUM64(kv));
1441       break;
1442     case REPOKEY_TYPE_CONSTANT:
1443       repodata_set_constant(data, handle, keyname, kv->num);
1444       break;
1445     case REPOKEY_TYPE_DIRNUMNUMARRAY:
1446       id = kv->id;
1447       id = copydir(pool, data, fromdata, id, cbdata->dircache);
1448       repodata_add_dirnumnum(data, handle, keyname, id, kv->num, kv->num2);
1449       break;
1450     case REPOKEY_TYPE_DIRSTRARRAY:
1451       id = kv->id;
1452       id = copydir(pool, data, fromdata, id, cbdata->dircache);
1453       repodata_add_dirstr(data, handle, keyname, id, kv->str);
1454       break;
1455     case REPOKEY_TYPE_FLEXARRAY:
1456       if (kv->eof == 2)
1457         {
1458           assert(cbdata->subhandle);
1459           cbdata->handle = cbdata->subhandle;
1460           cbdata->subhandle = 0;
1461           break;
1462         }
1463       if (!kv->entry)
1464         {
1465           assert(!cbdata->subhandle);
1466           cbdata->subhandle = cbdata->handle;
1467         }
1468       cbdata->handle = repodata_new_handle(data);
1469       repodata_add_flexarray(data, cbdata->subhandle, keyname, cbdata->handle);
1470       break;
1471     default:
1472       break;
1473     }
1474   return 0;
1475 }
1476
1477 static void
1478 solvable_copy(Solvable *s, Solvable *r, Repodata *data, Id *dircache)
1479 {
1480   int p, i;
1481   Repo *repo = s->repo;
1482   Pool *pool = repo->pool;
1483   Repo *fromrepo = r->repo;
1484   struct solvable_copy_cbdata cbdata;
1485
1486   /* copy solvable data */
1487   s->name = r->name;
1488   s->evr = r->evr;
1489   s->arch = r->arch;
1490   s->vendor = r->vendor;
1491   s->provides = copydeps(pool, repo, r->provides, fromrepo);
1492   s->requires = copydeps(pool, repo, r->requires, fromrepo);
1493   s->conflicts = copydeps(pool, repo, r->conflicts, fromrepo);
1494   s->obsoletes = copydeps(pool, repo, r->obsoletes, fromrepo);
1495   s->recommends = copydeps(pool, repo, r->recommends, fromrepo);
1496   s->suggests = copydeps(pool, repo, r->suggests, fromrepo);
1497   s->supplements = copydeps(pool, repo, r->supplements, fromrepo);
1498   s->enhances  = copydeps(pool, repo, r->enhances, fromrepo);
1499
1500   /* copy all attributes */
1501   if (!data)
1502     return;
1503   cbdata.data = data;
1504   cbdata.handle = s - pool->solvables;
1505   cbdata.subhandle = 0;
1506   cbdata.dircache = dircache;
1507   p = r - fromrepo->pool->solvables;
1508 #if 0
1509   repo_search(fromrepo, p, 0, 0, SEARCH_NO_STORAGE_SOLVABLE | SEARCH_SUB | SEARCH_ARRAYSENTINEL, solvable_copy_cb, &cbdata);
1510 #else
1511   FOR_REPODATAS(fromrepo, i, data)
1512     {
1513       if (p < data->start || p >= data->end)
1514         continue;
1515       repodata_search(data, p, 0, SEARCH_SUB | SEARCH_ARRAYSENTINEL, solvable_copy_cb, &cbdata);
1516     }
1517 #endif
1518 }
1519
1520 /* used to sort entries by package name that got returned in some database order */
1521 static int
1522 rpmids_sort_cmp(const void *va, const void *vb, void *dp)
1523 {
1524   struct rpmdbentry const *a = va, *b = vb;
1525   char *namedata = dp;
1526   int r;
1527   r = strcmp(namedata + a->nameoff, namedata + b->nameoff);
1528   if (r)
1529     return r;
1530   return a->rpmdbid - b->rpmdbid;
1531 }
1532
1533 static int
1534 pkgids_sort_cmp(const void *va, const void *vb, void *dp)
1535 {
1536   Repo *repo = dp;
1537   Pool *pool = repo->pool;
1538   Solvable *a = pool->solvables + *(Id *)va;
1539   Solvable *b = pool->solvables + *(Id *)vb;
1540   Id *rpmdbid;
1541
1542   if (a->name != b->name)
1543     return strcmp(pool_id2str(pool, a->name), pool_id2str(pool, b->name));
1544   rpmdbid = repo->rpmdbid;
1545   return rpmdbid[(a - pool->solvables) - repo->start] - rpmdbid[(b - pool->solvables) - repo->start];
1546 }
1547
1548 static void
1549 swap_solvables(Repo *repo, Repodata *data, Id pa, Id pb)
1550 {
1551   Pool *pool = repo->pool;
1552   Solvable tmp;
1553
1554   tmp = pool->solvables[pa];
1555   pool->solvables[pa] = pool->solvables[pb];
1556   pool->solvables[pb] = tmp;
1557   if (repo->rpmdbid)
1558     {
1559       Id tmpid = repo->rpmdbid[pa - repo->start];
1560       repo->rpmdbid[pa - repo->start] = repo->rpmdbid[pb - repo->start];
1561       repo->rpmdbid[pb - repo->start] = tmpid;
1562     }
1563   /* only works if nothing is already internalized! */
1564   if (data)
1565     repodata_swap_attrs(data, pa, pb);
1566 }
1567
1568 static void
1569 mkrpmdbcookie(struct stat *st, unsigned char *cookie)
1570 {
1571   memset(cookie, 0, 32);
1572   cookie[3] = RPMDB_COOKIE_VERSION;
1573   memcpy(cookie + 16, &st->st_ino, sizeof(st->st_ino));
1574   memcpy(cookie + 24, &st->st_dev, sizeof(st->st_dev));
1575 }
1576
1577 /*
1578  * read rpm db as repo
1579  *
1580  */
1581
1582 int
1583 repo_add_rpmdb(Repo *repo, Repo *ref, int flags)
1584 {
1585   Pool *pool = repo->pool;
1586   char dbpath[PATH_MAX];
1587   struct stat packagesstat;
1588   unsigned char newcookie[32];
1589   const unsigned char *oldcookie = 0;
1590   Id oldcookietype = 0;
1591   Repodata *data;
1592   int count = 0, done = 0;
1593   const char *rootdir = 0;
1594   struct rpmdbstate state;
1595   int i;
1596   Solvable *s;
1597   unsigned int now;
1598
1599   now = solv_timems(0);
1600   memset(&state, 0, sizeof(state));
1601   state.pool = pool;
1602
1603   data = repo_add_repodata(repo, flags);
1604
1605   if (ref && !(ref->nsolvables && ref->rpmdbid && ref->pool == repo->pool))
1606     {
1607       if ((flags & RPMDB_EMPTY_REFREPO) != 0)
1608         repo_empty(ref, 1);
1609       ref = 0;
1610     }
1611
1612   if (flags & REPO_USE_ROOTDIR)
1613     rootdir = pool_get_rootdir(pool);
1614   if (!opendbenv(&state, rootdir))
1615     return -1;
1616
1617   /* XXX: should get ro lock of Packages database! */
1618   snprintf(dbpath, PATH_MAX, "%s/var/lib/rpm/Packages", rootdir ? rootdir : "");
1619   if (stat(dbpath, &packagesstat))
1620     {
1621       pool_error(pool, -1, "%s: %s", dbpath, strerror(errno));
1622       freestate(&state);
1623       return -1;
1624     }
1625   mkrpmdbcookie(&packagesstat, newcookie);
1626   repodata_set_bin_checksum(data, SOLVID_META, REPOSITORY_RPMDBCOOKIE, REPOKEY_TYPE_SHA256, newcookie);
1627
1628   if (ref)
1629     oldcookie = repo_lookup_bin_checksum(ref, SOLVID_META, REPOSITORY_RPMDBCOOKIE, &oldcookietype);
1630   if (!ref || !oldcookie || oldcookietype != REPOKEY_TYPE_SHA256 || memcmp(oldcookie, newcookie, 32) != 0)
1631     {
1632       int solvstart = 0, solvend = 0;
1633       Id dbid;
1634       DBC *dbc = 0;
1635
1636       if (ref && (flags & RPMDB_EMPTY_REFREPO) != 0)
1637         repo_empty(ref, 1);     /* get it out of the way */
1638       if ((flags & RPMDB_REPORT_PROGRESS) != 0)
1639         count = count_headers(pool, rootdir, state.dbenv);
1640       if (!openpkgdb(&state))
1641         {
1642           freestate(&state);
1643           return -1;
1644         }
1645       if (state.db->cursor(state.db, NULL, &dbc, 0))
1646         {
1647           freestate(&state);
1648           return pool_error(pool, -1, "db->cursor failed");
1649         }
1650       i = 0;
1651       s = 0;
1652       while ((dbid = getrpmcursor(&state, dbc)) != 0)
1653         {
1654           if (dbid == -1)
1655             {
1656               dbc->c_close(dbc);
1657               freestate(&state);
1658               return -1;
1659             }
1660           if (!s)
1661             {
1662               s = pool_id2solvable(pool, repo_add_solvable(repo));
1663               if (!solvstart)
1664                 solvstart = s - pool->solvables;
1665               solvend = s - pool->solvables + 1;
1666             }
1667           if (!repo->rpmdbid)
1668             repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
1669           repo->rpmdbid[(s - pool->solvables) - repo->start] = dbid;
1670           if (rpm2solv(pool, repo, data, s, state.rpmhead, flags | RPM_ADD_TRIGGERS))
1671             {
1672               i++;
1673               s = 0;
1674             }
1675           else
1676             {
1677               /* We can reuse this solvable, but make sure it's still
1678                  associated with this repo.  */
1679               memset(s, 0, sizeof(*s));
1680               s->repo = repo;
1681             }
1682           if ((flags & RPMDB_REPORT_PROGRESS) != 0)
1683             {
1684               if (done < count)
1685                 done++;
1686               if (done < count && (done - 1) * 100 / count != done * 100 / count)
1687                 pool_debug(pool, SOLV_ERROR, "%%%% %d\n", done * 100 / count);
1688             }
1689         }
1690       dbc->c_close(dbc);
1691       if (s)
1692         {
1693           /* oops, could not reuse. free it instead */
1694           repo_free_solvable(repo, s - pool->solvables, 1);
1695           solvend--;
1696           s = 0;
1697         }
1698       /* now sort all solvables in the new solvstart..solvend block */
1699       if (solvend - solvstart > 1)
1700         {
1701           Id *pkgids = solv_malloc2(solvend - solvstart, sizeof(Id));
1702           for (i = solvstart; i < solvend; i++)
1703             pkgids[i - solvstart] = i;
1704           solv_sort(pkgids, solvend - solvstart, sizeof(Id), pkgids_sort_cmp, repo);
1705           /* adapt order */
1706           for (i = solvstart; i < solvend; i++)
1707             {
1708               int j = pkgids[i - solvstart];
1709               while (j < i)
1710                 j = pkgids[i - solvstart] = pkgids[j - solvstart];
1711               if (j != i)
1712                 swap_solvables(repo, data, i, j);
1713             }
1714           solv_free(pkgids);
1715         }
1716     }
1717   else
1718     {
1719       Id dircache[COPYDIR_DIRCACHE_SIZE];               /* see copydir */
1720       struct rpmdbentry *entries = 0, *rp;
1721       int nentries = 0;
1722       char *namedata = 0;
1723       unsigned int refmask, h;
1724       Id id, *refhash;
1725       int res;
1726
1727       memset(dircache, 0, sizeof(dircache));
1728
1729       /* get ids of installed rpms */
1730       entries = getinstalledrpmdbids(&state, "Name", 0, &nentries, &namedata);
1731       if (!entries)
1732         {
1733           freestate(&state);
1734           return -1;
1735         }
1736
1737       /* sort by name */
1738       if (nentries > 1)
1739         solv_sort(entries, nentries, sizeof(*entries), rpmids_sort_cmp, namedata);
1740
1741       /* create hash from dbid to ref */
1742       refmask = mkmask(ref->nsolvables);
1743       refhash = solv_calloc(refmask + 1, sizeof(Id));
1744       for (i = 0; i < ref->end - ref->start; i++)
1745         {
1746           if (!ref->rpmdbid[i])
1747             continue;
1748           h = ref->rpmdbid[i] & refmask;
1749           while (refhash[h])
1750             h = (h + 317) & refmask;
1751           refhash[h] = i + 1;   /* make it non-zero */
1752         }
1753
1754       /* count the misses, they will cost us time */
1755       if ((flags & RPMDB_REPORT_PROGRESS) != 0)
1756         {
1757           for (i = 0, rp = entries; i < nentries; i++, rp++)
1758             {
1759               if (refhash)
1760                 {
1761                   Id dbid = rp->rpmdbid;
1762                   h = dbid & refmask;
1763                   while ((id = refhash[h]))
1764                     {
1765                       if (ref->rpmdbid[id - 1] == dbid)
1766                         break;
1767                       h = (h + 317) & refmask;
1768                     }
1769                   if (id)
1770                     continue;
1771                 }
1772               count++;
1773             }
1774         }
1775
1776       if (ref && (flags & RPMDB_EMPTY_REFREPO) != 0)
1777         s = pool_id2solvable(pool, repo_add_solvable_block_before(repo, nentries, ref));
1778       else
1779         s = pool_id2solvable(pool, repo_add_solvable_block(repo, nentries));
1780       if (!repo->rpmdbid)
1781         repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
1782
1783       for (i = 0, rp = entries; i < nentries; i++, rp++, s++)
1784         {
1785           Id dbid = rp->rpmdbid;
1786           repo->rpmdbid[(s - pool->solvables) - repo->start] = rp->rpmdbid;
1787           if (refhash)
1788             {
1789               h = dbid & refmask;
1790               while ((id = refhash[h]))
1791                 {
1792                   if (ref->rpmdbid[id - 1] == dbid)
1793                     break;
1794                   h = (h + 317) & refmask;
1795                 }
1796               if (id)
1797                 {
1798                   Solvable *r = ref->pool->solvables + ref->start + (id - 1);
1799                   if (r->repo == ref)
1800                     {
1801                       solvable_copy(s, r, data, dircache);
1802                       continue;
1803                     }
1804                 }
1805             }
1806           res = getrpmdbid(&state, dbid);
1807           if (res <= 0)
1808             {
1809               if (!res)
1810                 return pool_error(pool, -1, "inconsistent rpm database, key %d not found. run 'rpm --rebuilddb' to fix.", dbid);
1811               freestate(&state);
1812               solv_free(entries);
1813               solv_free(namedata);
1814               solv_free(refhash);
1815               return -1;
1816             }
1817           rpm2solv(pool, repo, data, s, state.rpmhead, flags | RPM_ADD_TRIGGERS);
1818           if ((flags & RPMDB_REPORT_PROGRESS) != 0)
1819             {
1820               if (done < count)
1821                 done++;
1822               if (done < count && (done - 1) * 100 / count != done * 100 / count)
1823                 pool_debug(pool, SOLV_ERROR, "%%%% %d\n", done * 100 / count);
1824             }
1825         }
1826
1827       solv_free(entries);
1828       solv_free(namedata);
1829       solv_free(refhash);
1830       if (ref && (flags & RPMDB_EMPTY_REFREPO) != 0)
1831         repo_empty(ref, 1);
1832     }
1833
1834   freestate(&state);
1835   if (!(flags & REPO_NO_INTERNALIZE))
1836     repodata_internalize(data);
1837   if ((flags & RPMDB_REPORT_PROGRESS) != 0)
1838     pool_debug(pool, SOLV_ERROR, "%%%% 100\n");
1839   POOL_DEBUG(SOLV_DEBUG_STATS, "repo_add_rpmdb took %d ms\n", solv_timems(now));
1840   POOL_DEBUG(SOLV_DEBUG_STATS, "repo size: %d solvables\n", repo->nsolvables);
1841   POOL_DEBUG(SOLV_DEBUG_STATS, "repo memory used: %d K incore, %d K idarray\n", repodata_memused(data)/1024, repo->idarraysize / (int)(1024/sizeof(Id)));
1842   return 0;
1843 }
1844
1845 int
1846 repo_add_rpmdb_reffp(Repo *repo, FILE *fp, int flags)
1847 {
1848   int res;
1849   Repo *ref = 0;
1850
1851   if (!fp)
1852     return repo_add_rpmdb(repo, 0, flags);
1853   ref = repo_create(repo->pool, "add_rpmdb_reffp");
1854   if (repo_add_solv(ref, fp, 0) != 0)
1855     {
1856       repo_free(ref, 1);
1857       ref = 0;
1858     }
1859   if (ref && ref->start == ref->end)
1860     {
1861       repo_free(ref, 1);
1862       ref = 0;
1863     }
1864   if (ref)
1865     repo_disable_paging(ref);
1866   res = repo_add_rpmdb(repo, ref, flags | RPMDB_EMPTY_REFREPO);
1867   if (ref)
1868     repo_free(ref, 1);
1869   return res;
1870 }
1871
1872 static inline unsigned int
1873 getu32(const unsigned char *dp)
1874 {
1875   return dp[0] << 24 | dp[1] << 16 | dp[2] << 8 | dp[3];
1876 }
1877
1878
1879 Id
1880 repo_add_rpm(Repo *repo, const char *rpm, int flags)
1881 {
1882   unsigned int sigdsize, sigcnt, l;
1883   Pool *pool = repo->pool;
1884   Solvable *s;
1885   RpmHead *rpmhead = 0;
1886   int rpmheadsize = 0;
1887   char *payloadformat;
1888   FILE *fp;
1889   unsigned char lead[4096];
1890   int headerstart, headerend;
1891   struct stat stb;
1892   Repodata *data;
1893   unsigned char pkgid[16];
1894   unsigned char leadsigid[16];
1895   unsigned char hdrid[32];
1896   int pkgidtype, leadsigidtype, hdridtype;
1897   Id chksumtype = 0;
1898   void *chksumh = 0;
1899   void *leadsigchksumh = 0;
1900   int forcebinary = 0;
1901
1902   data = repo_add_repodata(repo, flags);
1903
1904   if ((flags & RPM_ADD_WITH_SHA256SUM) != 0)
1905     chksumtype = REPOKEY_TYPE_SHA256;
1906   else if ((flags & RPM_ADD_WITH_SHA1SUM) != 0)
1907     chksumtype = REPOKEY_TYPE_SHA1;
1908
1909   if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, rpm) : rpm, "r")) == 0)
1910     {
1911       pool_error(pool, -1, "%s: %s", rpm, strerror(errno));
1912       return 0;
1913     }
1914   if (fstat(fileno(fp), &stb))
1915     {
1916       pool_error(pool, -1, "fstat: %s", strerror(errno));
1917       fclose(fp);
1918       return 0;
1919     }
1920   if (chksumtype)
1921     chksumh = solv_chksum_create(chksumtype);
1922   if ((flags & RPM_ADD_WITH_LEADSIGID) != 0)
1923     leadsigchksumh = solv_chksum_create(REPOKEY_TYPE_MD5);
1924   if (fread(lead, 96 + 16, 1, fp) != 1 || getu32(lead) != 0xedabeedb)
1925     {
1926       pool_error(pool, -1, "%s: not a rpm", rpm);
1927       fclose(fp);
1928       return 0;
1929     }
1930   forcebinary = lead[6] != 0 || lead[7] != 1;
1931   if (chksumh)
1932     solv_chksum_add(chksumh, lead, 96 + 16);
1933   if (leadsigchksumh)
1934     solv_chksum_add(leadsigchksumh, lead, 96 + 16);
1935   if (lead[78] != 0 || lead[79] != 5)
1936     {
1937       pool_error(pool, -1, "%s: not a rpm v5 header", rpm);
1938       fclose(fp);
1939       return 0;
1940     }
1941   if (getu32(lead + 96) != 0x8eade801)
1942     {
1943       pool_error(pool, -1, "%s: bad signature header", rpm);
1944       fclose(fp);
1945       return 0;
1946     }
1947   sigcnt = getu32(lead + 96 + 8);
1948   sigdsize = getu32(lead + 96 + 12);
1949   if (sigcnt >= 0x100000 || sigdsize >= 0x100000)
1950     {
1951       pool_error(pool, -1, "%s: bad signature header", rpm);
1952       fclose(fp);
1953       return 0;
1954     }
1955   sigdsize += sigcnt * 16;
1956   sigdsize = (sigdsize + 7) & ~7;
1957   headerstart = 96 + 16 + sigdsize;
1958   pkgidtype = leadsigidtype = hdridtype = 0;
1959   if ((flags & (RPM_ADD_WITH_PKGID | RPM_ADD_WITH_HDRID)) != 0)
1960     {
1961       /* extract pkgid or hdrid from the signature header */
1962       if (sigdsize > rpmheadsize)
1963         {
1964           rpmheadsize = sigdsize + 128;
1965           rpmhead = solv_realloc(rpmhead, sizeof(*rpmhead) + rpmheadsize);
1966         }
1967       if (fread(rpmhead->data, sigdsize, 1, fp) != 1)
1968         {
1969           pool_error(pool, -1, "%s: unexpected EOF", rpm);
1970           fclose(fp);
1971           return 0;
1972         }
1973       if (chksumh)
1974         solv_chksum_add(chksumh, rpmhead->data, sigdsize);
1975       if (leadsigchksumh)
1976         solv_chksum_add(leadsigchksumh, rpmhead->data, sigdsize);
1977       rpmhead->forcebinary = 0;
1978       rpmhead->cnt = sigcnt;
1979       rpmhead->dcnt = sigdsize - sigcnt * 16;
1980       rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
1981       if ((flags & RPM_ADD_WITH_PKGID) != 0)
1982         {
1983           unsigned char *chksum;
1984           unsigned int chksumsize;
1985           chksum = headbinary(rpmhead, SIGTAG_MD5, &chksumsize);
1986           if (chksum && chksumsize == 16)
1987             {
1988               pkgidtype = REPOKEY_TYPE_MD5;
1989               memcpy(pkgid, chksum, 16);
1990             }
1991         }
1992       if ((flags & RPM_ADD_WITH_HDRID) != 0)
1993         {
1994           const char *str = headstring(rpmhead, TAG_SHA1HEADER);
1995           if (str && strlen(str) == 40)
1996             {
1997               if (solv_hex2bin(&str, hdrid, 20) == 20)
1998                 hdridtype = REPOKEY_TYPE_SHA1;
1999             }
2000           else if (str && strlen(str) == 64)
2001             {
2002               if (solv_hex2bin(&str, hdrid, 32) == 32)
2003                 hdridtype = REPOKEY_TYPE_SHA256;
2004             }
2005         }
2006     }
2007   else
2008     {
2009       /* just skip the signature header */
2010       while (sigdsize)
2011         {
2012           l = sigdsize > 4096 ? 4096 : sigdsize;
2013           if (fread(lead, l, 1, fp) != 1)
2014             {
2015               pool_error(pool, -1, "%s: unexpected EOF", rpm);
2016               fclose(fp);
2017               return 0;
2018             }
2019           if (chksumh)
2020             solv_chksum_add(chksumh, lead, l);
2021           if (leadsigchksumh)
2022             solv_chksum_add(leadsigchksumh, lead, l);
2023           sigdsize -= l;
2024         }
2025     }
2026   if (leadsigchksumh)
2027     {
2028       leadsigchksumh = solv_chksum_free(leadsigchksumh, leadsigid);
2029       leadsigidtype = REPOKEY_TYPE_MD5;
2030     }
2031   if (fread(lead, 16, 1, fp) != 1)
2032     {
2033       pool_error(pool, -1, "%s: unexpected EOF", rpm);
2034       fclose(fp);
2035       return 0;
2036     }
2037   if (chksumh)
2038     solv_chksum_add(chksumh, lead, 16);
2039   if (getu32(lead) != 0x8eade801)
2040     {
2041       pool_error(pool, -1, "%s: bad header", rpm);
2042       fclose(fp);
2043       return 0;
2044     }
2045   sigcnt = getu32(lead + 8);
2046   sigdsize = getu32(lead + 12);
2047   if (sigcnt >= 0x100000 || sigdsize >= 0x2000000)
2048     {
2049       pool_error(pool, -1, "%s: bad header", rpm);
2050       fclose(fp);
2051       return 0;
2052     }
2053   l = sigdsize + sigcnt * 16;
2054   headerend = headerstart + 16 + l;
2055   if (l > rpmheadsize)
2056     {
2057       rpmheadsize = l + 128;
2058       rpmhead = solv_realloc(rpmhead, sizeof(*rpmhead) + rpmheadsize);
2059     }
2060   if (fread(rpmhead->data, l, 1, fp) != 1)
2061     {
2062       pool_error(pool, -1, "%s: unexpected EOF", rpm);
2063       fclose(fp);
2064       return 0;
2065     }
2066   if (chksumh)
2067     solv_chksum_add(chksumh, rpmhead->data, l);
2068   rpmhead->forcebinary = forcebinary;
2069   rpmhead->cnt = sigcnt;
2070   rpmhead->dcnt = sigdsize;
2071   rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
2072   if (headexists(rpmhead, TAG_PATCHESNAME))
2073     {
2074       /* this is a patch rpm, ignore */
2075       pool_error(pool, -1, "%s: is patch rpm", rpm);
2076       fclose(fp);
2077       solv_chksum_free(chksumh, 0);
2078       solv_free(rpmhead);
2079       return 0;
2080     }
2081   payloadformat = headstring(rpmhead, TAG_PAYLOADFORMAT);
2082   if (payloadformat && !strcmp(payloadformat, "drpm"))
2083     {
2084       /* this is a delta rpm */
2085       pool_error(pool, -1, "%s: is delta rpm", rpm);
2086       fclose(fp);
2087       solv_chksum_free(chksumh, 0);
2088       solv_free(rpmhead);
2089       return 0;
2090     }
2091   if (chksumh)
2092     while ((l = fread(lead, 1, sizeof(lead), fp)) > 0)
2093       solv_chksum_add(chksumh, lead, l);
2094   fclose(fp);
2095   s = pool_id2solvable(pool, repo_add_solvable(repo));
2096   if (!rpm2solv(pool, repo, data, s, rpmhead, flags & ~(RPM_ADD_WITH_HDRID | RPM_ADD_WITH_PKGID)))
2097     {
2098       repo_free_solvable(repo, s - pool->solvables, 1);
2099       solv_chksum_free(chksumh, 0);
2100       solv_free(rpmhead);
2101       return 0;
2102     }
2103   if (!(flags & REPO_NO_LOCATION))
2104     repodata_set_location(data, s - pool->solvables, 0, 0, rpm);
2105   if (S_ISREG(stb.st_mode))
2106     repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned long long)stb.st_size);
2107   repodata_set_num(data, s - pool->solvables, SOLVABLE_HEADEREND, headerend);
2108   if (pkgidtype)
2109     repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, pkgidtype, pkgid);
2110   if (hdridtype)
2111     repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_HDRID, hdridtype, hdrid);
2112   if (leadsigidtype)
2113     repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_LEADSIGID, leadsigidtype, leadsigid);
2114   if (chksumh)
2115     {
2116       repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, chksumtype, solv_chksum_get(chksumh, 0));
2117       chksumh = solv_chksum_free(chksumh, 0);
2118     }
2119   if (rpmhead)
2120     solv_free(rpmhead);
2121   if (!(flags & REPO_NO_INTERNALIZE))
2122     repodata_internalize(data);
2123   return s - pool->solvables;
2124 }
2125
2126 Id
2127 repo_add_rpm_handle(Repo *repo, void *rpmhandle, int flags)
2128 {
2129   Pool *pool = repo->pool;
2130   Repodata *data;
2131   RpmHead *rpmhead = rpmhandle;
2132   Solvable *s;
2133   char *payloadformat;
2134
2135   data = repo_add_repodata(repo, flags);
2136   if (headexists(rpmhead, TAG_PATCHESNAME))
2137     {
2138       pool_error(pool, -1, "is a patch rpm");
2139       return 0;
2140     }
2141   payloadformat = headstring(rpmhead, TAG_PAYLOADFORMAT);
2142   if (payloadformat && !strcmp(payloadformat, "drpm"))
2143     {
2144       /* this is a delta rpm */
2145       pool_error(pool, -1, "is a delta rpm");
2146       return 0;
2147     }
2148   s = pool_id2solvable(pool, repo_add_solvable(repo));
2149   if (!rpm2solv(pool, repo, data, s, rpmhead, flags))
2150     {
2151       repo_free_solvable(repo, s - pool->solvables, 1);
2152       return 0;
2153     }
2154   if (!(flags & REPO_NO_INTERNALIZE))
2155     repodata_internalize(data);
2156   return s - pool->solvables;
2157 }
2158
2159 static inline void
2160 linkhash(const char *lt, char *hash)
2161 {
2162   unsigned int r = 0;
2163   const unsigned char *str = (const unsigned char *)lt;
2164   int l, c;
2165
2166   l = strlen(lt);
2167   while ((c = *str++) != 0)
2168     r += (r << 3) + c;
2169   sprintf(hash, "%08x%08x%08x%08x", r, l, 0, 0);
2170 }
2171
2172 void
2173 rpm_iterate_filelist(void *rpmhandle, int flags, void (*cb)(void *, const char *, struct filelistinfo *), void *cbdata)
2174 {
2175   RpmHead *rpmhead = rpmhandle;
2176   char **bn;
2177   char **dn;
2178   char **md = 0;
2179   char **lt = 0;
2180   unsigned int *di, diidx;
2181   unsigned int *co = 0;
2182   unsigned int *ff = 0;
2183   unsigned int lastdir;
2184   int lastdirl;
2185   unsigned int *fm;
2186   int cnt, dcnt, cnt2;
2187   int i, l1, l;
2188   char *space = 0;
2189   int spacen = 0;
2190   char md5[33];
2191   struct filelistinfo info;
2192
2193   dn = headstringarray(rpmhead, TAG_DIRNAMES, &dcnt);
2194   if (!dn)
2195     return;
2196   if ((flags & RPM_ITERATE_FILELIST_ONLYDIRS) != 0)
2197     {
2198       for (i = 0; i < dcnt; i++)
2199         (*cb)(cbdata, dn[i], 0);
2200       solv_free(dn);
2201       return;
2202     }
2203   bn = headstringarray(rpmhead, TAG_BASENAMES, &cnt);
2204   if (!bn)
2205     {
2206       solv_free(dn);
2207       return;
2208     }
2209   di = headint32array(rpmhead, TAG_DIRINDEXES, &cnt2);
2210   if (!di || cnt != cnt2)
2211     {
2212       solv_free(di);
2213       solv_free(bn);
2214       solv_free(dn);
2215       return;
2216     }
2217   fm = headint16array(rpmhead, TAG_FILEMODES, &cnt2);
2218   if (!fm || cnt != cnt2)
2219     {
2220       solv_free(fm);
2221       solv_free(di);
2222       solv_free(bn);
2223       solv_free(dn);
2224       return;
2225     }
2226   if ((flags & RPM_ITERATE_FILELIST_WITHMD5) != 0)
2227     {
2228       md = headstringarray(rpmhead, TAG_FILEMD5S, &cnt2);
2229       if (!md || cnt != cnt2)
2230         {
2231           solv_free(md);
2232           solv_free(fm);
2233           solv_free(di);
2234           solv_free(bn);
2235           solv_free(dn);
2236           return;
2237         }
2238     }
2239   if ((flags & RPM_ITERATE_FILELIST_WITHCOL) != 0)
2240     {
2241       co = headint32array(rpmhead, TAG_FILECOLORS, &cnt2);
2242       if (!co || cnt != cnt2)
2243         {
2244           solv_free(co);
2245           solv_free(md);
2246           solv_free(fm);
2247           solv_free(di);
2248           solv_free(bn);
2249           solv_free(dn);
2250           return;
2251         }
2252     }
2253   if ((flags & RPM_ITERATE_FILELIST_NOGHOSTS) != 0)
2254     {
2255       ff = headint32array(rpmhead, TAG_FILEFLAGS, &cnt2);
2256       if (!ff || cnt != cnt2)
2257         {
2258           solv_free(ff);
2259           solv_free(co);
2260           solv_free(md);
2261           solv_free(fm);
2262           solv_free(di);
2263           solv_free(bn);
2264           solv_free(dn);
2265           return;
2266         }
2267     }
2268   lastdir = dcnt;
2269   lastdirl = 0;
2270   memset(&info, 0, sizeof(info));
2271   for (i = 0; i < cnt; i++)
2272     {
2273       if (ff && (ff[i] & FILEFLAG_GHOST) != 0)
2274         continue;
2275       diidx = di[i];
2276       if (diidx >= dcnt)
2277         continue;
2278       l1 = lastdir == diidx ? lastdirl : strlen(dn[diidx]);
2279       l = l1 + strlen(bn[i]) + 1;
2280       if (l > spacen)
2281         {
2282           spacen = l + 16;
2283           space = solv_realloc(space, spacen);
2284         }
2285       if (lastdir != diidx)
2286         {
2287           strcpy(space, dn[diidx]);
2288           lastdir = diidx;
2289           lastdirl = l1;
2290         }
2291       strcpy(space + l1, bn[i]);
2292       info.diridx = diidx;
2293       info.dirlen = l1;
2294       if (fm)
2295         info.mode = fm[i];
2296       if (md)
2297         {
2298           info.digest = md[i];
2299           if (fm && S_ISLNK(fm[i]))
2300             {
2301               info.digest = 0;
2302               if (!lt)
2303                 {
2304                   lt = headstringarray(rpmhead, TAG_FILELINKTOS, &cnt2);
2305                   if (cnt != cnt2)
2306                     lt = solv_free(lt);
2307                 }
2308               if (lt)
2309                 {
2310                   linkhash(lt[i], md5);
2311                   info.digest = md5;
2312                 }
2313             }
2314           if (!info.digest)
2315             {
2316               sprintf(md5, "%08x%08x%08x%08x", (fm[i] >> 12) & 65535, 0, 0, 0);
2317               info.digest = md5;
2318             }
2319         }
2320       if (co)
2321         info.color = co[i];
2322       (*cb)(cbdata, space, &info);
2323     }
2324   solv_free(space);
2325   solv_free(lt);
2326   solv_free(md);
2327   solv_free(fm);
2328   solv_free(di);
2329   solv_free(bn);
2330   solv_free(dn);
2331   solv_free(co);
2332   solv_free(ff);
2333 }
2334
2335 char *
2336 rpm_query(void *rpmhandle, Id what)
2337 {
2338   const char *name, *arch, *sourcerpm;
2339   char *evr, *r;
2340   int l;
2341
2342   RpmHead *rpmhead = rpmhandle;
2343   r = 0;
2344   switch (what)
2345     {
2346     case 0:
2347       name = headstring(rpmhead, TAG_NAME);
2348       if (!name)
2349         name = "";
2350       sourcerpm = headstring(rpmhead, TAG_SOURCERPM);
2351       if (sourcerpm || (rpmhead->forcebinary && !headexists(rpmhead, TAG_SOURCEPACKAGE)))
2352         arch = headstring(rpmhead, TAG_ARCH);
2353       else
2354         {
2355           if (headexists(rpmhead, TAG_NOSOURCE) || headexists(rpmhead, TAG_NOPATCH))
2356             arch = "nosrc";
2357           else
2358             arch = "src";
2359         }
2360       if (!arch)
2361         arch = "noarch";
2362       evr = headtoevr(rpmhead);
2363       l = strlen(name) + 1 + strlen(evr ? evr : "") + 1 + strlen(arch) + 1;
2364       r = solv_malloc(l);
2365       sprintf(r, "%s-%s.%s", name, evr ? evr : "", arch);
2366       solv_free(evr);
2367       break;
2368     case SOLVABLE_NAME:
2369       name = headstring(rpmhead, TAG_NAME);
2370       r = solv_strdup(name);
2371       break;
2372     case SOLVABLE_SUMMARY:
2373       name = headstring(rpmhead, TAG_SUMMARY);
2374       r = solv_strdup(name);
2375       break;
2376     case SOLVABLE_DESCRIPTION:
2377       name = headstring(rpmhead, TAG_DESCRIPTION);
2378       r = solv_strdup(name);
2379       break;
2380     case SOLVABLE_EVR:
2381       r = headtoevr(rpmhead);
2382       break;
2383     }
2384   return r;
2385 }
2386
2387 unsigned long long
2388 rpm_query_num(void *rpmhandle, Id what, unsigned long long notfound)
2389 {
2390   RpmHead *rpmhead = rpmhandle;
2391   unsigned int u32;
2392
2393   switch (what)
2394     {
2395     case SOLVABLE_INSTALLTIME:
2396       u32 = headint32(rpmhead, TAG_INSTALLTIME);
2397       return u32 ? u32 : notfound;
2398     }
2399   return notfound;
2400 }
2401
2402 int
2403 rpm_installedrpmdbids(void *rpmstate, const char *index, const char *match, Queue *rpmdbidq)
2404 {
2405   struct rpmdbentry *entries;
2406   int nentries, i;
2407
2408   entries = getinstalledrpmdbids(rpmstate, index ? index : "Name", match, &nentries, 0);
2409   if (rpmdbidq)
2410     {
2411       queue_empty(rpmdbidq);
2412       for (i = 0; i < nentries; i++)
2413         queue_push(rpmdbidq, entries[i].rpmdbid);
2414     }
2415   solv_free(entries);
2416   return nentries;
2417 }
2418
2419 void *
2420 rpm_byrpmdbid(void *rpmstate, Id rpmdbid)
2421 {
2422   struct rpmdbstate *state = rpmstate;
2423   int r;
2424
2425   r = getrpmdbid(state, rpmdbid);
2426   if (!r)
2427     pool_error(state->pool, 0, "header #%d not in database", rpmdbid);
2428   return r <= 0 ? 0 : state->rpmhead;
2429 }
2430
2431 void *
2432 rpm_byfp(void *rpmstate, FILE *fp, const char *name)
2433 {
2434   struct rpmdbstate *state = rpmstate;
2435   /* int headerstart, headerend; */
2436   RpmHead *rpmhead;
2437   unsigned int sigdsize, sigcnt, l;
2438   unsigned char lead[4096];
2439   int forcebinary = 0;
2440
2441   if (fread(lead, 96 + 16, 1, fp) != 1 || getu32(lead) != 0xedabeedb)
2442     {
2443       pool_error(state->pool, 0, "%s: not a rpm", name);
2444       return 0;
2445     }
2446   forcebinary = lead[6] != 0 || lead[7] != 1;
2447   if (lead[78] != 0 || lead[79] != 5)
2448     {
2449       pool_error(state->pool, 0, "%s: not a V5 header", name);
2450       return 0;
2451     }
2452   if (getu32(lead + 96) != 0x8eade801)
2453     {
2454       pool_error(state->pool, 0, "%s: bad signature header", name);
2455       return 0;
2456     }
2457   sigcnt = getu32(lead + 96 + 8);
2458   sigdsize = getu32(lead + 96 + 12);
2459   if (sigcnt >= 0x100000 || sigdsize >= 0x100000)
2460     {
2461       pool_error(state->pool, 0, "%s: bad signature header", name);
2462       return 0;
2463     }
2464   sigdsize += sigcnt * 16;
2465   sigdsize = (sigdsize + 7) & ~7;
2466   /* headerstart = 96 + 16 + sigdsize; */
2467   while (sigdsize)
2468     {
2469       l = sigdsize > 4096 ? 4096 : sigdsize;
2470       if (fread(lead, l, 1, fp) != 1)
2471         {
2472           pool_error(state->pool, 0, "%s: unexpected EOF", name);
2473           return 0;
2474         }
2475       sigdsize -= l;
2476     }
2477   if (fread(lead, 16, 1, fp) != 1)
2478     {
2479       pool_error(state->pool, 0, "%s: unexpected EOF", name);
2480       return 0;
2481     }
2482   if (getu32(lead) != 0x8eade801)
2483     {
2484       pool_error(state->pool, 0, "%s: bad header", name);
2485       return 0;
2486     }
2487   sigcnt = getu32(lead + 8);
2488   sigdsize = getu32(lead + 12);
2489   if (sigcnt >= 0x100000 || sigdsize >= 0x2000000)
2490     {
2491       pool_error(state->pool, 0, "%s: bad header", name);
2492       return 0;
2493     }
2494   l = sigdsize + sigcnt * 16;
2495   /* headerend = headerstart + 16 + l; */
2496   if (l > state->rpmheadsize)
2497     {
2498       state->rpmheadsize = l + 128;
2499       state->rpmhead = solv_realloc(state->rpmhead, sizeof(*state->rpmhead) + state->rpmheadsize);
2500     }
2501   rpmhead = state->rpmhead;
2502   if (fread(rpmhead->data, l, 1, fp) != 1)
2503     {
2504       pool_error(state->pool, 0, "%s: unexpected EOF", name);
2505       return 0;
2506     }
2507   rpmhead->forcebinary = forcebinary;
2508   rpmhead->cnt = sigcnt;
2509   rpmhead->dcnt = sigdsize;
2510   rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
2511   return rpmhead;
2512 }
2513
2514 #ifdef ENABLE_RPMDB_BYRPMHEADER
2515
2516 void *
2517 rpm_byrpmh(void *rpmstate, Header h)
2518 {
2519   struct rpmdbstate *state = rpmstate;
2520   const unsigned char *uh;
2521   unsigned int sigdsize, sigcnt, l;
2522   RpmHead *rpmhead;
2523
2524 #ifndef RPM5
2525   uh = headerUnload(h);
2526 #else
2527   uh = headerUnload(h, NULL);
2528 #endif
2529   if (!uh)
2530     return 0;
2531   sigcnt = getu32(uh);
2532   sigdsize = getu32(uh + 4);
2533   l = sigdsize + sigcnt * 16;
2534   if (l > state->rpmheadsize)
2535     {
2536       state->rpmheadsize = l + 128;
2537       state->rpmhead = solv_realloc(state->rpmhead, sizeof(*state->rpmhead) + state->rpmheadsize);
2538     }
2539   rpmhead = state->rpmhead;
2540   memcpy(rpmhead->data, uh + 8, l - 8);
2541   free((void *)uh);
2542   rpmhead->forcebinary = 0;
2543   rpmhead->cnt = sigcnt;
2544   rpmhead->dcnt = sigdsize;
2545   rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
2546   return rpmhead;
2547 }
2548
2549 #endif
2550