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