- cleanup code a bit
[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 #define INTERESTED_START        SOLVABLE_NAME
30 #define INTERESTED_END          SOLVABLE_FRESHENS
31
32 static Pool *mypool;            /* for pool_debug... */
33
34 /*-----------------------------------------------------------------*/
35 /* .solv read functions */
36
37 /*
38  * read u32
39  */
40
41 static unsigned int
42 read_u32(FILE *fp)
43 {
44   int c, i;
45   unsigned int x = 0;
46
47   for (i = 0; i < 4; i++)
48     {
49       c = getc(fp);
50       if (c == EOF)
51         {
52           pool_debug(mypool, SAT_FATAL, "unexpected EOF\n");
53           exit(1);
54         }
55       x = (x << 8) | c;
56     }
57   return x;
58 }
59
60
61 /*
62  * read u8
63  */
64
65 static unsigned int
66 read_u8(FILE *fp)
67 {
68   int c;
69   c = getc(fp);
70   if (c == EOF)
71     {
72       pool_debug(mypool, SAT_FATAL, "unexpected EOF\n");
73       exit(1);
74     }
75   return c;
76 }
77
78
79 /*
80  * read Id
81  */
82
83 static Id
84 read_id(FILE *fp, Id max)
85 {
86   unsigned int x = 0;
87   int c, i;
88
89   for (i = 0; i < 5; i++)
90     {
91       c = getc(fp);
92       if (c == EOF)
93         {
94           pool_debug(mypool, SAT_FATAL, "unexpected EOF\n");
95           exit(1);
96         }
97       if (!(c & 128))
98         {
99           x = (x << 7) | c;
100           if (max && x >= max)
101             {
102               pool_debug(mypool, SAT_FATAL, "read_id: id too large (%u/%u)\n", x, max);
103               exit(1);
104             }
105           return x;
106         }
107       x = (x << 7) ^ c ^ 128;
108     }
109   pool_debug(mypool, SAT_FATAL, "read_id: id too long\n");
110   exit(1);
111 }
112
113
114 /*
115  * read array of Ids
116  */
117
118 static Id *
119 read_idarray(FILE *fp, Id max, Id *map, Id *store, Id *end, int relative)
120 {
121   unsigned int x = 0;
122   int c;
123   Id old = 0;
124   for (;;)
125     {
126       c = getc(fp);
127       if (c == EOF)
128         {
129           pool_debug(mypool, SAT_FATAL, "unexpected EOF\n");
130           exit(1);
131         }
132       if ((c & 128) == 0)
133         {
134           x = (x << 6) | (c & 63);
135           if (relative)
136             {
137               if (x == 0 && c == 0x40)
138                 {
139                   /* prereq hack */
140                   if (store == end)
141                     {
142                       pool_debug(mypool, SAT_FATAL, "read_idarray: array overflow\n");
143                       exit(1);
144                     }
145                   *store++ = SOLVABLE_PREREQMARKER;
146                   old = 0;
147                   x = 0;
148                   continue;
149                 }
150               x = (x - 1) + old;
151               old = x;
152             }
153           if (x >= max)
154             {
155               pool_debug(mypool, SAT_FATAL, "read_idarray: id too large (%u/%u)\n", x, max);
156               exit(1);
157             }
158           if (map)
159             x = map[x];
160           if (store == end)
161             {
162               pool_debug(mypool, SAT_FATAL, "read_idarray: array overflow\n");
163               exit(1);
164             }
165           *store++ = x;
166           if ((c & 64) == 0)
167             {
168               if (x == 0)       /* already have trailing zero? */
169                 return store;
170               if (store == end)
171                 {
172                   pool_debug(mypool, SAT_FATAL, "read_idarray: array overflow\n");
173                   exit(1);
174                 }
175               *store++ = 0;
176               return store;
177             }
178           x = 0;
179           continue;
180         }
181       x = (x << 7) ^ c ^ 128;
182     }
183 }
184
185
186 /*-----------------------------------------------------------------*/
187
188 struct key {
189   Id name;
190   Id type;
191   Id size;
192 };
193
194
195 // ----------------------------------------------
196
197 /*
198  * read repo from .solv file
199  *  and add it to pool
200  */
201
202 void
203 repo_add_solv(Repo *repo, FILE *fp)
204 {
205   Pool *pool = repo->pool;
206   int i, l;
207   unsigned int numid, numrel, numsolv;
208   unsigned int numkeys, numschemata, numinfo;
209
210   int type;
211   Offset sizeid;
212   Offset *str;                         /* map Id -> Offset into string space */
213   char *strsp;                         /* repo string space */
214   char *sp;                            /* pointer into string space */
215   Id *idmap;                           /* map of repo Ids to pool Ids */
216   Id id;
217   unsigned int hashmask, h;
218   int hh;
219   Id *hashtbl;
220   Id name, evr, did;
221   int flags;
222   Reldep *ran;
223   unsigned int size_idarray;
224   Id *idarraydatap, *idarraydataend;
225   Offset ido;
226   Solvable *s;
227   unsigned int solvflags;
228   unsigned int solvversion;
229   struct key *keys;
230   Id *schemadata, *schemadatap, *schemadataend;
231   Id *schemata, key;
232
233   mypool = pool;
234
235   if (read_u32(fp) != ('S' << 24 | 'O' << 16 | 'L' << 8 | 'V'))
236     {
237       pool_debug(pool, SAT_FATAL, "not a SOLV file\n");
238       exit(1);
239     }
240   solvversion = read_u32(fp);
241   switch (solvversion)
242     {
243       case SOLV_VERSION_1:
244       case SOLV_VERSION_2:
245         break;
246       default:
247         pool_debug(pool, SAT_FATAL, "unsupported SOLV version\n");
248         exit(1);
249     }
250
251   pool_freeidhashes(pool);
252
253   numid = read_u32(fp);
254   numrel = read_u32(fp);
255   numsolv = read_u32(fp);
256   numkeys = read_u32(fp);
257   numschemata = read_u32(fp);
258   numinfo = read_u32(fp);
259   solvflags = read_u32(fp);
260
261   /*******  Part 1: string IDs  *****************************************/
262
263   sizeid = read_u32(fp);               /* size of string+Id space */
264
265   /*
266    * read strings and Ids
267    * 
268    */
269
270   
271   /*
272    * alloc buffers
273    */
274
275   /* alloc string buffer */
276   strsp = (char *)xrealloc(pool->ss.stringspace, pool->ss.sstrings + sizeid + 1);
277   /* alloc string offsets (Id -> Offset into string space) */
278   str = (Offset *)xrealloc(pool->ss.strings, (pool->ss.nstrings + numid) * sizeof(Offset));
279
280   pool->ss.stringspace = strsp;
281   pool->ss.strings = str;                      /* array of offsets into strsp, indexed by Id */
282
283   /* point to _BEHIND_ already allocated string/Id space */
284   strsp += pool->ss.sstrings;
285
286   /* alloc id map for name and rel Ids. this maps ids in the solv files
287    * to the ids in our pool */
288   idmap = (Id *)xcalloc(numid + numrel, sizeof(Id));
289
290   /*
291    * read new repo at end of pool
292    */
293   
294   if ((solvflags & SOLV_FLAG_PREFIX_POOL) == 0)
295     {
296       if (fread(strsp, sizeid, 1, fp) != 1)
297         {
298           pool_debug(pool, SAT_FATAL, "read error while reading strings\n");
299           exit(1);
300         }
301     }
302   else
303     {
304       unsigned int pfsize = read_u32 (fp);
305       char *prefix = xmalloc (pfsize);
306       char *pp = prefix;
307       char *old_str = "";
308       char *dest = strsp;
309       if (fread (prefix, pfsize, 1, fp) != 1)
310         {
311           pool_debug(pool, SAT_FATAL, "read error while reading strings\n");
312           exit(1);
313         }
314       for (i = 1; i < numid; i++)
315         {
316           int same = (unsigned char)*pp++;
317           size_t len = strlen (pp) + 1;
318           if (same)
319             memcpy (dest, old_str, same);
320           memcpy (dest + same, pp, len);
321           pp += len;
322           old_str = dest;
323           dest += same + len;
324         }
325       xfree (prefix);
326     }
327   strsp[sizeid] = 0;                   /* make string space \0 terminated */
328   sp = strsp;
329
330   /*
331    * build hashes for all read strings
332    * 
333    */
334   
335   hashmask = mkmask(pool->ss.nstrings + numid);
336
337 #if 0
338   POOL_DEBUG(SAT_DEBUG_STATS, "read %d strings\n", numid);
339   POOL_DEBUG(SAT_DEBUG_STATS, "string hash buckets: %d\n", hashmask + 1);
340 #endif
341
342   /*
343    * ensure sufficient hash size
344    */
345   
346   hashtbl = (Id *)xcalloc(hashmask + 1, sizeof(Id));
347
348   /*
349    * fill hashtable with strings already in pool
350    */
351   
352   for (i = 1; i < pool->ss.nstrings; i++)  /* leave out our dummy zero id */
353     {
354       h = strhash(pool->ss.stringspace + pool->ss.strings[i]) & hashmask;
355       hh = HASHCHAIN_START;
356       while (hashtbl[h])
357         h = HASHCHAIN_NEXT(h, hh, hashmask);
358       hashtbl[h] = i;
359     }
360
361   /*
362    * run over string space, calculate offsets
363    * 
364    * build id map (maps solv Id -> pool Id)
365    */
366   
367   for (i = 1; i < numid; i++)
368     {
369       if (sp >= strsp + sizeid)
370         {
371           pool_debug(pool, SAT_FATAL, "not enough strings\n");
372           exit(1);
373         }
374       if (!*sp)                        /* empty string */
375         {
376           idmap[i] = ID_EMPTY;
377           sp++;
378           continue;
379         }
380
381       /* find hash slot */
382       h = strhash(sp) & hashmask;
383       hh = HASHCHAIN_START;
384       for (;;)
385         {
386           id = hashtbl[h];
387           if (id == 0)
388             break;
389           if (!strcmp(pool->ss.stringspace + pool->ss.strings[id], sp))
390             break;                     /* existing string */
391           h = HASHCHAIN_NEXT(h, hh, hashmask);
392         }
393
394       /* length == offset to next string */
395       l = strlen(sp) + 1;
396       if (id == ID_NULL)               /* end of hash chain -> new string */
397         {
398           id = pool->ss.nstrings++;
399           hashtbl[h] = id;
400           str[id] = pool->ss.sstrings;    /* save Offset */
401           if (sp != pool->ss.stringspace + pool->ss.sstrings)   /* not at end-of-buffer */
402             memmove(pool->ss.stringspace + pool->ss.sstrings, sp, l);   /* append to pool buffer */
403           pool->ss.sstrings += l;
404         }
405       idmap[i] = id;                   /* repo relative -> pool relative */
406       sp += l;                         /* next string */
407     }
408   xfree(hashtbl);
409   pool_shrink_strings(pool);           /* vacuum */
410
411   
412   /*******  Part 2: Relation IDs  ***************************************/
413
414   /*
415    * read RelDeps
416    * 
417    */
418   
419   if (numrel)
420     {
421       /* extend rels */
422       ran = (Reldep *)xrealloc(pool->rels, (pool->nrels + numrel) * sizeof(Reldep));
423       if (!ran)
424         {
425           pool_debug(pool, SAT_FATAL, "no mem for rel space\n");
426           exit(1);
427         }
428       pool->rels = ran;        /* extended rel space */
429
430       hashmask = mkmask(pool->nrels + numrel);
431 #if 0
432       POOL_DEBUG(SAT_DEBUG_STATS, "read %d rels\n", numrel);
433       POOL_DEBUG(SAT_DEBUG_STATS, "rel hash buckets: %d\n", hashmask + 1);
434 #endif
435       /*
436        * prep hash table with already existing RelDeps
437        */
438       
439       hashtbl = (Id *)xcalloc(hashmask + 1, sizeof(Id));
440       for (i = 1; i < pool->nrels; i++)
441         {
442           h = relhash(ran[i].name, ran[i].evr, ran[i].flags) & hashmask;
443           hh = HASHCHAIN_START;
444           while (hashtbl[h])
445             h = HASHCHAIN_NEXT(h, hh, hashmask);
446           hashtbl[h] = i;
447         }
448
449       /*
450        * read RelDeps from repo
451        */
452       
453       for (i = 0; i < numrel; i++)
454         {
455           name = read_id(fp, i + numid);        /* read (repo relative) Ids */
456           evr = read_id(fp, i + numid);
457           flags = read_u8(fp);
458           name = idmap[name];           /* map to (pool relative) Ids */
459           evr = idmap[evr];
460           h = relhash(name, evr, flags) & hashmask;
461           hh = HASHCHAIN_START;
462           for (;;)
463             {
464               id = hashtbl[h];
465               if (id == ID_NULL)        /* end of hash chain */
466                 break;
467               if (ran[id].name == name && ran[id].evr == evr && ran[id].flags == flags)
468                 break;
469               h = HASHCHAIN_NEXT(h, hh, hashmask);
470             }
471           if (id == ID_NULL)            /* new RelDep */
472             {
473               id = pool->nrels++;
474               hashtbl[h] = id;
475               ran[id].name = name;
476               ran[id].evr = evr;
477               ran[id].flags = flags;
478             }
479           idmap[i + numid] = MAKERELDEP(id);   /* fill Id map */
480         }
481       xfree(hashtbl);
482       pool_shrink_rels(pool);           /* vacuum */
483     }
484
485
486   /*******  Part 3: Keys  ***********************************************/
487
488   keys = xcalloc(numkeys, sizeof(*keys));
489   /* keys start at 1 */
490   for (i = 1; i < numkeys; i++)
491     {
492       keys[i].name = idmap[read_id(fp, numid)];
493       keys[i].type = read_id(fp, 0);
494       keys[i].size = read_id(fp, 0);
495     }
496
497   /*******  Part 4: Schemata ********************************************/
498   
499   id = read_id(fp, 0);
500   schemadata = xcalloc(id, sizeof(Id));
501   schemadatap = schemadata;
502   schemadataend = schemadata + id;
503   schemata = xcalloc(numschemata, sizeof(Id));
504   for (i = 0; i < numschemata; i++)
505     {
506       schemata[i] = schemadatap - schemadata;
507       schemadatap = read_idarray(fp, numid, 0, schemadatap, schemadataend, 0);
508     }
509
510   /*******  Part 5: Info  ***********************************************/
511   /* we skip the info for now... */
512   for (i = 0; i < numinfo; i++)
513     {
514       Id *schema = schemadata + schemata[read_id(fp, numschemata)];
515       while ((key = *schema++) != 0)
516         {
517           type = keys[key].type;
518           switch (type)
519             {
520               case TYPE_ID:
521                 read_id(fp, numid + numrel);   /* just check Id */
522                 break;
523               case TYPE_U32:
524                 read_u32(fp);
525                 break;
526               case TYPE_STR:
527                 while(read_u8(fp) != 0)
528                   ;
529                 break;
530               case TYPE_IDARRAY:
531               case TYPE_REL_IDARRAY:
532                 while ((read_u8(fp) & 0xc0) != 0)
533                   ;
534                 break;
535               default:
536                 pool_debug(pool, SAT_FATAL, "unknown type %d\n", type);
537                 exit(0);
538             }
539         }
540     }
541
542   /*******  Part 6: packed sizes (optional)  ****************************/
543   if ((solvflags & SOLV_FLAG_PACKEDSIZES) != 0)
544     {
545       for (i = 0; i < numsolv; i++)
546         read_id(fp, 0);
547     }
548
549   /*******  Part 7: item data *******************************************/
550
551   /* calculate idarray size */
552   size_idarray = 0;
553   for (i = 1; i < numkeys; i++)
554     {
555       id = keys[i].name;
556       if ((keys[i].type == TYPE_IDARRAY || keys[i].type == TYPE_REL_IDARRAY)
557           && id >= INTERESTED_START && id <= INTERESTED_END)
558         size_idarray += keys[i].size;
559     }
560
561   /* allocate needed space in repo */
562   if (size_idarray)
563     {
564       repo_reserve_ids(repo, 0, size_idarray);
565       idarraydatap = repo->idarraydata + repo->idarraysize;
566       repo->idarraysize += size_idarray;
567       idarraydataend = repo->idarraydata + repo->idarraysize;
568       repo->lastoff = 0;
569     }
570   else
571     {
572       idarraydatap = 0;
573       idarraydataend = 0;
574     }
575
576   /* read solvables */
577   s = pool_id2solvable(pool, repo_add_solvable_block(repo, numsolv));
578
579   if ((solvflags & SOLV_FLAG_VERTICAL) != 0)
580     {
581       Id *solvschema = xcalloc(numsolv, sizeof(Id));
582       unsigned char *used = xmalloc(numschemata);
583       Solvable *sstart = s;
584       Id type;
585
586       for (i = 0; i < numsolv; i++)
587         solvschema[i] = read_id(fp, numschemata);
588       for (key = 1; key < numkeys; key++)
589         {
590           id = keys[key].name;
591           type = keys[key].type;
592           memset(used, 0, numschemata);
593           for (i = 0; i < numschemata; i++)
594             {
595               Id *keyp = schemadata + schemata[i];
596               while (*keyp)
597                 if (*keyp++ == key)
598                   {
599                     used[i] = 1;
600                     break;
601                   }
602             }
603           for (i = 0, s = sstart; i < numsolv; i++, s++)
604              {
605               if (!used[solvschema[i]])
606                 continue;
607               switch (type)
608                 {
609                 case TYPE_ID:
610                   did = idmap[read_id(fp, numid + numrel)];
611                   if (id == SOLVABLE_NAME)
612                     s->name = did;
613                   else if (id == SOLVABLE_ARCH)
614                     s->arch = did;
615                   else if (id == SOLVABLE_EVR)
616                     s->evr = did;
617                   else if (id == SOLVABLE_VENDOR)
618                     s->vendor = did;
619                   break;
620                 case TYPE_U32:
621                   h = read_u32(fp);
622                   if (id == RPM_RPMDBID)
623                     {
624                       if (!repo->rpmdbid)
625                         repo->rpmdbid = (Id *)xcalloc(numsolv, sizeof(Id));
626                       repo->rpmdbid[i] = h;
627                     }
628                   break;
629                 case TYPE_STR:
630                   while(read_u8(fp) != 0)
631                     ;
632                   break;
633                 case TYPE_IDARRAY:
634                 case TYPE_REL_IDARRAY:
635                   if (id < INTERESTED_START || id > INTERESTED_END)
636                     {
637                       /* not interested in array */
638                       while ((read_u8(fp) & 0xc0) != 0)
639                         ;
640                       break;
641                     }
642                   ido = idarraydatap - repo->idarraydata;
643                   idarraydatap = read_idarray(fp, numid + numrel, idmap, idarraydatap, idarraydataend, type == TYPE_REL_IDARRAY);
644                   if (id == SOLVABLE_PROVIDES)
645                     s->provides = ido;
646                   else if (id == SOLVABLE_OBSOLETES)
647                     s->obsoletes = ido;
648                   else if (id == SOLVABLE_CONFLICTS)
649                     s->conflicts = ido;
650                   else if (id == SOLVABLE_REQUIRES)
651                     s->requires = ido;
652                   else if (id == SOLVABLE_RECOMMENDS)
653                     s->recommends= ido;
654                   else if (id == SOLVABLE_SUPPLEMENTS)
655                     s->supplements = ido;
656                   else if (id == SOLVABLE_SUGGESTS)
657                     s->suggests = ido;
658                   else if (id == SOLVABLE_ENHANCES)
659                     s->enhances = ido;
660                   else if (id == SOLVABLE_FRESHENS)
661                     s->freshens = ido;
662                   break;
663                 }
664             }
665         }
666       xfree(used);
667       xfree(solvschema);
668       xfree(idmap);
669       xfree(schemata);
670       xfree(schemadata);
671       xfree(keys);
672       mypool = 0;
673       return;
674     }
675   for (i = 0; i < numsolv; i++, s++)
676     {
677       Id *keyp = schemadata + schemata[read_id(fp, numschemata)];
678       while ((key = *keyp++) != 0)
679         {
680           id = keys[key].name;
681           switch (keys[key].type)
682             {
683             case TYPE_ID:
684               did = idmap[read_id(fp, numid + numrel)];
685               if (id == SOLVABLE_NAME)
686                 s->name = did;
687               else if (id == SOLVABLE_ARCH)
688                 s->arch = did;
689               else if (id == SOLVABLE_EVR)
690                 s->evr = did;
691               else if (id == SOLVABLE_VENDOR)
692                 s->vendor = did;
693 #if 0
694               POOL_DEBUG(SAT_DEBUG_STATS, "%s -> %s\n", id2str(pool, id), id2str(pool, did));
695 #endif
696               break;
697             case TYPE_U32:
698               h = read_u32(fp);
699 #if 0
700               POOL_DEBUG(SAT_DEBUG_STATS, "%s -> %u\n", id2str(pool, id), h);
701 #endif
702               if (id == RPM_RPMDBID)
703                 {
704                   if (!repo->rpmdbid)
705                     repo->rpmdbid = (Id *)xcalloc(numsolv, sizeof(Id));
706                   repo->rpmdbid[i] = h;
707                 }
708               break;
709             case TYPE_STR:
710               while(read_u8(fp) != 0)
711                 ;
712               break;
713             case TYPE_IDARRAY:
714             case TYPE_REL_IDARRAY:
715               if (id < INTERESTED_START || id > INTERESTED_END)
716                 {
717                   /* not interested in array */
718                   while ((read_u8(fp) & 0xc0) != 0)
719                     ;
720                   break;
721                 }
722               ido = idarraydatap - repo->idarraydata;
723               idarraydatap = read_idarray(fp, numid + numrel, idmap, idarraydatap, idarraydataend, keys[key].type == TYPE_REL_IDARRAY);
724               if (id == SOLVABLE_PROVIDES)
725                 s->provides = ido;
726               else if (id == SOLVABLE_OBSOLETES)
727                 s->obsoletes = ido;
728               else if (id == SOLVABLE_CONFLICTS)
729                 s->conflicts = ido;
730               else if (id == SOLVABLE_REQUIRES)
731                 s->requires = ido;
732               else if (id == SOLVABLE_RECOMMENDS)
733                 s->recommends= ido;
734               else if (id == SOLVABLE_SUPPLEMENTS)
735                 s->supplements = ido;
736               else if (id == SOLVABLE_SUGGESTS)
737                 s->suggests = ido;
738               else if (id == SOLVABLE_ENHANCES)
739                 s->enhances = ido;
740               else if (id == SOLVABLE_FRESHENS)
741                 s->freshens = ido;
742 #if 0
743               POOL_DEBUG(SAT_DEBUG_STATS, "%s ->\n", id2str(pool, id));
744               for (; repo->idarraydata[ido]; ido++)
745                 POOL_DEBUG(SAT_DEBUG_STATS,"  %s\n", dep2str(pool, repo->idarraydata[ido]));
746 #endif
747               break;
748             }
749         }
750     }
751   xfree(idmap);
752   xfree(schemata);
753   xfree(schemadata);
754   xfree(keys);
755   mypool = 0;
756 }
757
758 // EOF