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