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