Fix rpmdb2solv, reused solvables must retain their association with the
[platform/upstream/libsolv.git] / tools / repo_rpmdb.c
1 /*
2  * Copyright (c) 2007, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * repo_rpmdb
10  * 
11  * convert rpm db to repo
12  * 
13  */
14
15 #include <sys/types.h>
16 #include <limits.h>
17 #include <fcntl.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #include <db43/db.h>
23
24 #include "pool.h"
25 #include "repo.h"
26 #include "hash.h"
27 #include "util.h"
28 #include "repo_rpmdb.h"
29
30 #define TAG_NAME                1000
31 #define TAG_VERSION             1001
32 #define TAG_RELEASE             1002
33 #define TAG_EPOCH               1003
34 #define TAG_SUMMARY             1004
35 #define TAG_DESCRIPTION         1005
36 #define TAG_BUILDTIME           1006
37 #define TAG_VENDOR              1011
38 #define TAG_ARCH                1022
39 #define TAG_PROVIDENAME         1047
40 #define TAG_REQUIREFLAGS        1048
41 #define TAG_REQUIRENAME         1049
42 #define TAG_REQUIREVERSION      1050
43 #define TAG_CONFLICTFLAGS       1053
44 #define TAG_CONFLICTNAME        1054
45 #define TAG_CONFLICTVERSION     1055
46 #define TAG_OBSOLETENAME        1090
47 #define TAG_PROVIDEFLAGS        1112
48 #define TAG_PROVIDEVERSION      1113
49 #define TAG_OBSOLETEFLAGS       1114
50 #define TAG_OBSOLETEVERSION     1115
51 #define TAG_DIRINDEXES          1116
52 #define TAG_BASENAMES           1117
53 #define TAG_DIRNAMES            1118
54 #define TAG_SUGGESTSNAME        1156
55 #define TAG_SUGGESTSVERSION     1157
56 #define TAG_SUGGESTSFLAGS       1158
57 #define TAG_ENHANCESNAME        1159
58 #define TAG_ENHANCESVERSION     1160
59 #define TAG_ENHANCESFLAGS       1161
60
61 #define DEP_LESS                (1 << 1)
62 #define DEP_GREATER             (1 << 2)
63 #define DEP_EQUAL               (1 << 3)
64 #define DEP_STRONG              (1 << 27)
65 #define DEP_PRE                 ((1 << 6) | (1 << 9) | (1 << 10) | (1 << 11) | (1 << 12))
66
67
68 static DBT key;
69 static DBT data;
70
71 struct rpmid {
72   unsigned int dbid;
73   char *name;
74 };
75
76 typedef struct rpmhead {
77   int cnt;
78   int dcnt;
79   unsigned char *dp;
80   unsigned char data[1];
81 } RpmHead;
82
83 static unsigned int *
84 headint32(RpmHead *h, int tag, int *cnt)
85 {
86   unsigned int i, o, *r;
87   unsigned char *d, taga[4];
88
89   d = h->dp - 16;
90   taga[0] = tag >> 24;
91   taga[1] = tag >> 16;
92   taga[2] = tag >> 8;
93   taga[3] = tag;
94   for (i = 0; i < h->cnt; i++, d -= 16)
95     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
96       break;
97   if (i >= h->cnt)
98     return 0;
99   if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 4)
100     return 0;
101   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
102   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
103   if (o + 4 * i > h->dcnt)
104     return 0;
105   d = h->dp + o;
106   r = xcalloc(i ? i : 1, sizeof(unsigned int));
107   if (cnt)
108     *cnt = i;
109   for (o = 0; o < i; o++, d += 4)
110     r[o] = d[0] << 24 | d[1] << 16 | d[2] << 8 | d[3];
111   return r;
112 }
113
114 static char *
115 headstring(RpmHead *h, int tag)
116 {
117   unsigned int i, o;
118   unsigned char *d, taga[4];
119   d = h->dp - 16;
120   taga[0] = tag >> 24;
121   taga[1] = tag >> 16;
122   taga[2] = tag >> 8;
123   taga[3] = tag;
124   for (i = 0; i < h->cnt; i++, d -= 16)
125     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
126       break;
127   if (i >= h->cnt)
128     return 0;
129   if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 6)
130     return 0;
131   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
132   return (char *)h->dp + o;
133 }
134
135 static char **
136 headstringarray(RpmHead *h, int tag, int *cnt)
137 {
138   unsigned int i, o;
139   unsigned char *d, taga[4];
140   char **r;
141
142   d = h->dp - 16;
143   taga[0] = tag >> 24;
144   taga[1] = tag >> 16;
145   taga[2] = tag >> 8;
146   taga[3] = tag;
147   for (i = 0; i < h->cnt; i++, d -= 16)
148     if (d[3] == taga[3] && d[2] == taga[2] && d[1] == taga[1] && d[0] == taga[0])
149       break;
150   if (i >= h->cnt)
151     return 0;
152   if (d[4] != 0 || d[5] != 0 || d[6] != 0 || d[7] != 8)
153     return 0;
154   o = d[8] << 24 | d[9] << 16 | d[10] << 8 | d[11];
155   i = d[12] << 24 | d[13] << 16 | d[14] << 8 | d[15];
156   r = xcalloc(i ? i : 1, sizeof(char *));
157   if (cnt)
158     *cnt = i;
159   d = h->dp + o;
160   for (o = 0; o < i; o++)
161     {
162       r[o] = (char *)d;
163       if (o + 1 < i)
164         d += strlen((char *)d) + 1;
165       if (d >= h->dp + h->dcnt)
166         {
167           free(r);
168           return 0;
169         }
170     }
171   return r;
172 }
173
174 static char *headtoevr(RpmHead *h)
175 {
176   unsigned int epoch, *epochp; 
177   char *version, *v;
178   char *release;
179   char *evr;
180   int epochcnt = 0;
181
182   version  = headstring(h, TAG_VERSION);
183   release  = headstring(h, TAG_RELEASE);
184   epochp = headint32(h, TAG_EPOCH, &epochcnt);
185   if (!version || !release)
186     {
187       fprintf(stderr, "headtoevr: bad rpm header\n");
188       exit(1);
189     }
190   for (v = version; *v >= 0 && *v <= '9'; v++)
191     ;
192   epoch = epochp && epochcnt ? *epochp : 0;
193   if (epoch || (v != version && *v == ':'))
194     {
195       char epochbuf[11];        /* 32bit decimal will fit in */
196       sprintf(epochbuf, "%u", epoch);
197       evr = xmalloc(strlen(epochbuf) + 1 + strlen(version) + 1 + strlen(release) + 1);
198       sprintf(evr, "%s:%s-%s", epochbuf, version, release);
199     }
200   else
201     {
202       evr = xmalloc(strlen(version) + 1 + strlen(release) + 1);
203       sprintf(evr, "%s-%s", version, release);
204     }
205   if (epochp)
206     free(epochp);
207   return evr;
208 }
209
210 static unsigned int
211 makedeps(Pool *pool, Repo *repo, RpmHead *rpmhead, int tagn, int tagv, int tagf, int strong)
212 {
213   char **n, **v;
214   unsigned int *f;
215   int i, cc, nc, vc, fc;
216   int haspre = 0;
217   unsigned int olddeps;
218   Id *ida;
219
220   n = headstringarray(rpmhead, tagn, &nc);
221   if (!n)
222     return 0;
223   v = headstringarray(rpmhead, tagv, &vc);
224   if (!v)
225     {
226       free(n);
227       return 0;
228     }
229   f = headint32(rpmhead, tagf, &fc);
230   if (!f)
231     {
232       free(n);
233       free(v);
234       return 0;
235     }
236   if (nc != vc || nc != fc)
237     {
238       fprintf(stderr, "bad dependency entries\n");
239       exit(1);
240     }
241
242   cc = nc;
243   if (strong)
244     {
245       cc = 0;
246       for (i = 0; i < nc; i++)
247         if ((f[i] & DEP_STRONG) == (strong == 1 ? 0 : DEP_STRONG))
248           {
249             cc++;
250             if ((f[i] & DEP_PRE) != 0)
251               haspre = 1;
252           }
253     }
254   else
255     {
256       for (i = 0; i < nc; i++)
257         if ((f[i] & DEP_PRE) != 0)
258           {
259             haspre = 1;
260             break;
261           }
262     }
263   if (tagn != TAG_REQUIRENAME)
264      haspre = 0;
265   if (cc == 0)
266     {
267       free(n);
268       free(v);
269       free(f);
270       return 0;
271     }
272   cc += haspre;
273   olddeps = repo_reserve_ids(repo, 0, cc);
274   ida = repo->idarraydata + olddeps;
275   for (i = 0; ; i++)
276     {
277       if (i == nc)
278         {
279           if (haspre != 1)
280             break;
281           haspre = 2;
282           i = 0;
283           *ida++ = SOLVABLE_PREREQMARKER;
284         }
285       if (strong && (f[i] & DEP_STRONG) != (strong == 1 ? 0 : DEP_STRONG))
286         continue;
287       if (haspre == 1 && (f[i] & DEP_PRE) != 0)
288         continue;
289       if (haspre == 2 && (f[i] & DEP_PRE) == 0)
290         continue;
291       if (f[i] & (DEP_LESS|DEP_GREATER|DEP_EQUAL))
292         {
293           Id name, evr;
294           int flags = 0;
295           if ((f[i] & DEP_LESS) != 0)
296             flags |= 4;
297           if ((f[i] & DEP_EQUAL) != 0)
298             flags |= 2;
299           if ((f[i] & DEP_GREATER) != 0)
300             flags |= 1;
301           name = str2id(pool, n[i], 1);
302           if (v[i][0] == '0' && v[i][1] == ':' && v[i][2])
303             evr = str2id(pool, v[i] + 2, 1);
304           else
305             evr = str2id(pool, v[i], 1);
306           *ida++ = rel2id(pool, name, evr, flags, 1);
307         }
308       else
309         *ida++ = str2id(pool, n[i], 1);
310     }
311   *ida++ = 0;
312   repo->idarraysize += cc + 1;
313   free(n);
314   free(v);
315   free(f);
316   return olddeps;
317 }
318
319 static Offset
320 copydeps(Pool *pool, Repo *repo, Offset fromoff, Repo *fromrepo)
321 {
322   int cc;
323   Id id, *ida, *from;
324   Offset ido;
325   Pool *frompool = fromrepo->pool;
326
327   if (!fromoff)
328     return 0;
329   from = fromrepo->idarraydata + fromoff;
330   for (ida = from, cc = 0; *ida; ida++, cc++)
331     ;
332   if (cc == 0)
333     return 0;
334   ido = repo_reserve_ids(repo, 0, cc);
335   ida = repo->idarraydata + ido;
336   if (frompool && pool != frompool)
337     {
338       while (*from)
339         {
340           id = *from++;
341           if (ISRELDEP(id))
342             {
343               Reldep *rd = GETRELDEP(frompool, id);
344               Id name = str2id(pool, id2str(frompool, rd->name), 1);
345               Id evr = str2id(pool, id2str(frompool, rd->evr), 1);
346               id = rel2id(pool, name, evr, rd->flags, 1);
347             }
348           else
349             id = str2id(pool, id2str(frompool, id), 1);
350           *ida++ = id;
351         }
352       *ida = 0;
353     }
354   else
355     memcpy(ida, from, (cc + 1) * sizeof(Id));
356   repo->idarraysize += cc + 1;
357   return ido;
358 }
359
360
361 #define FILEFILTER_EXACT    0
362 #define FILEFILTER_STARTS   1
363 #define FILEFILTER_CONTAINS 2
364
365 struct filefilter {
366   int dirmatch;
367   char *dir;
368   char *base;
369 };
370
371 static struct filefilter filefilters[] = {
372   { FILEFILTER_CONTAINS, "/bin/", 0},
373   { FILEFILTER_CONTAINS, "/sbin/", 0},
374   { FILEFILTER_CONTAINS, "/lib/", 0},
375   { FILEFILTER_CONTAINS, "/lib64/", 0},
376   { FILEFILTER_CONTAINS, "/etc/", 0},
377   { FILEFILTER_STARTS, "/usr/games/", 0},
378   { FILEFILTER_EXACT, "/usr/share/dict/", "words"},
379   { FILEFILTER_STARTS, "/usr/share/", "magic.mime"},
380   { FILEFILTER_STARTS, "/opt/gnome/games/", 0},
381 };
382
383 /* assumes last processed array is provides! */
384 static unsigned int
385 addfileprovides(Pool *pool, Repo *repo, RpmHead *rpmhead, unsigned int olddeps)
386 {
387   char **bn;
388   char **dn;
389   unsigned int *di;
390   int bnc, dnc, dic;
391   int i, j;
392   struct filefilter *ff;
393   char *fn = 0;
394   int fna = 0;
395
396   bn = headstringarray(rpmhead, TAG_BASENAMES, &bnc);
397   if (!bn)
398     return olddeps;
399   dn = headstringarray(rpmhead, TAG_DIRNAMES, &dnc);
400   if (!dn)
401     {
402       free(bn);
403       return olddeps;
404     }
405   di = headint32(rpmhead, TAG_DIRINDEXES, &dic);
406   if (!di)
407     {
408       free(bn);
409       free(dn);
410       return olddeps;
411     }
412   if (bnc != dic)
413     {
414       fprintf(stderr, "bad filelist\n");
415       exit(1);
416     }
417   for (i = 0; i < bnc; i++)
418     {
419       ff = filefilters;
420       for (j = 0; j < sizeof(filefilters)/sizeof(*filefilters); j++, ff++)
421         {
422           if (ff->dir)
423             {
424               switch (ff->dirmatch)
425                 {
426                 case FILEFILTER_STARTS:
427                   if (strncmp(dn[di[i]], ff->dir, strlen(ff->dir)))
428                     continue;
429                   break;
430                 case FILEFILTER_CONTAINS:
431                   if (!strstr(dn[di[i]], ff->dir))
432                     continue;
433                   break;
434                 case FILEFILTER_EXACT:
435                 default:
436                   if (strcmp(dn[di[i]], ff->dir))
437                     continue;
438                   break;
439                 }
440             }
441           if (ff->base)
442             {
443               if (strcmp(bn[i], ff->base))
444                 continue;
445             }
446           break;
447         }
448       if (j == sizeof(filefilters)/sizeof(*filefilters))
449         continue;
450       j = strlen(bn[i]) + strlen(dn[di[i]]) + 1;
451       if (j > fna)
452         {
453           fna = j + 256;
454           fn = xrealloc(fn, fna);
455         }
456       strcpy(fn, dn[di[i]]);
457       strcat(fn, bn[i]);
458       olddeps = repo_addid(repo, olddeps, str2id(pool, fn, 1));
459     }
460   if (fn)
461     free(fn);
462   free(bn);
463   free(dn);
464   free(di);
465   return olddeps;
466 }
467
468 static int
469 rpm2solv(Pool *pool, Repo *repo, Solvable *s, RpmHead *rpmhead)
470 {
471   char *name;
472   char *evr;
473
474   name = headstring(rpmhead, TAG_NAME);
475   if (!strcmp(name, "gpg-pubkey"))
476     return 0;
477   s->name = str2id(pool, name, 1);
478   if (!s->name)
479     {
480       fprintf(stderr, "package has no name\n");
481       exit(1);
482     }
483   s->arch = str2id(pool, headstring(rpmhead, TAG_ARCH), 1);
484   if (!s->arch)
485     s->arch = ARCH_NOARCH;
486   evr = headtoevr(rpmhead);
487   s->evr = str2id(pool, evr, 1);
488   free(evr);
489   s->vendor = str2id(pool, headstring(rpmhead, TAG_VENDOR), 1);
490
491   s->provides = makedeps(pool, repo, rpmhead, TAG_PROVIDENAME, TAG_PROVIDEVERSION, TAG_PROVIDEFLAGS, 0);
492   s->provides = addfileprovides(pool, repo, rpmhead, s->provides);
493   s->provides = repo_addid_dep(repo, s->provides, rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
494   s->requires = makedeps(pool, repo, rpmhead, TAG_REQUIRENAME, TAG_REQUIREVERSION, TAG_REQUIREFLAGS, 0);
495   s->conflicts = makedeps(pool, repo, rpmhead, TAG_CONFLICTNAME, TAG_CONFLICTVERSION, TAG_CONFLICTFLAGS, 0);
496   s->obsoletes = makedeps(pool, repo, rpmhead, TAG_OBSOLETENAME, TAG_OBSOLETEVERSION, TAG_OBSOLETEFLAGS, 0);
497
498   s->recommends = makedeps(pool, repo, rpmhead, TAG_SUGGESTSNAME, TAG_SUGGESTSVERSION, TAG_SUGGESTSFLAGS, 2);
499   s->suggests = makedeps(pool, repo, rpmhead, TAG_SUGGESTSNAME, TAG_SUGGESTSVERSION, TAG_SUGGESTSFLAGS, 1);
500   s->supplements = makedeps(pool, repo, rpmhead, TAG_ENHANCESNAME, TAG_ENHANCESVERSION, TAG_ENHANCESFLAGS, 2);
501   s->enhances  = makedeps(pool, repo, rpmhead, TAG_ENHANCESNAME, TAG_ENHANCESVERSION, TAG_ENHANCESFLAGS, 1);
502   s->freshens = 0;
503   s->supplements = repo_fix_legacy(repo, s->provides, s->supplements);
504   return 1;
505 }
506
507
508 /*
509  * read rpm db as repo
510  * 
511  */
512
513 void
514 repo_add_rpmdb(Repo *repo, Repo *ref)
515 {
516   Pool *pool = repo->pool;
517   unsigned char buf[16];
518   DB *db = 0;
519   DBC *dbc = 0;
520   int byteswapped;
521   unsigned int dbid;
522   unsigned char *dp, *dbidp;
523   int dl, nrpmids;
524   struct rpmid *rpmids, *rp;
525   int i;
526   int rpmheadsize;
527   RpmHead *rpmhead;
528   Solvable *s;
529   Id id, *refhash;
530   unsigned int refmask, h;
531   int asolv;
532
533   if (repo->start != repo->end)
534     abort();            /* FIXME: rpmdbid */
535
536   if (ref && !(ref->nsolvables && ref->rpmdbid))
537     ref = 0;
538
539   if (db_create(&db, 0, 0))
540     {
541       perror("db_create");
542       exit(1);
543     }
544
545   if (!ref)
546     {
547       if (db->open(db, 0, "/var/lib/rpm/Packages", 0, DB_HASH, DB_RDONLY, 0664))
548         {
549           perror("db->open /var/lib/rpm/Packages");
550           exit(1);
551         }
552       if (db->get_byteswapped(db, &byteswapped))
553         {
554           perror("db->get_byteswapped");
555           exit(1);
556         }
557       if (db->cursor(db, NULL, &dbc, 0))
558         {
559           perror("db->cursor");
560           exit(1);
561         }
562       dbidp = (unsigned char *)&dbid;
563       repo->rpmdbid = xcalloc(256, sizeof(unsigned int));
564       asolv = 256;
565       rpmheadsize = 0;
566       rpmhead = 0;
567       i = 0;
568       s = 0;
569       while (dbc->c_get(dbc, &key, &data, DB_NEXT) == 0)
570         {
571           if (!s)
572             s = pool_id2solvable(pool, repo_add_solvable(repo));
573           if (i >= asolv)
574             {
575               repo->rpmdbid = xrealloc(repo->rpmdbid, (asolv + 256) * sizeof(unsigned int));
576               memset(repo->rpmdbid + asolv, 0, 256 * sizeof(unsigned int));
577               asolv += 256;
578             }
579           if (key.size != 4)
580             {
581               fprintf(stderr, "corrupt Packages database (key size)\n");
582               exit(1);
583             }
584           dp = key.data;
585           if (byteswapped)
586             {
587               dbidp[0] = dp[3];
588               dbidp[1] = dp[2];
589               dbidp[2] = dp[1];
590               dbidp[3] = dp[0];
591             }
592           else
593             memcpy(dbidp, dp, 4);
594           if (dbid == 0)                /* the join key */
595             continue;
596           if (data.size < 8)
597             {
598               fprintf(stderr, "corrupt rpm database (size %u)\n", data.size);
599               exit(1);
600             }
601           if (data.size > rpmheadsize)
602             rpmhead = xrealloc(rpmhead, sizeof(*rpmhead) + data.size);
603           memcpy(buf, data.data, 8);
604           rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
605           rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
606           if (8 + rpmhead->cnt * 16 + rpmhead->dcnt > data.size)
607             {
608               fprintf(stderr, "corrupt rpm database (data size)\n");
609               exit(1);
610             }
611           memcpy(rpmhead->data, (unsigned char *)data.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
612           rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
613           repo->rpmdbid[i] = dbid;
614           if (rpm2solv(pool, repo, s, rpmhead))
615             {
616               i++;
617               s = 0;
618             }
619           else
620             {
621               /* We can reuse this solvable, but make sure it's still
622                  associated with this repo.  */
623               memset(s, 0, sizeof(*s));
624               s->repo = repo;
625             }
626         }
627       if (s)
628         {
629           /* oops, could not reuse. free it instead */
630           repo_free_solvable_block(repo, s - pool->solvables, 1, 1);
631           s = 0;
632         }
633       dbc->c_close(dbc);
634       db->close(db, 0);
635       db = 0;
636     }
637   else
638     {
639       if (db->open(db, 0, "/var/lib/rpm/Name", 0, DB_HASH, DB_RDONLY, 0664))
640         {
641           perror("db->open /var/lib/rpm/Name");
642           exit(1);
643         }
644       if (db->get_byteswapped(db, &byteswapped))
645         {
646           perror("db->get_byteswapped");
647           exit(1);
648         }
649       if (db->cursor(db, NULL, &dbc, 0))
650         {
651           perror("db->cursor");
652           exit(1);
653         }
654       dbidp = (unsigned char *)&dbid;
655       nrpmids = 0;
656       rpmids = 0;
657       while (dbc->c_get(dbc, &key, &data, DB_NEXT) == 0)
658         {
659           if (key.size == 10 && !memcmp(key.data, "gpg-pubkey", 10))
660             continue;
661           dl = data.size;
662           dp = data.data;
663           while(dl >= 8)
664             {
665               if (byteswapped)
666                 {
667                   dbidp[0] = dp[3];
668                   dbidp[1] = dp[2];
669                   dbidp[2] = dp[1];
670                   dbidp[3] = dp[0];
671                 }
672               else
673                 memcpy(dbidp, dp, 4);
674               if ((nrpmids & 255) == 0)
675                 rpmids = xrealloc(rpmids, sizeof(*rpmids) * (nrpmids + 256));
676               rpmids[nrpmids].dbid = dbid;
677               rpmids[nrpmids].name = malloc((int)key.size + 1);
678               memcpy(rpmids[nrpmids].name, key.data, (int)key.size);
679               rpmids[nrpmids].name[(int)key.size] = 0;
680               nrpmids++;
681               dp += 8;
682               dl -= 8;
683             }
684         }
685       dbc->c_close(dbc);
686       db->close(db, 0);
687       db = 0;
688
689       rp = rpmids;
690       dbidp = (unsigned char *)&dbid;
691       rpmheadsize = 0;
692       rpmhead = 0;
693
694       refhash = 0;
695       refmask = 0;
696       if (ref)
697         {
698           refmask = mkmask(ref->nsolvables);
699           refhash = xcalloc(refmask + 1, sizeof(Id));
700           for (i = 0; i < ref->nsolvables; i++)
701             {
702               h = ref->rpmdbid[i] & refmask;
703               while (refhash[h])
704                 h = (h + 317) & refmask;
705               refhash[h] = i + 1;       /* make it non-zero */
706             }
707         }
708
709       repo->rpmdbid = xcalloc(nrpmids, sizeof(unsigned int));
710
711       s = pool_id2solvable(pool, repo_add_solvable_block(repo, nrpmids));
712
713       for (i = 0; i < nrpmids; i++, rp++, s++)
714         {
715           dbid = rp->dbid;
716           repo->rpmdbid[i] = dbid;
717           if (refhash)
718             {
719               h = dbid & refmask;
720               while ((id = refhash[h]))
721                 {
722                   if (ref->rpmdbid[id - 1] == dbid)
723                     break;
724                   h = (h + 317) & refmask;
725                 }
726               if (id)
727                 {
728                   Solvable *r = ref->pool->solvables + ref->start + (id - 1);
729                   if (pool == ref->pool)
730                     {
731                       s->name = r->name;
732                       s->evr = r->evr;
733                       s->arch = r->arch;
734                       s->vendor = r->vendor;
735                     }
736                   else
737                     {
738                       if (r->name)
739                         s->name = str2id(pool, id2str(ref->pool, r->name), 1);
740                       if (r->evr)
741                         s->evr = str2id(pool, id2str(ref->pool, r->evr), 1);
742                       if (r->arch)
743                         s->arch = str2id(pool, id2str(ref->pool, r->arch), 1);
744                       if (r->vendor)
745                         s->vendor = str2id(pool, id2str(ref->pool, r->vendor), 1);
746                     }
747                   s->provides = copydeps(pool, repo, r->provides, ref);
748                   s->requires = copydeps(pool, repo, r->requires, ref);
749                   s->conflicts = copydeps(pool, repo, r->conflicts, ref);
750                   s->obsoletes = copydeps(pool, repo, r->obsoletes, ref);
751                   s->recommends = copydeps(pool, repo, r->recommends, ref);
752                   s->suggests = copydeps(pool, repo, r->suggests, ref);
753                   s->supplements = copydeps(pool, repo, r->supplements, ref);
754                   s->enhances  = copydeps(pool, repo, r->enhances, ref);
755                   s->freshens = copydeps(pool, repo, r->freshens, ref);
756                   continue;
757                 }
758             }
759           if (!db)
760             {
761               if (db_create(&db, 0, 0))
762                 {
763                   perror("db_create");
764                   exit(1);
765                 }
766               if (db->open(db, 0, "/var/lib/rpm/Packages", 0, DB_HASH, DB_RDONLY, 0664))
767                 {
768                   perror("db->open /var/lib/rpm/Packages");
769                   exit(1);
770                 }
771               if (db->get_byteswapped(db, &byteswapped))
772                 {
773                   perror("db->get_byteswapped");
774                   exit(1);
775                 }
776             }
777           if (byteswapped)
778             {
779               buf[0] = dbidp[3];
780               buf[1] = dbidp[2];
781               buf[2] = dbidp[1];
782               buf[3] = dbidp[0];
783             }
784           else
785             memcpy(buf, dbidp, 4);
786           key.data = buf;
787           key.size = 4;
788           data.data = 0;
789           data.size = 0;
790           if (db->get(db, NULL, &key, &data, 0))
791             {
792               perror("db->get");
793               fprintf(stderr, "corrupt rpm database\n");
794               exit(1);
795             }
796           if (data.size < 8)
797             {
798               fprintf(stderr, "corrupt rpm database (size)\n");
799               exit(1);
800             }
801           if (data.size > rpmheadsize)
802             rpmhead = xrealloc(rpmhead, sizeof(*rpmhead) + data.size);
803           memcpy(buf, data.data, 8);
804           rpmhead->cnt = buf[0] << 24  | buf[1] << 16  | buf[2] << 8 | buf[3];
805           rpmhead->dcnt = buf[4] << 24  | buf[5] << 16  | buf[6] << 8 | buf[7];
806           if (8 + rpmhead->cnt * 16 + rpmhead->dcnt > data.size)
807             {
808               fprintf(stderr, "corrupt rpm database (data size)\n");
809               exit(1);
810             }
811           memcpy(rpmhead->data, (unsigned char *)data.data + 8, rpmhead->cnt * 16 + rpmhead->dcnt);
812           rpmhead->dp = rpmhead->data + rpmhead->cnt * 16;
813
814           rpm2solv(pool, repo, s, rpmhead);
815         }
816
817       if (refhash)
818         free(refhash);
819       if (rpmids)
820         {
821           for (i = 0; i < nrpmids; i++)
822             free(rpmids[i].name);
823           free(rpmids);
824         }
825     }
826   if (rpmhead)
827     free(rpmhead);
828   if (db)
829     db->close(db, 0);
830 }