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