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