- add some parens to fix the segfault
[platform/upstream/libsolv.git] / src / repo_solv.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_solv.c
10  * 
11  * Read the binary dump of a Repo and create a Repo * from it
12  * 
13  *  See
14  *   Repo *pool_addrepo_solv(Pool *pool, FILE *fp)
15  * below
16  * 
17  */
18
19
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <string.h>
25
26 #include "repo_solv.h"
27 #include "util.h"
28
29 #include "repopack.h"
30 #include "repopage.h"
31
32 #define INTERESTED_START        SOLVABLE_NAME
33 #define INTERESTED_END          SOLVABLE_ENHANCES
34
35 #define SOLV_ERROR_NOT_SOLV     1
36 #define SOLV_ERROR_UNSUPPORTED  2
37 #define SOLV_ERROR_EOF          3
38 #define SOLV_ERROR_ID_RANGE     4
39 #define SOLV_ERROR_OVERFLOW     5
40 #define SOLV_ERROR_CORRUPT      6
41
42 static Pool *mypool;            /* for pool_debug... */
43
44
45 static void repodata_load_stub(Repodata *data);
46
47
48 /*******************************************************************************
49  * functions to extract data from a file handle
50  */
51
52 /*
53  * read u32
54  */
55
56 static unsigned int
57 read_u32(Repodata *data)
58 {
59   int c, i;
60   unsigned int x = 0;
61
62   if (data->error)
63     return 0;
64   for (i = 0; i < 4; i++)
65     {
66       c = getc(data->fp);
67       if (c == EOF)
68         {
69           pool_debug(mypool, SAT_ERROR, "unexpected EOF\n");
70           data->error = SOLV_ERROR_EOF;
71           return 0;
72         }
73       x = (x << 8) | c;
74     }
75   return x;
76 }
77
78
79 /*
80  * read u8
81  */
82
83 static unsigned int
84 read_u8(Repodata *data)
85 {
86   int c;
87
88   if (data->error)
89     return 0;
90   c = getc(data->fp);
91   if (c == EOF)
92     {
93       pool_debug(mypool, SAT_ERROR, "unexpected EOF\n");
94       data->error = SOLV_ERROR_EOF;
95       return 0;
96     }
97   return c;
98 }
99
100
101 /*
102  * read Id
103  */
104
105 static Id
106 read_id(Repodata *data, Id max)
107 {
108   unsigned int x = 0;
109   int c, i;
110
111   if (data->error)
112     return 0;
113   for (i = 0; i < 5; i++)
114     {
115       c = getc(data->fp);
116       if (c == EOF)
117         {
118           pool_debug(mypool, SAT_ERROR, "unexpected EOF\n");
119           data->error = SOLV_ERROR_EOF;
120           return 0;
121         }
122       if (!(c & 128))
123         {
124           x = (x << 7) | c;
125           if (max && x >= max)
126             {
127               pool_debug(mypool, SAT_ERROR, "read_id: id too large (%u/%u)\n", x, max);
128               data->error = SOLV_ERROR_ID_RANGE;
129               return 0;
130             }
131           return x;
132         }
133       x = (x << 7) ^ c ^ 128;
134     }
135   pool_debug(mypool, SAT_ERROR, "read_id: id too long\n");
136   data->error = SOLV_ERROR_CORRUPT;
137   return 0;
138 }
139
140
141 static Id *
142 read_idarray(Repodata *data, Id max, Id *map, Id *store, Id *end)
143 {
144   unsigned int x = 0;
145   int c;
146
147   if (data->error)
148     return 0;
149   for (;;)
150     {
151       c = getc(data->fp);
152       if (c == EOF)
153         {
154           pool_debug(mypool, SAT_ERROR, "unexpected EOF\n");
155           data->error = SOLV_ERROR_EOF;
156           return 0;
157         }
158       if ((c & 128) != 0)
159         {
160           x = (x << 7) ^ c ^ 128;
161           continue;
162         }
163       x = (x << 6) | (c & 63);
164       if (max && x >= max)
165         {
166           pool_debug(mypool, SAT_ERROR, "read_idarray: id too large (%u/%u)\n", x, max);
167           data->error = SOLV_ERROR_ID_RANGE;
168           return 0;
169         }
170       if (map)
171         x = map[x];
172       if (store == end)
173         {
174           pool_debug(mypool, SAT_ERROR, "read_idarray: array overflow\n");
175           return 0;
176         }
177       *store++ = x;
178       if ((c & 64) == 0)
179         {
180           if (x == 0)   /* already have trailing zero? */
181             return store;
182           if (store == end)
183             {
184               pool_debug(mypool, SAT_ERROR, "read_idarray: array overflow\n");
185               data->error = SOLV_ERROR_OVERFLOW;
186               return 0;
187             }
188           *store++ = 0;
189           return store;
190         }
191       x = 0;
192     }
193 }
194
195
196 /*******************************************************************************
197  * functions to extract data from memory
198  */
199
200 /*
201  * read array of Ids
202  */
203
204 static inline unsigned char *
205 data_read_id_max(unsigned char *dp, Id *ret, Id *map, int max, int *error)
206 {
207   Id x;
208   dp = data_read_id(dp, &x);
209   if (max && x >= max)
210     {
211       pool_debug(mypool, SAT_ERROR, "data_read_idarray: id too large (%u/%u)\n", x, max);
212       *error = SOLV_ERROR_ID_RANGE;
213       x = 0;
214     }
215   *ret = map ? map[x] : x;
216   return dp;
217 }
218
219 unsigned char *
220 data_read_idarray(unsigned char *dp, Id **storep, Id *map, int max, int *error)
221 {
222   Id *store = *storep;
223   unsigned int x = 0;
224   int c;
225
226   for (;;)
227     {
228       c = *dp++;
229       if ((c & 128) != 0)
230         {
231           x = (x << 7) ^ c ^ 128;
232           continue;
233         }
234       x = (x << 6) | (c & 63);
235       if (max && x >= max)
236         {
237           pool_debug(mypool, SAT_ERROR, "data_read_idarray: id too large (%u/%u)\n", x, max);
238           *error = SOLV_ERROR_ID_RANGE;
239           break;
240         }
241       *store++ = x;
242       if ((c & 64) == 0)
243         break;
244       x = 0;
245     }
246   *store++ = 0;
247   *storep = store;
248   return dp;
249 }
250
251 unsigned char *
252 data_read_rel_idarray(unsigned char *dp, Id **storep, Id *map, int max, int *error, Id marker)
253 {
254   Id *store = *storep;
255   Id old = 0;
256   unsigned int x = 0;
257   int c;
258
259   for (;;)
260     {
261       c = *dp++;
262       if ((c & 128) != 0)
263         {
264           x = (x << 7) ^ c ^ 128;
265           continue;
266         }
267       x = (x << 6) | (c & 63);
268       if (x == 0)
269         {
270           if (!(c & 64))
271             break;
272           if (marker)
273             *store++ = marker;
274           old = 0;
275           continue;
276         }
277       x = old + (x - 1);
278       old = x;
279       if (max && x >= max)
280         {
281           pool_debug(mypool, SAT_ERROR, "data_read_rel_idarray: id too large (%u/%u)\n", x, max);
282           *error = SOLV_ERROR_ID_RANGE;
283           break;
284         }
285       *store++ = map ? map[x] : x;
286       if (!(c & 64))
287         break;
288       x = 0;
289     }
290   *store++ = 0;
291   *storep = store;
292   return dp;
293 }
294
295
296
297
298 /*******************************************************************************
299  * functions to add data to our incore memory space
300  */
301
302
303 static void
304 incore_add_id(Repodata *data, Id x)
305 {
306   unsigned char *dp;
307   /* make sure we have at least 5 bytes free */
308   if (data->incoredatafree < 5)
309     {
310       data->incoredata = sat_realloc(data->incoredata, data->incoredatalen + 1024);
311       data->incoredatafree = 1024;
312     }
313   dp = data->incoredata + data->incoredatalen;
314   if (x < 0)
315     abort();
316   if (x >= (1 << 14))
317     {
318       if (x >= (1 << 28))
319         *dp++ = (x >> 28) | 128;
320       if (x >= (1 << 21))
321         *dp++ = (x >> 21) | 128;
322       *dp++ = (x >> 14) | 128;
323     }
324   if (x >= (1 << 7))
325     *dp++ = (x >> 7) | 128;
326   *dp++ = x & 127;
327   data->incoredatafree -= dp - (data->incoredata + data->incoredatalen);
328   data->incoredatalen = dp - data->incoredata;
329 }
330
331 static void
332 incore_add_blob(Repodata *data, unsigned char *buf, int len)
333 {
334   if (data->incoredatafree < len)
335     {
336       data->incoredata = sat_realloc(data->incoredata, data->incoredatalen + 1024 + len);
337       data->incoredatafree = 1024 + len;
338     }
339   memcpy(data->incoredata + data->incoredatalen, buf, len);
340   data->incoredatafree -= len;
341   data->incoredatalen += len;
342 }
343
344 static void
345 incore_map_idarray(Repodata *data, unsigned char *dp, Id *map, Id max)
346 {
347   /* We have to map the IDs, which might also change
348      the necessary number of bytes, so we can't just copy
349      over the blob and adjust it.  */
350   for (;;)
351     {
352       Id id;
353       int eof;
354       dp = data_read_ideof(dp, &id, &eof);
355       if (max && id >= max)
356         {
357           pool_debug(mypool, SAT_ERROR, "incore_map_idarray: id too large (%u/%u)\n", id, max);
358           data->error = SOLV_ERROR_ID_RANGE;
359           break;
360         }
361       id = map[id];
362       if (id >= 64)
363         id = (id & 63) | ((id & ~63) << 1);
364       incore_add_id(data, eof ? id : id | 64);
365       if (eof)
366         break;
367     }
368 }
369
370 static void
371 incore_add_u32(Repodata *data, unsigned int x)
372 {
373   unsigned char *dp;
374   /* make sure we have at least 4 bytes free */
375   if (data->incoredatafree < 4)
376     {
377       data->incoredata = sat_realloc(data->incoredata, data->incoredatalen + 1024);
378       data->incoredatafree = 1024;
379     }
380   dp = data->incoredata + data->incoredatalen;
381   *dp++ = x >> 24;
382   *dp++ = x >> 16;
383   *dp++ = x >> 8;
384   *dp++ = x;
385   data->incoredatafree -= 4;
386   data->incoredatalen += 4;
387 }
388
389 #if 0
390 static void
391 incore_add_u8(Repodata *data, unsigned int x)
392 {
393   unsigned char *dp;
394   /* make sure we have at least 1 byte free */
395   if (data->incoredatafree < 1)
396     {
397       data->incoredata = sat_realloc(data->incoredata, data->incoredatalen + 1024);
398       data->incoredatafree = 1024;
399     }
400   dp = data->incoredata + data->incoredatalen;
401   *dp++ = x;
402   data->incoredatafree--;
403   data->incoredatalen++;
404 }
405 #endif
406
407
408 /*******************************************************************************
409  * callback to create our stub sub-repodatas from the incore data
410  */
411
412 struct create_stub_data {
413   Repodata *data;
414   Id xkeyname;
415 };
416
417 int
418 create_stub_cb(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
419 {
420   struct create_stub_data *stubdata = cbdata;
421   if (key->name == REPOSITORY_EXTERNAL && key->type == REPOKEY_TYPE_FLEXARRAY)
422     {
423       if (stubdata->data)
424         {
425           repodata_internalize(stubdata->data);
426           if (data->start != data->end)
427             {
428               repodata_extend(stubdata->data, data->start);
429               repodata_extend(stubdata->data, data->end - 1);
430             }
431           stubdata->data = 0;
432         }
433       if (kv->eof)
434         return SEARCH_NEXT_SOLVABLE;
435       stubdata->data = repo_add_repodata(data->repo, 0);
436       stubdata->data->state = REPODATA_STUB;
437       stubdata->data->loadcallback = repodata_load_stub;
438       return 0;
439     }
440   if (!stubdata->data)
441     return SEARCH_NEXT_KEY;
442   switch(key->type)
443     {
444     case REPOKEY_TYPE_ID:
445       repodata_set_id(stubdata->data, REPOENTRY_META, key->name, kv->id);
446       break;
447     case REPOKEY_TYPE_CONSTANTID:
448       repodata_set_constantid(stubdata->data, REPOENTRY_META, key->name, kv->id);
449       break;
450     case REPOKEY_TYPE_STR:
451       repodata_set_str(stubdata->data, REPOENTRY_META, key->name, kv->str);
452       break;
453     case REPOKEY_TYPE_VOID:
454       repodata_set_void(stubdata->data, REPOENTRY_META, key->name);
455       break;
456     case REPOKEY_TYPE_NUM:
457       repodata_set_num(stubdata->data, REPOENTRY_META, key->name, kv->num);
458       break;
459     case REPOKEY_TYPE_IDARRAY:
460       repodata_add_idarray(stubdata->data, REPOENTRY_META, key->name, kv->id);
461       if (key->name == REPOSITORY_KEYS)
462         {
463           if (!stubdata->xkeyname)
464             stubdata->xkeyname = kv->id;
465           else
466             {
467               Repokey xkey;
468
469               xkey.name = stubdata->xkeyname;
470               xkey.type = kv->id;
471               xkey.storage = KEY_STORAGE_INCORE;
472               xkey.size = 0;
473               repodata_key2id(stubdata->data, &xkey, 1);
474               stubdata->xkeyname = 0;
475             }
476           if (kv->eof)
477             stubdata->xkeyname = 0;
478         }
479       break;
480     case REPOKEY_TYPE_MD5:
481     case REPOKEY_TYPE_SHA1:
482     case REPOKEY_TYPE_SHA256:
483       repodata_set_checksum(stubdata->data, REPOENTRY_META, key->name, key->type, kv->str);
484       break;
485     default:
486       return SEARCH_NEXT_KEY;
487     }
488   return 0;
489 }
490
491
492 /*******************************************************************************
493  * our main function
494  */
495
496 /*
497  * read repo from .solv file and add it to pool
498  * if stubdata is set, substitute it with read data
499  * (this is used to replace a repodata stub with the real data)
500  */
501
502 static int
503 repo_add_solv_parent(Repo *repo, FILE *fp, Repodata *parent)
504 {
505   Pool *pool = repo->pool;
506   int i, l;
507   unsigned int numid, numrel, numdir, numsolv;
508   unsigned int numkeys, numschemata;
509
510   Offset sizeid;
511   Offset *str;                         /* map Id -> Offset into string space */
512   char *strsp;                         /* repo string space */
513   char *sp;                            /* pointer into string space */
514   Id *idmap;                           /* map of repo Ids to pool Ids */
515   Id id, type;
516   unsigned int hashmask, h;
517   int hh;
518   Id *hashtbl;
519   Id name, evr, did;
520   int flags;
521   Reldep *ran;
522   unsigned int size_idarray;
523   Id *idarraydatap, *idarraydataend;
524   Offset ido;
525   Solvable *s;
526   unsigned int solvflags;
527   unsigned int solvversion;
528   Repokey *keys;
529   Id *schemadata, *schemadatap, *schemadataend;
530   Id *schemata, key, *keyp;
531   int nentries;
532   int have_xdata;
533   int maxsize, allsize;
534   unsigned char *buf, *dp, *dps;
535   int left;
536   Id stack[10];
537   int keydepth;
538
539   struct _Stringpool *spool;
540
541   Repodata data;
542
543   memset(&data, 0, sizeof(data));
544   data.repo = repo;
545   data.fp = fp;
546   repopagestore_init(&data.store);
547
548   mypool = pool;
549
550   if (read_u32(&data) != ('S' << 24 | 'O' << 16 | 'L' << 8 | 'V'))
551     {
552       pool_debug(pool, SAT_ERROR, "not a SOLV file\n");
553       return SOLV_ERROR_NOT_SOLV;
554     }
555   solvversion = read_u32(&data);
556   switch (solvversion)
557     {
558       case SOLV_VERSION_8:
559         break;
560       default:
561         pool_debug(pool, SAT_ERROR, "unsupported SOLV version\n");
562         return SOLV_ERROR_UNSUPPORTED;
563     }
564
565   pool_freeidhashes(pool);
566
567   numid = read_u32(&data);
568   numrel = read_u32(&data);
569   numdir = read_u32(&data);
570   numsolv = read_u32(&data);
571   numkeys = read_u32(&data);
572   numschemata = read_u32(&data);
573   solvflags = read_u32(&data);
574
575   if (numdir && numdir < 2)
576     {
577       pool_debug(pool, SAT_ERROR, "bad number of dirs\n");
578       return SOLV_ERROR_CORRUPT;
579     }
580
581   if (parent)
582     {
583       if (numrel)
584         {
585           pool_debug(pool, SAT_ERROR, "relations are forbidden in a sub-repository\n");
586           return SOLV_ERROR_CORRUPT;
587         }
588       if (parent->end - parent->start != numsolv)
589         {
590           pool_debug(pool, SAT_ERROR, "sub-repository solvable number doesn't match main repository (%d - %d)\n", parent->end - parent->start, numsolv);
591           return SOLV_ERROR_CORRUPT;
592         }
593     }
594
595   /*******  Part 1: string IDs  *****************************************/
596
597   sizeid = read_u32(&data);            /* size of string+Id space */
598
599   /*
600    * read strings and Ids
601    * 
602    */
603
604   
605   /*
606    * alloc buffers
607    */
608
609   if (!parent)
610     spool = &pool->ss;
611   else
612     {
613       data.localpool = 1;
614       spool = &data.spool;
615       spool->stringspace = sat_malloc(7);
616       strcpy(spool->stringspace, "<NULL>");
617       spool->sstrings = 7;
618       spool->nstrings = 0;
619     }
620
621   /* alloc string buffer */
622   spool->stringspace = sat_realloc(spool->stringspace, spool->sstrings + sizeid + 1);
623   /* alloc string offsets (Id -> Offset into string space) */
624   spool->strings = sat_realloc2(spool->strings, spool->nstrings + numid, sizeof(Offset));
625
626   strsp = spool->stringspace;
627   str = spool->strings;                /* array of offsets into strsp, indexed by Id */
628
629   /* point to _BEHIND_ already allocated string/Id space */
630   strsp += spool->sstrings;
631
632
633   /*
634    * read new repo at end of pool
635    */
636   
637   if ((solvflags & SOLV_FLAG_PREFIX_POOL) == 0)
638     {
639       if (sizeid && fread(strsp, sizeid, 1, fp) != 1)
640         {
641           pool_debug(pool, SAT_ERROR, "read error while reading strings\n");
642           return SOLV_ERROR_EOF;
643         }
644     }
645   else
646     {
647       unsigned int pfsize = read_u32(&data);
648       char *prefix = sat_malloc(pfsize);
649       char *pp = prefix;
650       char *old_str = 0;
651       char *dest = strsp;
652       int freesp = sizeid;
653
654       if (pfsize && fread(prefix, pfsize, 1, fp) != 1)
655         {
656           pool_debug(pool, SAT_ERROR, "read error while reading strings\n");
657           sat_free(prefix);
658           return SOLV_ERROR_EOF;
659         }
660       for (i = 1; i < numid; i++)
661         {
662           int same = (unsigned char)*pp++;
663           size_t len = strlen(pp) + 1;
664           freesp -= same + len;
665           if (freesp < 0)
666             {
667               pool_debug(pool, SAT_ERROR, "overflow while expanding strings\n");
668               sat_free(prefix);
669               return SOLV_ERROR_OVERFLOW;
670             }
671           if (same)
672             memcpy(dest, old_str, same);
673           memcpy(dest + same, pp, len);
674           pp += len;
675           old_str = dest;
676           dest += same + len;
677         }
678       sat_free(prefix);
679       if (freesp != 0)
680         {
681           pool_debug(pool, SAT_ERROR, "expanding strings size mismatch\n");
682           return SOLV_ERROR_CORRUPT;
683         }
684     }
685   strsp[sizeid] = 0;                   /* make string space \0 terminated */
686   sp = strsp;
687
688   if (parent)
689     {
690       /* no shared pool, thus no idmap and no unification */
691       idmap = 0;
692       spool->nstrings = numid;
693       str[0] = 0;
694       if (*sp)
695         {
696           /* we need the '' for directories */
697           pool_debug(pool, SAT_ERROR, "store strings don't start with ''\n");
698           return SOLV_ERROR_CORRUPT;
699         }
700       for (i = 1; i < spool->nstrings; i++)
701         {
702           if (sp >= strsp + sizeid)
703             {
704               pool_debug(pool, SAT_ERROR, "not enough strings\n");
705               return SOLV_ERROR_OVERFLOW;
706             }
707           str[i] = sp - spool->stringspace;
708           sp += strlen(sp) + 1;
709         }
710       spool->sstrings = sp - spool->stringspace;
711     }
712   else
713     {
714
715       /* alloc id map for name and rel Ids. this maps ids in the solv files
716        * to the ids in our pool */
717       idmap = sat_calloc(numid + numrel, sizeof(Id));
718
719       /*
720        * build hashes for all read strings
721        * 
722        */
723       
724       hashmask = mkmask(spool->nstrings + numid);
725
726 #if 0
727       POOL_DEBUG(SAT_DEBUG_STATS, "read %d strings\n", numid);
728       POOL_DEBUG(SAT_DEBUG_STATS, "string hash buckets: %d\n", hashmask + 1);
729 #endif
730
731       /*
732        * create hashtable with strings already in pool
733        */
734
735       hashtbl = sat_calloc(hashmask + 1, sizeof(Id));
736       for (i = 1; i < spool->nstrings; i++)  /* leave out our dummy zero id */
737         {
738           h = strhash(spool->stringspace + spool->strings[i]) & hashmask;
739           hh = HASHCHAIN_START;
740           while (hashtbl[h])
741             h = HASHCHAIN_NEXT(h, hh, hashmask);
742           hashtbl[h] = i;
743         }
744
745       /*
746        * run over string space, calculate offsets
747        * 
748        * build id map (maps solv Id -> pool Id)
749        */
750       
751       for (i = 1; i < numid; i++)
752         {
753           if (sp >= strsp + sizeid)
754             {
755               sat_free(hashtbl);
756               sat_free(idmap);
757               pool_debug(pool, SAT_ERROR, "not enough strings %d %d\n", i, numid);
758               return SOLV_ERROR_OVERFLOW;
759             }
760           if (!*sp)                            /* empty string */
761             {
762               idmap[i] = ID_EMPTY;
763               sp++;
764               continue;
765             }
766
767           /* find hash slot */
768           h = strhash(sp) & hashmask;
769           hh = HASHCHAIN_START;
770           for (;;)
771             {
772               id = hashtbl[h];
773               if (id == 0)
774                 break;
775               if (!strcmp(spool->stringspace + spool->strings[id], sp))
776                 break;                 /* existing string */
777               h = HASHCHAIN_NEXT(h, hh, hashmask);
778             }
779
780           /* length == offset to next string */
781           l = strlen(sp) + 1;
782           if (id == ID_NULL)           /* end of hash chain -> new string */
783             {
784               id = spool->nstrings++;
785               hashtbl[h] = id;
786               str[id] = spool->sstrings;    /* save Offset */
787               if (sp != spool->stringspace + spool->sstrings)   /* not at end-of-buffer */
788                 memmove(spool->stringspace + spool->sstrings, sp, l);   /* append to pool buffer */
789               spool->sstrings += l;
790             }
791           idmap[i] = id;                       /* repo relative -> pool relative */
792           sp += l;                             /* next string */
793         }
794       sat_free(hashtbl);
795     }
796   pool_shrink_strings(pool);           /* vacuum */
797
798   
799   /*******  Part 2: Relation IDs  ***************************************/
800
801   /*
802    * read RelDeps
803    * 
804    */
805   
806   if (numrel)
807     {
808       /* extend rels */
809       pool->rels = sat_realloc2(pool->rels, pool->nrels + numrel, sizeof(Reldep));
810       ran = pool->rels;
811
812       hashmask = mkmask(pool->nrels + numrel);
813 #if 0
814       POOL_DEBUG(SAT_DEBUG_STATS, "read %d rels\n", numrel);
815       POOL_DEBUG(SAT_DEBUG_STATS, "rel hash buckets: %d\n", hashmask + 1);
816 #endif
817       /*
818        * prep hash table with already existing RelDeps
819        */
820       
821       hashtbl = sat_calloc(hashmask + 1, sizeof(Id));
822       for (i = 1; i < pool->nrels; i++)
823         {
824           h = relhash(ran[i].name, ran[i].evr, ran[i].flags) & hashmask;
825           hh = HASHCHAIN_START;
826           while (hashtbl[h])
827             h = HASHCHAIN_NEXT(h, hh, hashmask);
828           hashtbl[h] = i;
829         }
830
831       /*
832        * read RelDeps from repo
833        */
834       
835       for (i = 0; i < numrel; i++)
836         {
837           name = read_id(&data, i + numid);     /* read (repo relative) Ids */
838           evr = read_id(&data, i + numid);
839           flags = read_u8(&data);
840           name = idmap[name];           /* map to (pool relative) Ids */
841           evr = idmap[evr];
842           h = relhash(name, evr, flags) & hashmask;
843           hh = HASHCHAIN_START;
844           for (;;)
845             {
846               id = hashtbl[h];
847               if (id == ID_NULL)        /* end of hash chain */
848                 break;
849               if (ran[id].name == name && ran[id].evr == evr && ran[id].flags == flags)
850                 break;
851               h = HASHCHAIN_NEXT(h, hh, hashmask);
852             }
853           if (id == ID_NULL)            /* new RelDep */
854             {
855               id = pool->nrels++;
856               hashtbl[h] = id;
857               ran[id].name = name;
858               ran[id].evr = evr;
859               ran[id].flags = flags;
860             }
861           idmap[i + numid] = MAKERELDEP(id);   /* fill Id map */
862         }
863       sat_free(hashtbl);
864       pool_shrink_rels(pool);           /* vacuum */
865     }
866
867
868   /*******  Part 3: Dirs  ***********************************************/
869   if (numdir)
870     {
871       data.dirpool.dirs = sat_malloc2(numdir, sizeof(Id));
872       data.dirpool.ndirs = numdir;
873       data.dirpool.dirs[0] = 0;         /* dir 0: virtual root */
874       data.dirpool.dirs[1] = 1;         /* dir 1: / */
875       for (i = 2; i < numdir; i++)
876         {
877           id = read_id(&data, i + numid);
878           if (id >= numid)
879             data.dirpool.dirs[i] = -(id - numid);
880           else if (idmap)
881             data.dirpool.dirs[i] = idmap[id];
882           else
883             data.dirpool.dirs[i] = id;
884         }
885     }
886
887   /*******  Part 4: Keys  ***********************************************/
888
889   keys = sat_calloc(numkeys, sizeof(*keys));
890   /* keys start at 1 */
891   for (i = 1; i < numkeys; i++)
892     {
893       id = read_id(&data, numid);
894       if (idmap)
895         id = idmap[id];
896       else if (parent)
897         id = str2id(pool, stringpool_id2str(spool, id), 1);
898       type = read_id(&data, numid);
899       if (idmap)
900         type = idmap[type];
901       else if (parent)
902         type = str2id(pool, stringpool_id2str(spool, type), 1);
903       if (type < REPOKEY_TYPE_VOID || type > REPOKEY_TYPE_FLEXARRAY)
904         {
905           pool_debug(pool, SAT_ERROR, "unsupported data type '%s'\n", id2str(pool, type));
906           data.error = SOLV_ERROR_UNSUPPORTED;
907           type = REPOKEY_TYPE_VOID;
908         }
909       keys[i].name = id;
910       keys[i].type = type;
911       keys[i].size = read_id(&data, keys[i].type == REPOKEY_TYPE_CONSTANTID ? numid + numrel : 0);
912       keys[i].storage = read_id(&data, 0);
913       if (id >= SOLVABLE_NAME && id <= RPM_RPMDBID)
914         keys[i].storage = KEY_STORAGE_SOLVABLE;
915       else if (keys[i].storage == KEY_STORAGE_SOLVABLE)
916         keys[i].storage = KEY_STORAGE_INCORE;
917       if (keys[i].type == REPOKEY_TYPE_CONSTANTID)
918         {
919           if (idmap)
920             keys[i].size = idmap[keys[i].size];
921           else if (parent)
922             keys[i].size = str2id(pool, stringpool_id2str(spool, keys[i].size), 1);
923         }
924 #if 0
925       fprintf(stderr, "key %d %s %s %d %d\n", i, id2str(pool,id), id2str(pool, keys[i].type),
926                keys[i].size, keys[i].storage);
927 #endif
928     }
929
930   have_xdata = parent ? 1 : 0;
931   for (i = 1; i < numkeys; i++)
932     if (keys[i].storage == KEY_STORAGE_INCORE || keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET)
933       have_xdata = 1;
934
935   data.keys = keys;
936   data.nkeys = numkeys;
937   for (i = 1; i < numkeys; i++)
938     {
939       id = keys[i].name;
940       data.keybits[(id >> 3) & (sizeof(data.keybits) - 1)] |= 1 << (id & 7);
941     }
942
943   /*******  Part 5: Schemata ********************************************/
944   
945   id = read_id(&data, 0);
946   schemadata = sat_calloc(id + 1, sizeof(Id));
947   schemadatap = schemadata + 1;
948   schemadataend = schemadatap + id;
949   schemata = sat_calloc(numschemata, sizeof(Id));
950   for (i = 1; i < numschemata; i++)
951     {
952       schemata[i] = schemadatap - schemadata;
953       schemadatap = read_idarray(&data, numid, 0, schemadatap, schemadataend);
954 #if 0
955       Id *sp = schemadata + schemata[i];
956       fprintf (stderr, "schema %d:", i);
957       for (; *sp; sp++)
958         fprintf (stderr, " %d", *sp);
959       fprintf (stderr, "\n");
960 #endif
961     }
962   data.schemata = schemata;
963   data.nschemata = numschemata;
964   data.schemadata = schemadata;
965   data.schemadatalen = schemadataend - data.schemadata;
966
967   /*******  Part 6: Data ********************************************/
968
969   idarraydatap = idarraydataend = 0;
970   size_idarray = 0;
971
972   maxsize = read_id(&data, 0);
973   allsize = read_id(&data, 0);
974   maxsize += 5; /* so we can read the next schema */
975   if (maxsize > allsize)
976     maxsize = allsize;
977
978   left = 0;
979   buf = sat_calloc(maxsize + 4, 1);
980   dp = buf;
981
982   l = maxsize;
983   if (l > allsize)
984     l = allsize;
985   if (!l || fread(buf, l, 1, data.fp) != 1)
986     {
987       pool_debug(mypool, SAT_ERROR, "unexpected EOF\n");
988       data.error = SOLV_ERROR_EOF;
989       id = 0;
990     }
991   else
992     {
993       left = l;
994       allsize -= l;
995       dp = data_read_id_max(dp, &id, 0, numschemata, &data.error);
996     }
997
998   incore_add_id(&data, 0);      /* XXX? */
999   incore_add_id(&data, id);
1000   keyp = schemadata + schemata[id];
1001   data.mainschema = id;
1002   for (i = 0; keyp[i]; i++)
1003     ;
1004   if (i)
1005     data.mainschemaoffsets = sat_calloc(i, sizeof(Id));
1006
1007   nentries = 0;
1008   keydepth = 0;
1009   s = 0;
1010   for(;;)
1011     {
1012       key = *keyp++;
1013 #if 0
1014 printf("key %d at %d\n", key, keyp - 1 - schemadata);
1015 #endif
1016       if (!key)
1017         {
1018           if (nentries)
1019             {
1020               if (s && keydepth == 2)
1021                 {
1022                   s++;  /* next solvable */
1023                   if (have_xdata)
1024                     data.incoreoffset[(s - pool->solvables) - data.start] = data.incoredatalen;
1025                 }
1026               dp = data_read_id_max(dp, &id, 0, numschemata, &data.error);
1027               incore_add_id(&data, id);
1028               keyp = schemadata + schemata[id];
1029               nentries--;
1030               continue;
1031             }
1032           if (!keydepth)
1033             break;
1034           keyp = schemadata + stack[--keydepth];
1035           nentries = stack[--keydepth];
1036 #if 0
1037 printf("pop flexarray %d %d\n", keydepth, nentries);
1038 #endif
1039           if (!keydepth && s)
1040             s = 0;      /* back from solvables */
1041           continue;
1042         }
1043
1044       if (keydepth <= 2)
1045         {
1046           if (keydepth == 0)
1047             data.mainschemaoffsets[keyp - 1 - (schemadata + schemata[data.mainschema])] = data.incoredatalen;
1048           /* read data chunk to dp */
1049           if (data.error)
1050             break;
1051           left -= (dp - buf);
1052           if (left < 0)
1053             {
1054               pool_debug(mypool, SAT_ERROR, "buffer overrun\n");
1055               data.error = SOLV_ERROR_EOF;
1056               break;
1057             }
1058           if (left)
1059             memmove(buf, dp, left);
1060           l = maxsize - left;
1061           if (l > allsize)
1062             l = allsize;
1063           if (l && fread(buf + left, l, 1, data.fp) != 1)
1064             {
1065               pool_debug(mypool, SAT_ERROR, "unexpected EOF\n");
1066               data.error = SOLV_ERROR_EOF;
1067               break;
1068             }
1069           allsize -= l;
1070           left += l;
1071           dp = buf;
1072         }
1073
1074 #if 0
1075 printf("=> %s %s %p\n", id2str(pool, keys[key].name), id2str(pool, keys[key].type), s);
1076 #endif
1077       id = keys[key].name;
1078       if (keys[key].storage == KEY_STORAGE_VERTICAL_OFFSET)
1079         {
1080           dps = dp;
1081           dp = data_skip(dp, REPOKEY_TYPE_ID);
1082           dp = data_skip(dp, REPOKEY_TYPE_ID);
1083           incore_add_blob(&data, dps, dp - dps);
1084           continue;
1085         }
1086       switch (keys[key].type)
1087         {
1088         case REPOKEY_TYPE_ID:
1089           dp = data_read_id_max(dp, &did, idmap, numid + numrel, &data.error);
1090           if (s && id == SOLVABLE_NAME)
1091             s->name = did; 
1092           else if (s && id == SOLVABLE_ARCH)
1093             s->arch = did; 
1094           else if (s && id == SOLVABLE_EVR)
1095             s->evr = did; 
1096           else if (s && id == SOLVABLE_VENDOR)
1097             s->vendor = did; 
1098           else if (keys[key].storage == KEY_STORAGE_INCORE)
1099             incore_add_id(&data, did);
1100 #if 0
1101           POOL_DEBUG(SAT_DEBUG_STATS, "%s -> %s\n", id2str(pool, id), id2str(pool, did));
1102 #endif
1103           break;
1104         case REPOKEY_TYPE_U32:
1105           dp = data_read_u32(dp, &h);
1106 #if 0
1107           POOL_DEBUG(SAT_DEBUG_STATS, "%s -> %u\n", id2str(pool, id), h);
1108 #endif
1109           if (s && id == RPM_RPMDBID)
1110             {
1111               if (!repo->rpmdbid)
1112                 repo->rpmdbid = repo_sidedata_create(repo, sizeof(Id));
1113               repo->rpmdbid[(s - pool->solvables) - repo->start] = h;
1114             }
1115           else if (keys[key].storage == KEY_STORAGE_INCORE)
1116             incore_add_u32(&data, h);
1117           break;
1118         case REPOKEY_TYPE_IDARRAY:
1119         case REPOKEY_TYPE_REL_IDARRAY:
1120           if (!s || id < INTERESTED_START || id > INTERESTED_END)
1121             {
1122               dps = dp;
1123               dp = data_skip(dp, REPOKEY_TYPE_IDARRAY);
1124               if (keys[key].storage != KEY_STORAGE_INCORE)
1125                 break;
1126               if (idmap)
1127                 incore_map_idarray(&data, dps, idmap, numid);
1128               else
1129                 incore_add_blob(&data, dps, dp - dps);
1130               break;
1131             }
1132           ido = idarraydatap - repo->idarraydata;
1133           if (keys[key].type == REPOKEY_TYPE_IDARRAY)
1134             dp = data_read_idarray(dp, &idarraydatap, idmap, numid + numrel, &data.error);
1135           else if (id == SOLVABLE_REQUIRES)
1136             dp = data_read_rel_idarray(dp, &idarraydatap, idmap, numid + numrel, &data.error, SOLVABLE_PREREQMARKER);
1137           else if (id == SOLVABLE_PROVIDES)
1138             dp = data_read_rel_idarray(dp, &idarraydatap, idmap, numid + numrel, &data.error, SOLVABLE_FILEMARKER);
1139           else
1140             dp = data_read_rel_idarray(dp, &idarraydatap, idmap, numid + numrel, &data.error, 0);
1141           if (idarraydatap > idarraydataend)
1142             {
1143               pool_debug(pool, SAT_ERROR, "idarray overflow\n");
1144               data.error = SOLV_ERROR_OVERFLOW;
1145               break;
1146             }
1147           if (id == SOLVABLE_PROVIDES)
1148             s->provides = ido;
1149           else if (id == SOLVABLE_OBSOLETES)
1150             s->obsoletes = ido;
1151           else if (id == SOLVABLE_CONFLICTS)
1152             s->conflicts = ido;
1153           else if (id == SOLVABLE_REQUIRES)
1154             s->requires = ido;
1155           else if (id == SOLVABLE_RECOMMENDS)
1156             s->recommends= ido;
1157           else if (id == SOLVABLE_SUPPLEMENTS)
1158             s->supplements = ido;
1159           else if (id == SOLVABLE_SUGGESTS)
1160             s->suggests = ido;
1161           else if (id == SOLVABLE_ENHANCES)
1162             s->enhances = ido;
1163 #if 0
1164           POOL_DEBUG(SAT_DEBUG_STATS, "%s ->\n", id2str(pool, id));
1165           for (; repo->idarraydata[ido]; ido++)
1166             POOL_DEBUG(SAT_DEBUG_STATS,"  %s\n", dep2str(pool, repo->idarraydata[ido]));
1167 #endif
1168           break;
1169         case REPOKEY_TYPE_FLEXARRAY:
1170           if (keydepth == sizeof(stack)/sizeof(*stack))
1171             {
1172               pool_debug(pool, SAT_ERROR, "flexarray stack overflow\n");
1173               data.error = SOLV_ERROR_CORRUPT;
1174               break;
1175             }
1176           stack[keydepth++] = nentries;
1177           stack[keydepth++] = keyp - schemadata;
1178           dp = data_read_id(dp, &nentries);
1179           incore_add_id(&data, nentries);
1180           if (!nentries)
1181             {
1182               /* zero size array? */
1183               keydepth--;
1184               nentries = stack[--keydepth];
1185               break;
1186             }
1187           if (keydepth == 2 && id == REPOSITORY_SOLVABLES)
1188             {
1189               /* horray! here come the solvables */
1190               if (nentries != numsolv)
1191                 {
1192                   pool_debug(pool, SAT_ERROR, "inconsistent number of solvables: %d %d\n", nentries, numsolv);
1193                   data.error = SOLV_ERROR_CORRUPT;
1194                   break;
1195                 }
1196               if (idarraydatap)
1197                 {
1198                   pool_debug(pool, SAT_ERROR, "more than one solvable block\n");
1199                   data.error = SOLV_ERROR_CORRUPT;
1200                   break;
1201                 }
1202               if (parent)
1203                 s = pool_id2solvable(pool, parent->start);
1204               else
1205                 s = pool_id2solvable(pool, repo_add_solvable_block(repo, numsolv));
1206               data.start = s - pool->solvables;
1207               data.end = data.start + numsolv;
1208               repodata_extend_block(&data, data.start, numsolv);
1209               for (i = 1; i < numkeys; i++)
1210                 {
1211                   id = keys[i].name;
1212                   if ((keys[i].type == REPOKEY_TYPE_IDARRAY || keys[i].type == REPOKEY_TYPE_REL_IDARRAY)
1213                       && id >= INTERESTED_START && id <= INTERESTED_END)
1214                     size_idarray += keys[i].size;
1215                 }
1216               /* allocate needed space in repo */
1217               /* we add maxsize because it is an upper limit for all idarrays, thus we can't overflow */
1218               repo_reserve_ids(repo, 0, size_idarray + maxsize + 1);
1219               idarraydatap = repo->idarraydata + repo->idarraysize;
1220               repo->idarraysize += size_idarray;
1221               idarraydataend = idarraydatap + size_idarray;
1222               repo->lastoff = 0;
1223               if (have_xdata)
1224                 data.incoreoffset[(s - pool->solvables) - data.start] = data.incoredatalen;
1225             }
1226           nentries--;
1227           dp = data_read_id_max(dp, &id, 0, numschemata, &data.error);
1228           incore_add_id(&data, id);
1229           keyp = schemadata + schemata[id];
1230           break;
1231         default:
1232           dps = dp;
1233           dp = data_skip(dp, keys[key].type);
1234           if (keys[key].storage == KEY_STORAGE_INCORE)
1235             incore_add_blob(&data, dps, dp - dps);
1236           break;
1237         }
1238     }
1239   /* should shrink idarraydata again */
1240
1241   if (keydepth)
1242     {
1243       pool_debug(pool, SAT_ERROR, "unexpected EOF, depth = %d\n", keydepth);
1244       data.error = SOLV_ERROR_CORRUPT;
1245     }
1246   if (!data.error)
1247     {
1248       left -= (dp - buf);
1249       if (left < 0)
1250         {
1251           pool_debug(mypool, SAT_ERROR, "buffer overrun\n");
1252           data.error = SOLV_ERROR_EOF;
1253         }
1254     }
1255   sat_free(buf);
1256
1257   if (data.error)
1258     {
1259       /* free solvables */
1260       repo_free_solvable_block(repo, data.start, data.end - data.start, 1);
1261       /* free id array */
1262       repo->idarraysize -= size_idarray;
1263       /* free incore data */
1264       data.incoredata = sat_free(data.incoredata);
1265       data.incoredatalen = data.incoredatafree = 0;
1266     }
1267
1268   if (data.incoredatafree)
1269     {
1270       /* shrink excess size */
1271       data.incoredata = sat_realloc(data.incoredata, data.incoredatalen);
1272       data.incoredatafree = 0;
1273     }
1274
1275   for (i = 1; i < numkeys; i++)
1276     if (keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET)
1277       break;
1278   if (i < numkeys && !data.error)
1279     {
1280       Id fileoffset = 0;
1281       unsigned int pagesize;
1282       
1283       /* we have vertical data, make it available */
1284       data.verticaloffset = sat_calloc(numkeys, sizeof(Id));
1285       for (i = 1; i < numkeys; i++)
1286         if (keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET)
1287           {
1288             data.verticaloffset[i] = fileoffset;
1289             fileoffset += keys[i].size;
1290           }
1291       data.lastverticaloffset = fileoffset;
1292       pagesize = read_u32(&data);
1293       data.error = repopagestore_read_or_setup_pages(&data.store, data.fp, pagesize, fileoffset);
1294     }
1295   else
1296     {
1297       /* no longer needed */
1298       data.fp = 0;
1299     }
1300   sat_free(idmap);
1301   mypool = 0;
1302
1303   if (data.error)
1304     {
1305       /* XXX: free repodata? */
1306       return data.error;
1307     }
1308
1309   if (parent)
1310     {
1311       /* overwrite stub repodata */
1312       repodata_free(parent);
1313       *parent = data;
1314     }
1315   else
1316     {
1317       /* make it available as new repodata */
1318       repo->repodata = sat_realloc2(repo->repodata, repo->nrepodata + 1, sizeof(data));
1319       repo->repodata[repo->nrepodata++] = data;
1320     }
1321
1322   /* create stub repodata entries for all external */
1323   for (key = 1 ; key < data.nkeys; key++)
1324     if (data.keys[key].name == REPOSITORY_EXTERNAL && data.keys[key].type == REPOKEY_TYPE_FLEXARRAY)
1325       break;
1326   if (key < data.nkeys)
1327     {
1328       struct create_stub_data stubdata;
1329       /* got some */
1330       memset(&stubdata, 0, sizeof(stubdata));
1331       repodata_search(&data, REPOENTRY_META, REPOSITORY_EXTERNAL, create_stub_cb, &stubdata);
1332     }
1333   return 0;
1334 }
1335
1336 int
1337 repo_add_solv(Repo *repo, FILE *fp)
1338 {
1339   return repo_add_solv_parent(repo, fp, 0);
1340 }
1341
1342 static void
1343 repodata_load_stub(Repodata *data)
1344 {
1345   FILE *fp;
1346   Pool *pool = data->repo->pool;
1347   if (!pool->loadcallback)
1348     {   
1349       data->state = REPODATA_ERROR;
1350       return;
1351     }   
1352   /* so that we can retrieve meta data */
1353   data->state = REPODATA_AVAILABLE;
1354   fp = pool->loadcallback(pool, data, pool->loadcallbackdata);
1355   if (!fp)
1356     {   
1357       data->state = REPODATA_ERROR;
1358       return;
1359     }   
1360   if (repo_add_solv_parent(data->repo, fp, data))
1361     data->state = REPODATA_ERROR;
1362   else
1363     data->state = REPODATA_AVAILABLE;
1364   fclose(fp);
1365 }