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