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