1 /*
2  * Copyright (c) 2018, SUSE LLC.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7 
8 /*
9  * repodata.c
10  *
11  * Manage data coming from one repository
12  *
13  * a repository can contain multiple repodata entries, consisting of
14  * different sets of keys and different sets of solvables
15  */
16 
17 #define _GNU_SOURCE
18 #include <string.h>
19 #include <fnmatch.h>
20 
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <assert.h>
25 #include <regex.h>
26 
27 #include "repo.h"
28 #include "pool.h"
29 #include "poolid_private.h"
30 #include "util.h"
31 #include "hash.h"
32 #include "chksum.h"
33 
34 #include "repopack.h"
35 #include "repopage.h"
36 
37 #ifdef _WIN32
38   #include "strfncs.h"
39 #endif
40 
41 #define REPODATA_BLOCK 255
42 
43 static unsigned char *data_skip_key(Repodata *data, unsigned char *dp, Repokey *key);
44 
45 void
repodata_initdata(Repodata * data,Repo * repo,int localpool)46 repodata_initdata(Repodata *data, Repo *repo, int localpool)
47 {
48   memset(data, 0, sizeof (*data));
49   data->repodataid = data - repo->repodata;
50   data->repo = repo;
51   data->localpool = localpool;
52   if (localpool)
53     stringpool_init_empty(&data->spool);
54   /* dirpool_init(&data->dirpool);	just zeros out again */
55   data->keys = solv_calloc(1, sizeof(Repokey));
56   data->nkeys = 1;
57   data->schemata = solv_calloc(1, sizeof(Id));
58   data->schemadata = solv_calloc(1, sizeof(Id));
59   data->nschemata = 1;
60   data->schemadatalen = 1;
61   repopagestore_init(&data->store);
62 }
63 
64 void
repodata_freedata(Repodata * data)65 repodata_freedata(Repodata *data)
66 {
67   int i;
68 
69   solv_free(data->keys);
70 
71   solv_free(data->schemata);
72   solv_free(data->schemadata);
73   solv_free(data->schematahash);
74 
75   stringpool_free(&data->spool);
76   dirpool_free(&data->dirpool);
77 
78   solv_free(data->mainschemaoffsets);
79   solv_free(data->incoredata);
80   solv_free(data->incoreoffset);
81   solv_free(data->verticaloffset);
82 
83   repopagestore_free(&data->store);
84 
85   solv_free(data->vincore);
86 
87   if (data->attrs)
88     for (i = 0; i < data->end - data->start; i++)
89       solv_free(data->attrs[i]);
90   solv_free(data->attrs);
91   if (data->xattrs)
92     for (i = 0; i < data->nxattrs; i++)
93       solv_free(data->xattrs[i]);
94   solv_free(data->xattrs);
95 
96   solv_free(data->attrdata);
97   solv_free(data->attriddata);
98   solv_free(data->attrnum64data);
99 
100   solv_free(data->dircache);
101 
102   repodata_free_filelistfilter(data);
103 }
104 
105 void
repodata_free(Repodata * data)106 repodata_free(Repodata *data)
107 {
108   Repo *repo = data->repo;
109   int i = data - repo->repodata;
110   if (i == 0)
111     return;
112   repodata_freedata(data);
113   if (i < repo->nrepodata - 1)
114     {
115       /* whoa! this changes the repodataids! */
116       memmove(repo->repodata + i, repo->repodata + i + 1, (repo->nrepodata - 1 - i) * sizeof(Repodata));
117       for (; i < repo->nrepodata - 1; i++)
118 	repo->repodata[i].repodataid = i;
119     }
120   repo->nrepodata--;
121   if (repo->nrepodata == 1)
122     {
123       repo->repodata = solv_free(repo->repodata);
124       repo->nrepodata = 0;
125     }
126 }
127 
128 void
repodata_empty(Repodata * data,int localpool)129 repodata_empty(Repodata *data, int localpool)
130 {
131   void (*loadcallback)(Repodata *) = data->loadcallback;
132   int state = data->state;
133   repodata_freedata(data);
134   repodata_initdata(data, data->repo, localpool);
135   data->state = state;
136   data->loadcallback = loadcallback;
137 }
138 
139 
140 /***************************************************************
141  * key pool management
142  */
143 
144 /* this is not so time critical that we need a hash, so we do a simple
145  * linear search */
146 Id
repodata_key2id(Repodata * data,Repokey * key,int create)147 repodata_key2id(Repodata *data, Repokey *key, int create)
148 {
149   Id keyid;
150 
151   for (keyid = 1; keyid < data->nkeys; keyid++)
152     if (data->keys[keyid].name == key->name && data->keys[keyid].type == key->type)
153       {
154         if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != data->keys[keyid].size)
155           continue;
156         break;
157       }
158   if (keyid == data->nkeys)
159     {
160       if (!create)
161 	return 0;
162       /* allocate new key */
163       data->keys = solv_realloc2(data->keys, data->nkeys + 1, sizeof(Repokey));
164       data->keys[data->nkeys++] = *key;
165       if (data->verticaloffset)
166         {
167           data->verticaloffset = solv_realloc2(data->verticaloffset, data->nkeys, sizeof(Id));
168           data->verticaloffset[data->nkeys - 1] = 0;
169         }
170       data->keybits[(key->name >> 3) & (sizeof(data->keybits) - 1)] |= 1 << (key->name & 7);
171     }
172   return keyid;
173 }
174 
175 
176 /***************************************************************
177  * schema pool management
178  */
179 
180 #define SCHEMATA_BLOCK 31
181 #define SCHEMATADATA_BLOCK 255
182 
183 Id
repodata_schema2id(Repodata * data,Id * schema,int create)184 repodata_schema2id(Repodata *data, Id *schema, int create)
185 {
186   int h, len, i;
187   Id *sp, cid;
188   Id *schematahash;
189 
190   if (!*schema)
191     return 0;	/* XXX: allow empty schema? */
192   if ((schematahash = data->schematahash) == 0)
193     {
194       data->schematahash = schematahash = solv_calloc(256, sizeof(Id));
195       for (i = 1; i < data->nschemata; i++)
196 	{
197 	  for (sp = data->schemadata + data->schemata[i], h = 0; *sp;)
198 	    h = h * 7 + *sp++;
199 	  h &= 255;
200 	  schematahash[h] = i;
201 	}
202       data->schemadata = solv_extend_resize(data->schemadata, data->schemadatalen, sizeof(Id), SCHEMATADATA_BLOCK);
203       data->schemata = solv_extend_resize(data->schemata, data->nschemata, sizeof(Id), SCHEMATA_BLOCK);
204     }
205 
206   for (sp = schema, len = 0, h = 0; *sp; len++)
207     h = h * 7 + *sp++;
208   h &= 255;
209   len++;
210 
211   cid = schematahash[h];
212   if (cid)
213     {
214       if ((data->schemata[cid] + len <= data->schemadatalen) &&
215 			  !memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
216         return cid;
217       /* cache conflict, do a slow search */
218       for (cid = 1; cid < data->nschemata; cid++)
219         if ((data->schemata[cid] + len <= data->schemadatalen) &&
220 				!memcmp(data->schemadata + data->schemata[cid], schema, len * sizeof(Id)))
221           return cid;
222     }
223   /* a new one */
224   if (!create)
225     return 0;
226   data->schemadata = solv_extend(data->schemadata, data->schemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
227   data->schemata = solv_extend(data->schemata, data->nschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
228   /* add schema */
229   memcpy(data->schemadata + data->schemadatalen, schema, len * sizeof(Id));
230   data->schemata[data->nschemata] = data->schemadatalen;
231   data->schemadatalen += len;
232   schematahash[h] = data->nschemata;
233 #if 0
234 fprintf(stderr, "schema2id: new schema\n");
235 #endif
236   return data->nschemata++;
237 }
238 
239 void
repodata_free_schemahash(Repodata * data)240 repodata_free_schemahash(Repodata *data)
241 {
242   data->schematahash = solv_free(data->schematahash);
243   /* shrink arrays */
244   data->schemata = solv_realloc2(data->schemata, data->nschemata, sizeof(Id));
245   data->schemadata = solv_realloc2(data->schemadata, data->schemadatalen, sizeof(Id));
246 }
247 
248 
249 /***************************************************************
250  * dir pool management
251  */
252 
253 #ifndef HAVE_STRCHRNUL
strchrnul(const char * str,char x)254 static inline const char *strchrnul(const char *str, char x)
255 {
256   const char *p = strchr(str, x);
257   return p ? p : str + strlen(str);
258 }
259 #endif
260 
261 #define DIRCACHE_SIZE 41	/* < 1k */
262 
263 #ifdef DIRCACHE_SIZE
264 struct dircache {
265   Id ids[DIRCACHE_SIZE];
266   char str[(DIRCACHE_SIZE * (DIRCACHE_SIZE - 1)) / 2];
267 };
268 #endif
269 
270 Id
repodata_str2dir(Repodata * data,const char * dir,int create)271 repodata_str2dir(Repodata *data, const char *dir, int create)
272 {
273   Id id, parent;
274 #ifdef DIRCACHE_SIZE
275   const char *dirs;
276 #endif
277   const char *dire;
278 
279   if (!*dir)
280     return data->dirpool.ndirs ? 0 : dirpool_add_dir(&data->dirpool, 0, 0, create);
281   while (*dir == '/' && dir[1] == '/')
282     dir++;
283   if (*dir == '/' && !dir[1])
284     return data->dirpool.ndirs ? 1 : dirpool_add_dir(&data->dirpool, 0, 1, create);
285   parent = 0;
286 #ifdef DIRCACHE_SIZE
287   dirs = dir;
288   if (data->dircache)
289     {
290       int l;
291       struct dircache *dircache = data->dircache;
292       l = strlen(dir);
293       while (l > 0)
294 	{
295 	  if (l < DIRCACHE_SIZE && dircache->ids[l] && !memcmp(dircache->str + l * (l - 1) / 2, dir, l))
296 	    {
297 	      parent = dircache->ids[l];
298 	      dir += l;
299 	      if (!*dir)
300 		return parent;
301 	      while (*dir == '/')
302 		dir++;
303 	      break;
304 	    }
305 	  while (--l)
306 	    if (dir[l] == '/')
307 	      break;
308 	}
309     }
310 #endif
311   while (*dir)
312     {
313       dire = strchrnul(dir, '/');
314       if (data->localpool)
315         id = stringpool_strn2id(&data->spool, dir, dire - dir, create);
316       else
317 	id = pool_strn2id(data->repo->pool, dir, dire - dir, create);
318       if (!id)
319 	return 0;
320       parent = dirpool_add_dir(&data->dirpool, parent, id, create);
321       if (!parent)
322 	return 0;
323 #ifdef DIRCACHE_SIZE
324       if (!data->dircache)
325 	data->dircache = solv_calloc(1, sizeof(struct dircache));
326       if (data->dircache)
327 	{
328 	  int l = dire - dirs;
329 	  if (l < DIRCACHE_SIZE)
330 	    {
331 	      data->dircache->ids[l] = parent;
332 	      memcpy(data->dircache->str + l * (l - 1) / 2, dirs, l);
333 	    }
334 	}
335 #endif
336       if (!*dire)
337 	break;
338       dir = dire + 1;
339       while (*dir == '/')
340 	dir++;
341     }
342   return parent;
343 }
344 
345 void
repodata_free_dircache(Repodata * data)346 repodata_free_dircache(Repodata *data)
347 {
348   data->dircache = solv_free(data->dircache);
349 }
350 
351 const char *
repodata_dir2str(Repodata * data,Id did,const char * suf)352 repodata_dir2str(Repodata *data, Id did, const char *suf)
353 {
354   Pool *pool = data->repo->pool;
355   int l = 0;
356   Id parent, comp;
357   const char *comps;
358   char *p;
359 
360   if (!did)
361     return suf ? suf : "";
362   if (did == 1 && !suf)
363     return "/";
364   parent = did;
365   while (parent)
366     {
367       comp = dirpool_compid(&data->dirpool, parent);
368       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
369       l += strlen(comps);
370       parent = dirpool_parent(&data->dirpool, parent);
371       if (parent)
372 	l++;
373     }
374   if (suf)
375     l += strlen(suf) + 1;
376   p = pool_alloctmpspace(pool, l + 1) + l;
377   *p = 0;
378   if (suf)
379     {
380       p -= strlen(suf);
381       strcpy(p, suf);
382       *--p = '/';
383     }
384   parent = did;
385   while (parent)
386     {
387       comp = dirpool_compid(&data->dirpool, parent);
388       comps = stringpool_id2str(data->localpool ? &data->spool : &pool->ss, comp);
389       l = strlen(comps);
390       p -= l;
391       memcpy(p, comps, l);
392       parent = dirpool_parent(&data->dirpool, parent);
393       if (parent)
394         *--p = '/';
395     }
396   return p;
397 }
398 
399 
400 /***************************************************************
401  * data management
402  */
403 
404 static inline unsigned char *
data_skip_schema(Repodata * data,unsigned char * dp,Id schema)405 data_skip_schema(Repodata *data, unsigned char *dp, Id schema)
406 {
407   Id *keyp = data->schemadata + data->schemata[schema];
408   for (; *keyp; keyp++)
409     dp = data_skip_key(data, dp, data->keys + *keyp);
410   return dp;
411 }
412 
413 static unsigned char *
data_skip_key(Repodata * data,unsigned char * dp,Repokey * key)414 data_skip_key(Repodata *data, unsigned char *dp, Repokey *key)
415 {
416   int nentries, schema;
417   switch(key->type)
418     {
419     case REPOKEY_TYPE_FIXARRAY:
420       dp = data_read_id(dp, &nentries);
421       if (!nentries)
422 	return dp;
423       dp = data_read_id(dp, &schema);
424       while (nentries--)
425 	dp = data_skip_schema(data, dp, schema);
426       return dp;
427     case REPOKEY_TYPE_FLEXARRAY:
428       dp = data_read_id(dp, &nentries);
429       while (nentries--)
430 	{
431 	  dp = data_read_id(dp, &schema);
432 	  dp = data_skip_schema(data, dp, schema);
433 	}
434       return dp;
435     default:
436       if (key->storage == KEY_STORAGE_INCORE)
437         dp = data_skip(dp, key->type);
438       else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
439 	{
440 	  dp = data_skip(dp, REPOKEY_TYPE_ID);
441 	  dp = data_skip(dp, REPOKEY_TYPE_ID);
442 	}
443       return dp;
444     }
445 }
446 
447 static unsigned char *
forward_to_key(Repodata * data,Id keyid,Id * keyp,unsigned char * dp)448 forward_to_key(Repodata *data, Id keyid, Id *keyp, unsigned char *dp)
449 {
450   Id k;
451 
452   if (!keyid)
453     return 0;
454   if (data->mainschemaoffsets && dp == data->incoredata + data->mainschemaoffsets[0] && keyp == data->schemadata + data->schemata[data->mainschema])
455     {
456       int i;
457       for (i = 0; (k = *keyp++) != 0; i++)
458         if (k == keyid)
459 	  return data->incoredata + data->mainschemaoffsets[i];
460       return 0;
461     }
462   while ((k = *keyp++) != 0)
463     {
464       if (k == keyid)
465 	return dp;
466       if (data->keys[k].storage == KEY_STORAGE_VERTICAL_OFFSET)
467 	{
468 	  dp = data_skip(dp, REPOKEY_TYPE_ID);	/* skip offset */
469 	  dp = data_skip(dp, REPOKEY_TYPE_ID);	/* skip length */
470 	  continue;
471 	}
472       if (data->keys[k].storage != KEY_STORAGE_INCORE)
473 	continue;
474       dp = data_skip_key(data, dp, data->keys + k);
475     }
476   return 0;
477 }
478 
479 static unsigned char *
get_vertical_data(Repodata * data,Repokey * key,Id off,Id len)480 get_vertical_data(Repodata *data, Repokey *key, Id off, Id len)
481 {
482   unsigned char *dp;
483   if (len <= 0)
484     return 0;
485   if (off >= data->lastverticaloffset)
486     {
487       off -= data->lastverticaloffset;
488       if ((unsigned int)off + len > data->vincorelen)
489 	return 0;
490       return data->vincore + off;
491     }
492   if ((unsigned int)off + len > key->size)
493     return 0;
494   /* we now have the offset, go into vertical */
495   off += data->verticaloffset[key - data->keys];
496   /* fprintf(stderr, "key %d page %d\n", key->name, off / REPOPAGE_BLOBSIZE); */
497   dp = repopagestore_load_page_range(&data->store, off / REPOPAGE_BLOBSIZE, (off + len - 1) / REPOPAGE_BLOBSIZE);
498   data->storestate++;
499   if (dp)
500     dp += off % REPOPAGE_BLOBSIZE;
501   return dp;
502 }
503 
504 static inline unsigned char *
get_data(Repodata * data,Repokey * key,unsigned char ** dpp,int advance)505 get_data(Repodata *data, Repokey *key, unsigned char **dpp, int advance)
506 {
507   unsigned char *dp = *dpp;
508 
509   if (!dp)
510     return 0;
511   if (key->storage == KEY_STORAGE_INCORE)
512     {
513       if (advance)
514         *dpp = data_skip_key(data, dp, key);
515       return dp;
516     }
517   else if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
518     {
519       Id off, len;
520       dp = data_read_id(dp, &off);
521       dp = data_read_id(dp, &len);
522       if (advance)
523         *dpp = dp;
524       return get_vertical_data(data, key, off, len);
525     }
526   return 0;
527 }
528 
529 void
repodata_load(Repodata * data)530 repodata_load(Repodata *data)
531 {
532   if (data->state != REPODATA_STUB)
533     return;
534   if (data->loadcallback)
535     data->loadcallback(data);
536   else
537     data->state = REPODATA_ERROR;
538 }
539 
540 static int
maybe_load_repodata_stub(Repodata * data,Id keyname)541 maybe_load_repodata_stub(Repodata *data, Id keyname)
542 {
543   if (data->state != REPODATA_STUB)
544     {
545       data->state = REPODATA_ERROR;
546       return 0;
547     }
548   if (keyname)
549     {
550       int i;
551       for (i = 1; i < data->nkeys; i++)
552 	if (keyname == data->keys[i].name)
553 	  break;
554       if (i == data->nkeys)
555 	return 0;
556     }
557   repodata_load(data);
558   return data->state == REPODATA_AVAILABLE ? 1 : 0;
559 }
560 
561 static inline int
maybe_load_repodata(Repodata * data,Id keyname)562 maybe_load_repodata(Repodata *data, Id keyname)
563 {
564   if (keyname && !repodata_precheck_keyname(data, keyname))
565     return 0;	/* do not bother... */
566   if (data->state == REPODATA_AVAILABLE || data->state == REPODATA_LOADING)
567     return 1;
568   if (data->state == REPODATA_ERROR)
569     return 0;
570   return maybe_load_repodata_stub(data, keyname);
571 }
572 
573 static inline unsigned char *
solvid2data(Repodata * data,Id solvid,Id * schemap)574 solvid2data(Repodata *data, Id solvid, Id *schemap)
575 {
576   unsigned char *dp = data->incoredata;
577   if (!dp)
578     return 0;
579   if (solvid == SOLVID_META)
580     dp += 1;	/* offset of "meta" solvable */
581   else if (solvid == SOLVID_POS)
582     {
583       Pool *pool = data->repo->pool;
584       if (data->repo != pool->pos.repo)
585 	return 0;
586       if (data != data->repo->repodata + pool->pos.repodataid)
587 	return 0;
588       dp += pool->pos.dp;
589       if (pool->pos.dp != 1)
590         {
591           *schemap = pool->pos.schema;
592           return dp;
593 	}
594     }
595   else
596     {
597       if (solvid < data->start || solvid >= data->end)
598 	return 0;
599       dp += data->incoreoffset[solvid - data->start];
600     }
601   return data_read_id(dp, schemap);
602 }
603 
604 /************************************************************************
605  * data lookup
606  */
607 
608 static unsigned char *
find_key_data(Repodata * data,Id solvid,Id keyname,Repokey ** keypp)609 find_key_data(Repodata *data, Id solvid, Id keyname, Repokey **keypp)
610 {
611   unsigned char *dp;
612   Id schema, *keyp, *kp;
613   Repokey *key;
614 
615   if (!maybe_load_repodata(data, keyname))
616     return 0;
617   dp = solvid2data(data, solvid, &schema);
618   if (!dp)
619     return 0;
620   keyp = data->schemadata + data->schemata[schema];
621   for (kp = keyp; *kp; kp++)
622     if (data->keys[*kp].name == keyname)
623       break;
624   if (!*kp)
625     return 0;
626   *keypp = key = data->keys + *kp;
627   if (key->type == REPOKEY_TYPE_DELETED)
628     return 0;
629   if (key->type == REPOKEY_TYPE_VOID || key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID)
630     return dp;	/* no need to forward... */
631   if (key->storage != KEY_STORAGE_INCORE && key->storage != KEY_STORAGE_VERTICAL_OFFSET)
632     return 0;	/* get_data will not work, no need to forward */
633   dp = forward_to_key(data, *kp, keyp, dp);
634   if (!dp)
635     return 0;
636   return get_data(data, key, &dp, 0);
637 }
638 
639 static const Id *
repodata_lookup_schemakeys(Repodata * data,Id solvid)640 repodata_lookup_schemakeys(Repodata *data, Id solvid)
641 {
642   Id schema;
643   if (!maybe_load_repodata(data, 0))
644     return 0;
645   if (!solvid2data(data, solvid, &schema))
646     return 0;
647   return data->schemadata + data->schemata[schema];
648 }
649 
650 static Id *
alloc_keyskip()651 alloc_keyskip()
652 {
653   Id *keyskip = solv_calloc(3 + 256, sizeof(Id));
654   keyskip[0] = 256;
655   keyskip[1] = keyskip[2] = 1;
656   return keyskip;
657 }
658 
659 Id *
repodata_fill_keyskip(Repodata * data,Id solvid,Id * keyskip)660 repodata_fill_keyskip(Repodata *data, Id solvid, Id *keyskip)
661 {
662   const Id *keyp;
663   Id maxkeyname, value;
664   keyp = repodata_lookup_schemakeys(data, solvid);
665   if (!keyp)
666     return keyskip;	/* no keys for this solvid */
667   if (!keyskip)
668     keyskip = alloc_keyskip();
669   maxkeyname = keyskip[0];
670   value = keyskip[1] + data->repodataid;
671   for (; *keyp; keyp++)
672     {
673       Id keyname = data->keys[*keyp].name;
674       if (keyname >= maxkeyname)
675 	{
676 	  int newmax = (keyname | 255) + 1;
677 	  keyskip = solv_realloc2(keyskip, 3 + newmax, sizeof(Id));
678 	  memset(keyskip + (3 + maxkeyname), 0, (newmax - maxkeyname) * sizeof(Id));
679 	  keyskip[0] = maxkeyname = newmax;
680 	}
681       keyskip[3 + keyname] = value;
682     }
683   return keyskip;
684 }
685 
686 Id
repodata_lookup_type(Repodata * data,Id solvid,Id keyname)687 repodata_lookup_type(Repodata *data, Id solvid, Id keyname)
688 {
689   Id schema, *keyp, *kp;
690   if (!maybe_load_repodata(data, keyname))
691     return 0;
692   if (!solvid2data(data, solvid, &schema))
693     return 0;
694   keyp = data->schemadata + data->schemata[schema];
695   for (kp = keyp; *kp; kp++)
696     if (data->keys[*kp].name == keyname)
697       return data->keys[*kp].type;
698   return 0;
699 }
700 
701 Id
repodata_lookup_id(Repodata * data,Id solvid,Id keyname)702 repodata_lookup_id(Repodata *data, Id solvid, Id keyname)
703 {
704   unsigned char *dp;
705   Repokey *key;
706   Id id;
707 
708   dp = find_key_data(data, solvid, keyname, &key);
709   if (!dp)
710     return 0;
711   if (key->type == REPOKEY_TYPE_CONSTANTID)
712     return key->size;
713   if (key->type != REPOKEY_TYPE_ID)
714     return 0;
715   dp = data_read_id(dp, &id);
716   return id;
717 }
718 
719 const char *
repodata_lookup_str(Repodata * data,Id solvid,Id keyname)720 repodata_lookup_str(Repodata *data, Id solvid, Id keyname)
721 {
722   unsigned char *dp;
723   Repokey *key;
724   Id id;
725 
726   dp = find_key_data(data, solvid, keyname, &key);
727   if (!dp)
728     return 0;
729   if (key->type == REPOKEY_TYPE_STR)
730     return (const char *)dp;
731   if (key->type == REPOKEY_TYPE_CONSTANTID)
732     id = key->size;
733   else if (key->type == REPOKEY_TYPE_ID)
734     dp = data_read_id(dp, &id);
735   else
736     return 0;
737   if (data->localpool)
738     return stringpool_id2str(&data->spool, id);
739   return pool_id2str(data->repo->pool, id);
740 }
741 
742 unsigned long long
repodata_lookup_num(Repodata * data,Id solvid,Id keyname,unsigned long long notfound)743 repodata_lookup_num(Repodata *data, Id solvid, Id keyname, unsigned long long notfound)
744 {
745   unsigned char *dp;
746   Repokey *key;
747   unsigned int high, low;
748 
749   dp = find_key_data(data, solvid, keyname, &key);
750   if (!dp)
751     return notfound;
752   switch (key->type)
753     {
754     case REPOKEY_TYPE_NUM:
755       data_read_num64(dp, &low, &high);
756       return (unsigned long long)high << 32 | low;
757     case REPOKEY_TYPE_CONSTANT:
758       return key->size;
759     default:
760       return notfound;
761     }
762 }
763 
764 int
repodata_lookup_void(Repodata * data,Id solvid,Id keyname)765 repodata_lookup_void(Repodata *data, Id solvid, Id keyname)
766 {
767   return repodata_lookup_type(data, solvid, keyname) == REPOKEY_TYPE_VOID ? 1 : 0;
768 }
769 
770 const unsigned char *
repodata_lookup_bin_checksum(Repodata * data,Id solvid,Id keyname,Id * typep)771 repodata_lookup_bin_checksum(Repodata *data, Id solvid, Id keyname, Id *typep)
772 {
773   unsigned char *dp;
774   Repokey *key;
775 
776   dp = find_key_data(data, solvid, keyname, &key);
777   if (!dp)
778     return 0;
779   switch (key->type)
780     {
781     case_CHKSUM_TYPES:
782       break;
783     default:
784       return 0;
785     }
786   *typep = key->type;
787   return dp;
788 }
789 
790 int
repodata_lookup_idarray(Repodata * data,Id solvid,Id keyname,Queue * q)791 repodata_lookup_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
792 {
793   unsigned char *dp;
794   Repokey *key;
795   Id id;
796   int eof = 0;
797 
798   queue_empty(q);
799   dp = find_key_data(data, solvid, keyname, &key);
800   if (!dp)
801     return 0;
802   switch (key->type)
803     {
804     case REPOKEY_TYPE_CONSTANTID:
805       queue_push(q, key->size);
806       break;
807     case REPOKEY_TYPE_ID:
808       dp = data_read_id(dp, &id);
809       queue_push(q, id);
810       break;
811     case REPOKEY_TYPE_IDARRAY:
812       for (;;)
813 	{
814 	  dp = data_read_ideof(dp, &id, &eof);
815 	  queue_push(q, id);
816 	  if (eof)
817 	    break;
818 	}
819       break;
820     default:
821       return 0;
822     }
823   return 1;
824 }
825 
826 const void *
repodata_lookup_binary(Repodata * data,Id solvid,Id keyname,int * lenp)827 repodata_lookup_binary(Repodata *data, Id solvid, Id keyname, int *lenp)
828 {
829   unsigned char *dp;
830   Repokey *key;
831   Id len;
832 
833   dp = find_key_data(data, solvid, keyname, &key);
834   if (!dp || key->type != REPOKEY_TYPE_BINARY)
835     {
836       *lenp = 0;
837       return 0;
838     }
839   dp = data_read_id(dp, &len);
840   *lenp = len;
841   return dp;
842 }
843 
844 unsigned int
repodata_lookup_count(Repodata * data,Id solvid,Id keyname)845 repodata_lookup_count(Repodata *data, Id solvid, Id keyname)
846 {
847   unsigned char *dp;
848   Repokey *key;
849   unsigned int cnt = 0;
850 
851   dp = find_key_data(data, solvid, keyname, &key);
852   if (!dp)
853     return 0;
854   switch (key->type)
855     {
856     case REPOKEY_TYPE_IDARRAY:
857     case REPOKEY_TYPE_REL_IDARRAY:
858       for (cnt = 1; (*dp & 0xc0) != 0; dp++)
859 	if ((*dp & 0xc0) == 0x40)
860 	  cnt++;
861       return cnt;
862     case REPOKEY_TYPE_FIXARRAY:
863     case REPOKEY_TYPE_FLEXARRAY:
864       data_read_id(dp, (int *)&cnt);
865       return cnt;
866     case REPOKEY_TYPE_DIRSTRARRAY:
867       for (;;)
868 	{
869 	  cnt++;
870 	  while (*dp & 0x80)
871 	    dp++;
872 	  if (!(*dp++ & 0x40))
873 	    return cnt;
874 	  dp += strlen((const char *)dp) + 1;
875 	}
876     case REPOKEY_TYPE_DIRNUMNUMARRAY:
877       for (;;)
878 	{
879 	  cnt++;
880 	  while (*dp++ & 0x80)
881 	    ;
882 	  while (*dp++ & 0x80)
883 	    ;
884 	  while (*dp & 0x80)
885 	    dp++;
886 	  if (!(*dp++ & 0x40))
887 	    return cnt;
888 	}
889       default:
890 	break;
891     }
892   return 1;
893 }
894 
895 /* highly specialized function to speed up fileprovides adding.
896  * - repodata must be available
897  * - solvid must be >= data->start and < data->end
898  * - returns NULL is not found, a "" entry if wrong type
899  * - also returns wrong type for REPOKEY_TYPE_DELETED
900  */
901 const unsigned char *
repodata_lookup_packed_dirstrarray(Repodata * data,Id solvid,Id keyname)902 repodata_lookup_packed_dirstrarray(Repodata *data, Id solvid, Id keyname)
903 {
904   static unsigned char wrongtype[2] = { 0x00 /* dir id 0 */, 0 /* "" */ };
905   unsigned char *dp;
906   Id schema, *keyp, *kp;
907   Repokey *key;
908 
909   if (!data->incoredata || !data->incoreoffset[solvid - data->start])
910     return 0;
911   dp = data->incoredata + data->incoreoffset[solvid - data->start];
912   dp = data_read_id(dp, &schema);
913   keyp = data->schemadata + data->schemata[schema];
914   for (kp = keyp; *kp; kp++)
915     if (data->keys[*kp].name == keyname)
916       break;
917   if (!*kp)
918     return 0;
919   key = data->keys + *kp;
920   if (key->type != REPOKEY_TYPE_DIRSTRARRAY)
921     return wrongtype;
922   dp = forward_to_key(data, *kp, keyp, dp);
923   if (key->storage == KEY_STORAGE_INCORE)
924     return dp;
925   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET && dp)
926     {
927       Id off, len;
928       dp = data_read_id(dp, &off);
929       data_read_id(dp, &len);
930       return get_vertical_data(data, key, off, len);
931     }
932   return 0;
933 }
934 
935 /* id translation functions */
936 
937 Id
repodata_globalize_id(Repodata * data,Id id,int create)938 repodata_globalize_id(Repodata *data, Id id, int create)
939 {
940   if (!id || !data || !data->localpool)
941     return id;
942   return pool_str2id(data->repo->pool, stringpool_id2str(&data->spool, id), create);
943 }
944 
945 Id
repodata_localize_id(Repodata * data,Id id,int create)946 repodata_localize_id(Repodata *data, Id id, int create)
947 {
948   if (!id || !data || !data->localpool)
949     return id;
950   return stringpool_str2id(&data->spool, pool_id2str(data->repo->pool, id), create);
951 }
952 
953 Id
repodata_translate_id(Repodata * data,Repodata * fromdata,Id id,int create)954 repodata_translate_id(Repodata *data, Repodata *fromdata, Id id, int create)
955 {
956   const char *s;
957   if (!id || !data || !fromdata)
958     return id;
959   if (data == fromdata || (!data->localpool && !fromdata->localpool))
960     return id;
961   if (fromdata->localpool)
962     s = stringpool_id2str(&fromdata->spool, id);
963   else
964     s = pool_id2str(data->repo->pool, id);
965   if (data->localpool)
966     return stringpool_str2id(&data->spool, s, create);
967   else
968     return pool_str2id(data->repo->pool, s, create);
969 }
970 
971 Id
repodata_translate_dir_slow(Repodata * data,Repodata * fromdata,Id dir,int create,Id * cache)972 repodata_translate_dir_slow(Repodata *data, Repodata *fromdata, Id dir, int create, Id *cache)
973 {
974   Id parent, compid;
975   if (!dir)
976     {
977       /* make sure that the dirpool has an entry */
978       if (create && !data->dirpool.ndirs)
979         dirpool_add_dir(&data->dirpool, 0, 0, create);
980       return 0;
981     }
982   parent = dirpool_parent(&fromdata->dirpool, dir);
983   if (parent)
984     {
985       if (!(parent = repodata_translate_dir(data, fromdata, parent, create, cache)))
986 	return 0;
987     }
988   compid = dirpool_compid(&fromdata->dirpool, dir);
989   if (compid > 1 && (data->localpool || fromdata->localpool))
990     {
991       if (!(compid = repodata_translate_id(data, fromdata, compid, create)))
992 	return 0;
993     }
994   if (!(compid = dirpool_add_dir(&data->dirpool, parent, compid, create)))
995     return 0;
996   if (cache)
997     {
998       cache[(dir & 255) * 2] = dir;
999       cache[(dir & 255) * 2 + 1] = compid;
1000     }
1001   return compid;
1002 }
1003 
1004 /************************************************************************
1005  * uninternalized lookup / search
1006  */
1007 
1008 static void
data_fetch_uninternalized(Repodata * data,Repokey * key,Id value,KeyValue * kv)1009 data_fetch_uninternalized(Repodata *data, Repokey *key, Id value, KeyValue *kv)
1010 {
1011   Id *array;
1012   kv->eof = 1;
1013   switch (key->type)
1014     {
1015     case REPOKEY_TYPE_STR:
1016       kv->str = (const char *)data->attrdata + value;
1017       return;
1018     case REPOKEY_TYPE_CONSTANT:
1019       kv->num2 = 0;
1020       kv->num = key->size;
1021       return;
1022     case REPOKEY_TYPE_CONSTANTID:
1023       kv->id = key->size;
1024       return;
1025     case REPOKEY_TYPE_NUM:
1026       kv->num2 = 0;
1027       kv->num = value;
1028       if (value & 0x80000000)
1029 	{
1030 	  kv->num = (unsigned int)data->attrnum64data[value ^ 0x80000000];
1031 	  kv->num2 = (unsigned int)(data->attrnum64data[value ^ 0x80000000] >> 32);
1032 	}
1033       return;
1034     case_CHKSUM_TYPES:
1035       kv->num = 0;	/* not stringified */
1036       kv->str = (const char *)data->attrdata + value;
1037       return;
1038     case REPOKEY_TYPE_BINARY:
1039       kv->str = (const char *)data_read_id(data->attrdata + value, (Id *)&kv->num);
1040       return;
1041     case REPOKEY_TYPE_IDARRAY:
1042       array = data->attriddata + (value + kv->entry);
1043       kv->id = array[0];
1044       kv->eof = array[1] ? 0 : 1;
1045       return;
1046     case REPOKEY_TYPE_DIRSTRARRAY:
1047       kv->num = 0;	/* not stringified */
1048       array = data->attriddata + (value + kv->entry * 2);
1049       kv->id = array[0];
1050       kv->str = (const char *)data->attrdata + array[1];
1051       kv->eof = array[2] ? 0 : 1;
1052       return;
1053     case REPOKEY_TYPE_DIRNUMNUMARRAY:
1054       array = data->attriddata + (value + kv->entry * 3);
1055       kv->id = array[0];
1056       kv->num = array[1];
1057       kv->num2 = array[2];
1058       kv->eof = array[3] ? 0 : 1;
1059       return;
1060     case REPOKEY_TYPE_FIXARRAY:
1061     case REPOKEY_TYPE_FLEXARRAY:
1062       array = data->attriddata + (value + kv->entry);
1063       kv->id = array[0];		/* the handle */
1064       kv->eof = array[1] ? 0 : 1;
1065       return;
1066     default:
1067       kv->id = value;
1068       return;
1069     }
1070 }
1071 
1072 Repokey *
repodata_lookup_kv_uninternalized(Repodata * data,Id solvid,Id keyname,KeyValue * kv)1073 repodata_lookup_kv_uninternalized(Repodata *data, Id solvid, Id keyname, KeyValue *kv)
1074 {
1075   Id *ap;
1076   if (!data->attrs || solvid < data->start || solvid >= data->end)
1077     return 0;
1078   ap = data->attrs[solvid - data->start];
1079   if (!ap)
1080     return 0;
1081   for (; *ap; ap += 2)
1082     {
1083       Repokey *key = data->keys + *ap;
1084       if (key->name != keyname)
1085 	continue;
1086       data_fetch_uninternalized(data, key, ap[1], kv);
1087       return key;
1088     }
1089   return 0;
1090 }
1091 
1092 void
repodata_search_uninternalized(Repodata * data,Id solvid,Id keyname,int flags,int (* callback)(void * cbdata,Solvable * s,Repodata * data,Repokey * key,KeyValue * kv),void * cbdata)1093 repodata_search_uninternalized(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
1094 {
1095   Id *ap;
1096   int stop;
1097   Solvable *s;
1098   KeyValue kv;
1099 
1100   if (!data->attrs || solvid < data->start || solvid >= data->end)
1101     return;
1102   ap = data->attrs[solvid - data->start];
1103   if (!ap)
1104     return;
1105   for (; *ap; ap += 2)
1106     {
1107       Repokey *key = data->keys + *ap;
1108       if (keyname && key->name != keyname)
1109 	continue;
1110       s = solvid > 0 ? data->repo->pool->solvables + solvid : 0;
1111       kv.entry = 0;
1112       do
1113 	{
1114 	  data_fetch_uninternalized(data, key, ap[1], &kv);
1115 	  stop = callback(cbdata, s, data, key, &kv);
1116 	  kv.entry++;
1117 	}
1118       while (!kv.eof && !stop);
1119       if (keyname || stop > SEARCH_NEXT_KEY)
1120 	return;
1121     }
1122 }
1123 
1124 /************************************************************************
1125  * data search
1126  */
1127 
1128 
1129 const char *
repodata_stringify(Pool * pool,Repodata * data,Repokey * key,KeyValue * kv,int flags)1130 repodata_stringify(Pool *pool, Repodata *data, Repokey *key, KeyValue *kv, int flags)
1131 {
1132   switch (key->type)
1133     {
1134     case REPOKEY_TYPE_ID:
1135     case REPOKEY_TYPE_CONSTANTID:
1136     case REPOKEY_TYPE_IDARRAY:
1137       if (data && data->localpool)
1138 	kv->str = stringpool_id2str(&data->spool, kv->id);
1139       else
1140 	kv->str = pool_id2str(pool, kv->id);
1141       if ((flags & SEARCH_SKIP_KIND) != 0 && key->storage == KEY_STORAGE_SOLVABLE && (key->name == SOLVABLE_NAME || key->type == REPOKEY_TYPE_IDARRAY))
1142 	{
1143 	  const char *s;
1144 	  for (s = kv->str; *s >= 'a' && *s <= 'z'; s++)
1145 	    ;
1146 	  if (*s == ':' && s > kv->str)
1147 	    kv->str = s + 1;
1148 	}
1149       return kv->str;
1150     case REPOKEY_TYPE_STR:
1151       return kv->str;
1152     case REPOKEY_TYPE_DIRSTRARRAY:
1153       if (!(flags & SEARCH_FILES))
1154 	return kv->str;	/* match just the basename */
1155       if (kv->num)
1156 	return kv->str;	/* already stringified */
1157       /* Put the full filename into kv->str.  */
1158       kv->str = repodata_dir2str(data, kv->id, kv->str);
1159       kv->num = 1;	/* mark stringification */
1160       return kv->str;
1161     case_CHKSUM_TYPES:
1162       if (!(flags & SEARCH_CHECKSUMS))
1163 	return 0;	/* skip em */
1164       if (kv->num)
1165 	return kv->str;	/* already stringified */
1166       kv->str = repodata_chk2str(data, key->type, (const unsigned char *)kv->str);
1167       kv->num = 1;	/* mark stringification */
1168       return kv->str;
1169     default:
1170       return 0;
1171     }
1172 }
1173 
1174 
1175 /* this is an internal hack to pass the parent kv to repodata_search_keyskip */
1176 struct subschema_data {
1177   void *cbdata;
1178   Id solvid;
1179   KeyValue *parent;
1180 };
1181 
1182 void
repodata_search_arrayelement(Repodata * data,Id solvid,Id keyname,int flags,KeyValue * kv,int (* callback)(void * cbdata,Solvable * s,Repodata * data,Repokey * key,KeyValue * kv),void * cbdata)1183 repodata_search_arrayelement(Repodata *data, Id solvid, Id keyname, int flags, KeyValue *kv, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
1184 {
1185   repodata_search_keyskip(data, solvid, keyname, flags | SEARCH_SUBSCHEMA, (Id *)kv, callback, cbdata);
1186 }
1187 
1188 static int
repodata_search_array(Repodata * data,Id solvid,Id keyname,int flags,Repokey * key,KeyValue * kv,int (* callback)(void * cbdata,Solvable * s,Repodata * data,Repokey * key,KeyValue * kv),void * cbdata)1189 repodata_search_array(Repodata *data, Id solvid, Id keyname, int flags, Repokey *key, KeyValue *kv, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
1190 {
1191   Solvable *s = solvid > 0 ? data->repo->pool->solvables + solvid : 0;
1192   unsigned char *dp = (unsigned char *)kv->str;
1193   int stop;
1194   Id schema = 0;
1195 
1196   if (!dp || kv->entry != -1)
1197     return 0;
1198   while (++kv->entry < (int)kv->num)
1199     {
1200       if (kv->entry)
1201 	dp = data_skip_schema(data, dp, schema);
1202       if (kv->entry == 0 || key->type == REPOKEY_TYPE_FLEXARRAY)
1203 	dp = data_read_id(dp, &schema);
1204       kv->id = schema;
1205       kv->str = (const char *)dp;
1206       kv->eof = kv->entry == kv->num - 1 ? 1 : 0;
1207       stop = callback(cbdata, s, data, key, kv);
1208       if (stop && stop != SEARCH_ENTERSUB)
1209 	return stop;
1210       if ((flags & SEARCH_SUB) != 0 || stop == SEARCH_ENTERSUB)
1211         repodata_search_keyskip(data, solvid, keyname, flags | SEARCH_SUBSCHEMA, (Id *)kv, callback, cbdata);
1212     }
1213   if ((flags & SEARCH_ARRAYSENTINEL) != 0)
1214     {
1215       if (kv->entry)
1216 	dp = data_skip_schema(data, dp, schema);
1217       kv->id = 0;
1218       kv->str = (const char *)dp;
1219       kv->eof = 2;
1220       return callback(cbdata, s, data, key, kv);
1221     }
1222   return 0;
1223 }
1224 
1225 /* search a specific repodata */
1226 void
repodata_search_keyskip(Repodata * data,Id solvid,Id keyname,int flags,Id * keyskip,int (* callback)(void * cbdata,Solvable * s,Repodata * data,Repokey * key,KeyValue * kv),void * cbdata)1227 repodata_search_keyskip(Repodata *data, Id solvid, Id keyname, int flags, Id *keyskip, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
1228 {
1229   Id schema;
1230   Repokey *key;
1231   Id keyid, *kp, *keyp;
1232   unsigned char *dp, *ddp;
1233   int onekey = 0;
1234   int stop;
1235   KeyValue kv;
1236   Solvable *s;
1237 
1238   if (!maybe_load_repodata(data, keyname))
1239     return;
1240   if ((flags & SEARCH_SUBSCHEMA) != 0)
1241     {
1242       flags ^= SEARCH_SUBSCHEMA;
1243       kv.parent = (KeyValue *)keyskip;
1244       keyskip = 0;
1245       schema = kv.parent->id;
1246       dp = (unsigned char *)kv.parent->str;
1247     }
1248   else
1249     {
1250       schema = 0;
1251       dp = solvid2data(data, solvid, &schema);
1252       if (!dp)
1253 	return;
1254       kv.parent = 0;
1255     }
1256   s = solvid > 0 ? data->repo->pool->solvables + solvid : 0;
1257   keyp = data->schemadata + data->schemata[schema];
1258   if (keyname)
1259     {
1260       /* search for a specific key */
1261       for (kp = keyp; *kp; kp++)
1262 	if (data->keys[*kp].name == keyname)
1263 	  break;
1264       if (!*kp)
1265 	return;
1266       dp = forward_to_key(data, *kp, keyp, dp);
1267       if (!dp)
1268 	return;
1269       keyp = kp;
1270       onekey = 1;
1271     }
1272   while ((keyid = *keyp++) != 0)
1273     {
1274       stop = 0;
1275       key = data->keys + keyid;
1276       ddp = get_data(data, key, &dp, *keyp && !onekey ? 1 : 0);
1277 
1278       if (keyskip && (key->name >= keyskip[0] || keyskip[3 + key->name] != keyskip[1] + data->repodataid))
1279 	{
1280 	  if (onekey)
1281 	    return;
1282 	  continue;
1283 	}
1284       if (key->type == REPOKEY_TYPE_DELETED && !(flags & SEARCH_KEEP_TYPE_DELETED))
1285 	{
1286 	  if (onekey)
1287 	    return;
1288 	  continue;
1289 	}
1290       if (key->type == REPOKEY_TYPE_FLEXARRAY || key->type == REPOKEY_TYPE_FIXARRAY)
1291 	{
1292 	  kv.entry = -1;
1293 	  ddp = data_read_id(ddp, (Id *)&kv.num);
1294 	  kv.str = (const char *)ddp;
1295 	  stop = repodata_search_array(data, solvid, 0, flags, key, &kv, callback, cbdata);
1296 	  if (onekey || stop > SEARCH_NEXT_KEY)
1297 	    return;
1298 	  continue;
1299 	}
1300       kv.entry = 0;
1301       do
1302 	{
1303 	  ddp = data_fetch(ddp, &kv, key);
1304 	  if (!ddp)
1305 	    break;
1306 	  stop = callback(cbdata, s, data, key, &kv);
1307 	  kv.entry++;
1308 	}
1309       while (!kv.eof && !stop);
1310       if (onekey || stop > SEARCH_NEXT_KEY)
1311 	return;
1312     }
1313 }
1314 
1315 void
repodata_search(Repodata * data,Id solvid,Id keyname,int flags,int (* callback)(void * cbdata,Solvable * s,Repodata * data,Repokey * key,KeyValue * kv),void * cbdata)1316 repodata_search(Repodata *data, Id solvid, Id keyname, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv), void *cbdata)
1317 {
1318   repodata_search_keyskip(data, solvid, keyname, flags, 0, callback, cbdata);
1319 }
1320 
1321 void
repodata_setpos_kv(Repodata * data,KeyValue * kv)1322 repodata_setpos_kv(Repodata *data, KeyValue *kv)
1323 {
1324   Pool *pool = data->repo->pool;
1325   if (!kv)
1326     pool_clear_pos(pool);
1327   else
1328     {
1329       pool->pos.repo = data->repo;
1330       pool->pos.repodataid = data - data->repo->repodata;
1331       pool->pos.dp = (unsigned char *)kv->str - data->incoredata;
1332       pool->pos.schema = kv->id;
1333     }
1334 }
1335 
1336 /************************************************************************
1337  * data iterator functions
1338  */
1339 
1340 static inline Id *
solvabledata_fetch(Solvable * s,KeyValue * kv,Id keyname)1341 solvabledata_fetch(Solvable *s, KeyValue *kv, Id keyname)
1342 {
1343   kv->id = keyname;
1344   switch (keyname)
1345     {
1346     case SOLVABLE_NAME:
1347       kv->eof = 1;
1348       return &s->name;
1349     case SOLVABLE_ARCH:
1350       kv->eof = 1;
1351       return &s->arch;
1352     case SOLVABLE_EVR:
1353       kv->eof = 1;
1354       return &s->evr;
1355     case SOLVABLE_VENDOR:
1356       kv->eof = 1;
1357       return &s->vendor;
1358     case SOLVABLE_PROVIDES:
1359       kv->eof = 0;
1360       return s->provides ? s->repo->idarraydata + s->provides : 0;
1361     case SOLVABLE_OBSOLETES:
1362       kv->eof = 0;
1363       return s->obsoletes ? s->repo->idarraydata + s->obsoletes : 0;
1364     case SOLVABLE_CONFLICTS:
1365       kv->eof = 0;
1366       return s->conflicts ? s->repo->idarraydata + s->conflicts : 0;
1367     case SOLVABLE_REQUIRES:
1368       kv->eof = 0;
1369       return s->requires ? s->repo->idarraydata + s->requires : 0;
1370     case SOLVABLE_RECOMMENDS:
1371       kv->eof = 0;
1372       return s->recommends ? s->repo->idarraydata + s->recommends : 0;
1373     case SOLVABLE_SUPPLEMENTS:
1374       kv->eof = 0;
1375       return s->supplements ? s->repo->idarraydata + s->supplements : 0;
1376     case SOLVABLE_SUGGESTS:
1377       kv->eof = 0;
1378       return s->suggests ? s->repo->idarraydata + s->suggests : 0;
1379     case SOLVABLE_ENHANCES:
1380       kv->eof = 0;
1381       return s->enhances ? s->repo->idarraydata + s->enhances : 0;
1382     case RPM_RPMDBID:
1383       kv->eof = 1;
1384       return s->repo->rpmdbid ? s->repo->rpmdbid + (s - s->repo->pool->solvables - s->repo->start) : 0;
1385     default:
1386       return 0;
1387     }
1388 }
1389 
1390 int
datamatcher_init(Datamatcher * ma,const char * match,int flags)1391 datamatcher_init(Datamatcher *ma, const char *match, int flags)
1392 {
1393   match = match ? solv_strdup(match) : 0;
1394   ma->match = match;
1395   ma->flags = flags;
1396   ma->error = 0;
1397   ma->matchdata = 0;
1398   if ((flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
1399     {
1400       ma->matchdata = solv_calloc(1, sizeof(regex_t));
1401       ma->error = regcomp((regex_t *)ma->matchdata, match, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | ((flags & SEARCH_NOCASE) ? REG_ICASE : 0));
1402       if (ma->error)
1403 	{
1404 	  solv_free(ma->matchdata);
1405 	  ma->flags = (flags & ~SEARCH_STRINGMASK) | SEARCH_ERROR;
1406 	}
1407     }
1408   if ((flags & SEARCH_FILES) != 0 && match)
1409     {
1410       /* prepare basename check */
1411       if ((flags & SEARCH_STRINGMASK) == SEARCH_STRING || (flags & SEARCH_STRINGMASK) == SEARCH_STRINGEND)
1412 	{
1413 	  const char *p = strrchr(match, '/');
1414 	  ma->matchdata = (void *)(p ? p + 1 : match);
1415 	}
1416       else if ((flags & SEARCH_STRINGMASK) == SEARCH_GLOB)
1417 	{
1418 	  const char *p;
1419 	  for (p = match + strlen(match) - 1; p >= match; p--)
1420 	    if (*p == '[' || *p == ']' || *p == '*' || *p == '?' || *p == '/')
1421 	      break;
1422 	  ma->matchdata = (void *)(p + 1);
1423 	}
1424     }
1425   return ma->error;
1426 }
1427 
1428 void
datamatcher_free(Datamatcher * ma)1429 datamatcher_free(Datamatcher *ma)
1430 {
1431   if (ma->match)
1432     ma->match = solv_free((char *)ma->match);
1433   if ((ma->flags & SEARCH_STRINGMASK) == SEARCH_REGEX && ma->matchdata)
1434     {
1435       regfree(ma->matchdata);
1436       solv_free(ma->matchdata);
1437     }
1438   ma->matchdata = 0;
1439 }
1440 
1441 int
datamatcher_match(Datamatcher * ma,const char * str)1442 datamatcher_match(Datamatcher *ma, const char *str)
1443 {
1444   int l;
1445   switch ((ma->flags & SEARCH_STRINGMASK))
1446     {
1447     case SEARCH_SUBSTRING:
1448       if (ma->flags & SEARCH_NOCASE)
1449 	return strcasestr(str, ma->match) != 0;
1450       else
1451 	return strstr(str, ma->match) != 0;
1452     case SEARCH_STRING:
1453       if (ma->flags & SEARCH_NOCASE)
1454 	return !strcasecmp(ma->match, str);
1455       else
1456 	return !strcmp(ma->match, str);
1457     case SEARCH_STRINGSTART:
1458       if (ma->flags & SEARCH_NOCASE)
1459         return !strncasecmp(ma->match, str, strlen(ma->match));
1460       else
1461         return !strncmp(ma->match, str, strlen(ma->match));
1462     case SEARCH_STRINGEND:
1463       l = strlen(str) - strlen(ma->match);
1464       if (l < 0)
1465 	return 0;
1466       if (ma->flags & SEARCH_NOCASE)
1467 	return !strcasecmp(ma->match, str + l);
1468       else
1469 	return !strcmp(ma->match, str + l);
1470     case SEARCH_GLOB:
1471       return !fnmatch(ma->match, str, (ma->flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0);
1472     case SEARCH_REGEX:
1473       return !regexec((const regex_t *)ma->matchdata, str, 0, NULL, 0);
1474     default:
1475       return 0;
1476     }
1477 }
1478 
1479 /* check if the matcher can match the provides basename */
1480 
1481 int
datamatcher_checkbasename(Datamatcher * ma,const char * basename)1482 datamatcher_checkbasename(Datamatcher *ma, const char *basename)
1483 {
1484   int l;
1485   const char *match = ma->matchdata;
1486   if (!match)
1487     return 1;
1488   switch (ma->flags & SEARCH_STRINGMASK)
1489     {
1490     case SEARCH_STRING:
1491       break;
1492     case SEARCH_STRINGEND:
1493       if (match != ma->match)
1494 	break;		/* had slash, do exact match on basename */
1495       /* FALLTHROUGH */
1496     case SEARCH_GLOB:
1497       /* check if the basename ends with match */
1498       l = strlen(basename) - strlen(match);
1499       if (l < 0)
1500 	return 0;
1501       basename += l;
1502       break;
1503     default:
1504       return 1;	/* maybe matches */
1505     }
1506   if ((ma->flags & SEARCH_NOCASE) != 0)
1507     return !strcasecmp(match, basename);
1508   else
1509     return !strcmp(match, basename);
1510 }
1511 
1512 enum {
1513   di_bye,
1514 
1515   di_enterrepo,
1516   di_entersolvable,
1517   di_enterrepodata,
1518   di_enterschema,
1519   di_enterkey,
1520 
1521   di_nextattr,
1522   di_nextkey,
1523   di_nextrepodata,
1524   di_nextsolvable,
1525   di_nextrepo,
1526 
1527   di_enterarray,
1528   di_nextarrayelement,
1529 
1530   di_entersub,
1531   di_leavesub,
1532 
1533   di_nextsolvablekey,
1534   di_entersolvablekey,
1535   di_nextsolvableattr
1536 };
1537 
1538 /* see dataiterator.h for documentation */
1539 int
dataiterator_init(Dataiterator * di,Pool * pool,Repo * repo,Id p,Id keyname,const char * match,int flags)1540 dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags)
1541 {
1542   memset(di, 0, sizeof(*di));
1543   di->pool = pool;
1544   di->flags = flags & ~SEARCH_THISSOLVID;
1545   if (!pool || (repo && repo->pool != pool))
1546     {
1547       di->state = di_bye;
1548       return -1;
1549     }
1550   if (match)
1551     {
1552       int error;
1553       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1554 	{
1555 	  di->state = di_bye;
1556 	  return error;
1557 	}
1558     }
1559   di->keyname = keyname;
1560   di->keynames[0] = keyname;
1561   dataiterator_set_search(di, repo, p);
1562   return 0;
1563 }
1564 
1565 void
dataiterator_init_clone(Dataiterator * di,Dataiterator * from)1566 dataiterator_init_clone(Dataiterator *di, Dataiterator *from)
1567 {
1568   *di = *from;
1569   if (di->dupstr)
1570     {
1571       if (di->dupstr == di->kv.str)
1572         di->dupstr = solv_memdup(di->dupstr, di->dupstrn);
1573       else
1574 	{
1575 	  di->dupstr = 0;
1576 	  di->dupstrn = 0;
1577 	}
1578     }
1579   memset(&di->matcher, 0, sizeof(di->matcher));
1580   if (from->matcher.match)
1581     datamatcher_init(&di->matcher, from->matcher.match, from->matcher.flags);
1582   if (di->nparents)
1583     {
1584       /* fix pointers */
1585       int i;
1586       for (i = 1; i < di->nparents; i++)
1587 	di->parents[i].kv.parent = &di->parents[i - 1].kv;
1588       di->kv.parent = &di->parents[di->nparents - 1].kv;
1589     }
1590   if (di->oldkeyskip)
1591     di->oldkeyskip = solv_memdup2(di->oldkeyskip, 3 + di->oldkeyskip[0], sizeof(Id));
1592   if (di->keyskip)
1593     di->keyskip = di->oldkeyskip;
1594 }
1595 
1596 int
dataiterator_set_match(Dataiterator * di,const char * match,int flags)1597 dataiterator_set_match(Dataiterator *di, const char *match, int flags)
1598 {
1599   di->flags = (flags & ~SEARCH_THISSOLVID) | (di->flags & SEARCH_THISSOLVID);
1600   datamatcher_free(&di->matcher);
1601   memset(&di->matcher, 0, sizeof(di->matcher));
1602   if (match)
1603     {
1604       int error;
1605       if ((error = datamatcher_init(&di->matcher, match, flags)) != 0)
1606 	{
1607 	  di->state = di_bye;
1608 	  return error;
1609 	}
1610     }
1611   return 0;
1612 }
1613 
1614 void
dataiterator_set_search(Dataiterator * di,Repo * repo,Id p)1615 dataiterator_set_search(Dataiterator *di, Repo *repo, Id p)
1616 {
1617   di->repo = repo;
1618   di->repoid = 0;
1619   di->flags &= ~SEARCH_THISSOLVID;
1620   di->nparents = 0;
1621   di->rootlevel = 0;
1622   di->repodataid = 1;
1623   if (!di->pool->urepos)
1624     {
1625       di->state = di_bye;
1626       return;
1627     }
1628   if (!repo)
1629     {
1630       di->repoid = 1;
1631       di->repo = di->pool->repos[di->repoid];
1632     }
1633   di->state = di_enterrepo;
1634   if (p)
1635     dataiterator_jump_to_solvid(di, p);
1636 }
1637 
1638 void
dataiterator_set_keyname(Dataiterator * di,Id keyname)1639 dataiterator_set_keyname(Dataiterator *di, Id keyname)
1640 {
1641   di->nkeynames = 0;
1642   di->keyname = keyname;
1643   di->keynames[0] = keyname;
1644 }
1645 
1646 void
dataiterator_prepend_keyname(Dataiterator * di,Id keyname)1647 dataiterator_prepend_keyname(Dataiterator *di, Id keyname)
1648 {
1649   int i;
1650 
1651   if (di->nkeynames >= sizeof(di->keynames)/sizeof(*di->keynames) - 2)
1652     {
1653       di->state = di_bye;	/* sorry */
1654       return;
1655     }
1656   for (i = di->nkeynames + 1; i > 0; i--)
1657     di->keynames[i] = di->keynames[i - 1];
1658   di->keynames[0] = di->keyname = keyname;
1659   di->nkeynames++;
1660 }
1661 
1662 void
dataiterator_free(Dataiterator * di)1663 dataiterator_free(Dataiterator *di)
1664 {
1665   if (di->matcher.match)
1666     datamatcher_free(&di->matcher);
1667   if (di->dupstr)
1668     solv_free(di->dupstr);
1669   if (di->oldkeyskip)
1670     solv_free(di->oldkeyskip);
1671 }
1672 
1673 static unsigned char *
dataiterator_find_keyname(Dataiterator * di,Id keyname)1674 dataiterator_find_keyname(Dataiterator *di, Id keyname)
1675 {
1676   Id *keyp;
1677   Repokey *keys = di->data->keys, *key;
1678   unsigned char *dp;
1679 
1680   for (keyp = di->keyp; *keyp; keyp++)
1681     if (keys[*keyp].name == keyname)
1682       break;
1683   if (!*keyp)
1684     return 0;
1685   key = keys + *keyp;
1686   if (key->type == REPOKEY_TYPE_DELETED)
1687     return 0;
1688   if (key->storage != KEY_STORAGE_INCORE && key->storage != KEY_STORAGE_VERTICAL_OFFSET)
1689     return 0;		/* get_data will not work, no need to forward */
1690   dp = forward_to_key(di->data, *keyp, di->keyp, di->dp);
1691   if (!dp)
1692     return 0;
1693   di->keyp = keyp;
1694   return dp;
1695 }
1696 
1697 int
dataiterator_step(Dataiterator * di)1698 dataiterator_step(Dataiterator *di)
1699 {
1700   Id schema;
1701 
1702   if (di->state == di_nextattr && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET && di->vert_ddp && di->vert_storestate != di->data->storestate)
1703     {
1704       unsigned int ddpoff = di->ddp - di->vert_ddp;
1705       di->vert_off += ddpoff;
1706       di->vert_len -= ddpoff;
1707       di->ddp = di->vert_ddp = get_vertical_data(di->data, di->key, di->vert_off, di->vert_len);
1708       di->vert_storestate = di->data->storestate;
1709       if (!di->ddp)
1710 	di->state = di_nextkey;
1711     }
1712   for (;;)
1713     {
1714       switch (di->state)
1715 	{
1716 	case di_enterrepo: di_enterrepo:
1717 	  if (!di->repo || (di->repo->disabled && !(di->flags & SEARCH_DISABLED_REPOS)))
1718 	    goto di_nextrepo;
1719 	  if (!(di->flags & SEARCH_THISSOLVID))
1720 	    {
1721 	      di->solvid = di->repo->start - 1;	/* reset solvid iterator */
1722 	      goto di_nextsolvable;
1723 	    }
1724 	  /* FALLTHROUGH */
1725 
1726 	case di_entersolvable: di_entersolvable:
1727 	  if (!di->repodataid)
1728 	    goto di_enterrepodata;	/* POS case, repodata is set */
1729 	  if (di->solvid > 0 && !(di->flags & SEARCH_NO_STORAGE_SOLVABLE) && (!di->keyname || (di->keyname >= SOLVABLE_NAME && di->keyname <= RPM_RPMDBID)) && di->nparents - di->rootlevel == di->nkeynames)
1730 	    {
1731 	      extern Repokey repo_solvablekeys[RPM_RPMDBID - SOLVABLE_NAME + 1];
1732 	      di->key = repo_solvablekeys + (di->keyname ? di->keyname - SOLVABLE_NAME : 0);
1733 	      di->data = 0;
1734 	      goto di_entersolvablekey;
1735 	    }
1736 
1737 	  if (di->keyname)
1738 	    {
1739 	      di->data = di->keyname == SOLVABLE_FILELIST ? repo_lookup_filelist_repodata(di->repo, di->solvid, &di->matcher) : repo_lookup_repodata_opt(di->repo, di->solvid, di->keyname);
1740 	      if (!di->data)
1741 		goto di_nextsolvable;
1742 	      di->repodataid = di->data - di->repo->repodata;
1743 	      di->keyskip = 0;
1744 	      goto di_enterrepodata;
1745 	    }
1746 	di_leavesolvablekey:
1747 	  di->repodataid = 1;	/* reset repodata iterator */
1748 	  di->keyskip = repo_create_keyskip(di->repo, di->solvid, &di->oldkeyskip);
1749 	  /* FALLTHROUGH */
1750 
1751 	case di_enterrepodata: di_enterrepodata:
1752 	  if (di->repodataid)
1753 	    {
1754 	      if (di->repodataid >= di->repo->nrepodata)
1755 		goto di_nextsolvable;
1756 	      di->data = di->repo->repodata + di->repodataid;
1757 	    }
1758 	  if (!maybe_load_repodata(di->data, di->keyname))
1759 	    goto di_nextrepodata;
1760 	  di->dp = solvid2data(di->data, di->solvid, &schema);
1761 	  if (!di->dp)
1762 	    goto di_nextrepodata;
1763 	  if (di->solvid == SOLVID_POS)
1764 	    di->solvid = di->pool->pos.solvid;
1765 	  /* reset key iterator */
1766 	  di->keyp = di->data->schemadata + di->data->schemata[schema];
1767 	  /* FALLTHROUGH */
1768 
1769 	case di_enterschema: di_enterschema:
1770 	  if (di->keyname)
1771 	    di->dp = dataiterator_find_keyname(di, di->keyname);
1772 	  if (!di->dp || !*di->keyp)
1773 	    {
1774 	      if (di->kv.parent)
1775 		goto di_leavesub;
1776 	      goto di_nextrepodata;
1777 	    }
1778 	  /* FALLTHROUGH */
1779 
1780 	case di_enterkey: di_enterkey:
1781 	  di->kv.entry = -1;
1782 	  di->key = di->data->keys + *di->keyp;
1783 	  if (!di->dp)
1784 	    goto di_nextkey;
1785 	  /* this is get_data() modified to store vert_ data */
1786 	  if (di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
1787 	    {
1788 	      Id off, len;
1789 	      di->dp = data_read_id(di->dp, &off);
1790 	      di->dp = data_read_id(di->dp, &len);
1791 	      di->vert_ddp = di->ddp = get_vertical_data(di->data, di->key, off, len);
1792 	      di->vert_off = off;
1793 	      di->vert_len = len;
1794 	      di->vert_storestate = di->data->storestate;
1795 	    }
1796 	  else if (di->key->storage == KEY_STORAGE_INCORE)
1797 	    {
1798 	      di->ddp = di->dp;		/* start of data */
1799 	      if (di->keyp[1] && (!di->keyname || (di->flags & SEARCH_SUB) != 0))
1800 		di->dp = data_skip_key(di->data, di->dp, di->key);	/* advance to next key */
1801 	    }
1802 	  else
1803 	    di->ddp = 0;
1804 	  if (!di->ddp)
1805 	    goto di_nextkey;
1806 	  if (di->keyskip && (di->key->name >= di->keyskip[0] || di->keyskip[3 + di->key->name] != di->keyskip[1] + di->data->repodataid))
1807 	    goto di_nextkey;
1808           if (di->key->type == REPOKEY_TYPE_DELETED && !(di->flags & SEARCH_KEEP_TYPE_DELETED))
1809 	    goto di_nextkey;
1810 	  if (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY)
1811 	    goto di_enterarray;
1812 	  if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1813 	    goto di_nextkey;
1814 	  /* FALLTHROUGH */
1815 
1816 	case di_nextattr:
1817           di->kv.entry++;
1818 	  di->ddp = data_fetch(di->ddp, &di->kv, di->key);
1819 	  di->state = di->kv.eof ? di_nextkey : di_nextattr;
1820 	  break;
1821 
1822 	case di_nextkey: di_nextkey:
1823 	  if (!di->keyname && *++di->keyp)
1824 	    goto di_enterkey;
1825 	  if (di->kv.parent)
1826 	    goto di_leavesub;
1827 	  /* FALLTHROUGH */
1828 
1829 	case di_nextrepodata: di_nextrepodata:
1830 	  if (!di->keyname && di->repodataid && ++di->repodataid < di->repo->nrepodata)
1831 	      goto di_enterrepodata;
1832 	  /* FALLTHROUGH */
1833 
1834 	case di_nextsolvable: di_nextsolvable:
1835 	  if (!(di->flags & SEARCH_THISSOLVID))
1836 	    {
1837 	      if (di->solvid < 0)
1838 		di->solvid = di->repo->start;
1839 	      else
1840 	        di->solvid++;
1841 	      for (; di->solvid < di->repo->end; di->solvid++)
1842 		{
1843 		  if (di->pool->solvables[di->solvid].repo == di->repo)
1844 		    goto di_entersolvable;
1845 		}
1846 	    }
1847 	  /* FALLTHROUGH */
1848 
1849 	case di_nextrepo: di_nextrepo:
1850 	  if (di->repoid > 0)
1851 	    {
1852 	      di->repoid++;
1853 	      di->repodataid = 1;
1854 	      if (di->repoid < di->pool->nrepos)
1855 		{
1856 		  di->repo = di->pool->repos[di->repoid];
1857 	          goto di_enterrepo;
1858 		}
1859 	    }
1860 	/* FALLTHROUGH */
1861 
1862 	case di_bye: di_bye:
1863 	  di->state = di_bye;
1864 	  return 0;
1865 
1866 	case di_enterarray: di_enterarray:
1867 	  if (di->key->name == REPOSITORY_SOLVABLES)
1868 	    goto di_nextkey;
1869 	  di->ddp = data_read_id(di->ddp, (Id *)&di->kv.num);
1870 	  di->kv.eof = 0;
1871 	  di->kv.entry = -1;
1872 	  /* FALLTHROUGH */
1873 
1874 	case di_nextarrayelement: di_nextarrayelement:
1875 	  di->kv.entry++;
1876 	  if (di->kv.entry)
1877 	    di->ddp = data_skip_schema(di->data, di->ddp, di->kv.id);
1878 	  if (di->kv.entry == di->kv.num)
1879 	    {
1880 	      if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1881 		goto di_nextkey;
1882 	      if (!(di->flags & SEARCH_ARRAYSENTINEL))
1883 		goto di_nextkey;
1884 	      di->kv.str = (char *)di->ddp;
1885 	      di->kv.eof = 2;
1886 	      di->state = di_nextkey;
1887 	      break;
1888 	    }
1889 	  if (di->kv.entry == di->kv.num - 1)
1890 	    di->kv.eof = 1;
1891 	  if (di->key->type == REPOKEY_TYPE_FLEXARRAY || !di->kv.entry)
1892 	    di->ddp = data_read_id(di->ddp, &di->kv.id);
1893 	  di->kv.str = (char *)di->ddp;
1894 	  if (di->nkeynames && di->nparents - di->rootlevel < di->nkeynames)
1895 	    goto di_entersub;
1896 	  if ((di->flags & SEARCH_SUB) != 0)
1897 	    di->state = di_entersub;
1898 	  else
1899 	    di->state = di_nextarrayelement;
1900 	  break;
1901 
1902 	case di_entersub: di_entersub:
1903 	  if (di->nparents == sizeof(di->parents)/sizeof(*di->parents) - 1)
1904 	    goto di_nextarrayelement;	/* sorry, full */
1905 	  di->parents[di->nparents].kv = di->kv;
1906 	  di->parents[di->nparents].dp = di->dp;
1907 	  di->parents[di->nparents].keyp = di->keyp;
1908 	  di->dp = (unsigned char *)di->kv.str;
1909 	  di->keyp = di->data->schemadata + di->data->schemata[di->kv.id];
1910 	  memset(&di->kv, 0, sizeof(di->kv));
1911 	  di->kv.parent = &di->parents[di->nparents].kv;
1912 	  di->nparents++;
1913 	  di->keyname = di->keynames[di->nparents - di->rootlevel];
1914 	  goto di_enterschema;
1915 
1916 	case di_leavesub: di_leavesub:
1917 	  if (di->nparents - 1 < di->rootlevel)
1918 	    goto di_bye;
1919 	  di->nparents--;
1920 	  di->dp = di->parents[di->nparents].dp;
1921 	  di->kv = di->parents[di->nparents].kv;
1922 	  di->keyp = di->parents[di->nparents].keyp;
1923 	  di->key = di->data->keys + *di->keyp;
1924 	  di->ddp = (unsigned char *)di->kv.str;
1925 	  di->keyname = di->keynames[di->nparents - di->rootlevel];
1926 	  goto di_nextarrayelement;
1927 
1928         /* special solvable attr handling follows */
1929 
1930 	case di_nextsolvablekey: di_nextsolvablekey:
1931 	  if (di->keyname)
1932 	    goto di_nextsolvable;
1933 	  if (di->key->name == RPM_RPMDBID)	/* reached end of list? */
1934 	    goto di_leavesolvablekey;
1935 	  di->key++;
1936 	  /* FALLTHROUGH */
1937 
1938 	case di_entersolvablekey: di_entersolvablekey:
1939 	  di->idp = solvabledata_fetch(di->pool->solvables + di->solvid, &di->kv, di->key->name);
1940 	  if (!di->idp || !*di->idp)
1941 	    goto di_nextsolvablekey;
1942 	  if (di->kv.eof)
1943 	    {
1944 	      /* not an array */
1945 	      di->kv.id = *di->idp;
1946 	      di->kv.num = *di->idp;	/* for rpmdbid */
1947 	      di->kv.num2 = 0;		/* for rpmdbid */
1948 	      di->kv.entry = 0;
1949 	      di->state = di_nextsolvablekey;
1950 	      break;
1951 	    }
1952 	  di->kv.entry = -1;
1953 	  /* FALLTHROUGH */
1954 
1955 	case di_nextsolvableattr:
1956 	  di->state = di_nextsolvableattr;
1957 	  di->kv.id = *di->idp++;
1958 	  di->kv.entry++;
1959 	  if (!*di->idp)
1960 	    {
1961 	      di->kv.eof = 1;
1962 	      di->state = di_nextsolvablekey;
1963 	    }
1964 	  break;
1965 
1966 	}
1967 
1968       /* we have a potential match */
1969       if (di->matcher.match)
1970 	{
1971 	  const char *str;
1972 	  /* simple pre-check so that we don't need to stringify */
1973 	  if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->matcher.flags & SEARCH_FILES) != 0)
1974 	    if (!datamatcher_checkbasename(&di->matcher, di->kv.str))
1975 	      continue;
1976 	  /* now stringify so that we can do the matching */
1977 	  if (!(str = repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags)))
1978 	    {
1979 	      if (di->keyname && (di->key->type == REPOKEY_TYPE_FIXARRAY || di->key->type == REPOKEY_TYPE_FLEXARRAY))
1980 		return 1;
1981 	      continue;
1982 	    }
1983 	  if (!datamatcher_match(&di->matcher, str))
1984 	    continue;
1985 	}
1986       else
1987 	{
1988 	  /* stringify filelist if requested */
1989 	  if (di->keyname == SOLVABLE_FILELIST && di->key->type == REPOKEY_TYPE_DIRSTRARRAY && (di->flags & SEARCH_FILES) != 0)
1990 	    repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags);
1991 	}
1992       /* found something! */
1993       return 1;
1994     }
1995 }
1996 
1997 void
dataiterator_entersub(Dataiterator * di)1998 dataiterator_entersub(Dataiterator *di)
1999 {
2000   if (di->state == di_nextarrayelement)
2001     di->state = di_entersub;
2002 }
2003 
2004 void
dataiterator_setpos(Dataiterator * di)2005 dataiterator_setpos(Dataiterator *di)
2006 {
2007   if (di->kv.eof == 2)
2008     {
2009       pool_clear_pos(di->pool);
2010       return;
2011     }
2012   di->pool->pos.solvid = di->solvid;
2013   di->pool->pos.repo = di->repo;
2014   di->pool->pos.repodataid = di->data - di->repo->repodata;
2015   di->pool->pos.schema = di->kv.id;
2016   di->pool->pos.dp = (unsigned char *)di->kv.str - di->data->incoredata;
2017 }
2018 
2019 void
dataiterator_setpos_parent(Dataiterator * di)2020 dataiterator_setpos_parent(Dataiterator *di)
2021 {
2022   if (!di->kv.parent || di->kv.parent->eof == 2)
2023     {
2024       pool_clear_pos(di->pool);
2025       return;
2026     }
2027   di->pool->pos.solvid = di->solvid;
2028   di->pool->pos.repo = di->repo;
2029   di->pool->pos.repodataid = di->data - di->repo->repodata;
2030   di->pool->pos.schema = di->kv.parent->id;
2031   di->pool->pos.dp = (unsigned char *)di->kv.parent->str - di->data->incoredata;
2032 }
2033 
2034 /* clones just the position, not the search keys/matcher */
2035 void
dataiterator_clonepos(Dataiterator * di,Dataiterator * from)2036 dataiterator_clonepos(Dataiterator *di, Dataiterator *from)
2037 {
2038   di->state = from->state;
2039   di->flags &= ~SEARCH_THISSOLVID;
2040   di->flags |= (from->flags & SEARCH_THISSOLVID);
2041   di->repo = from->repo;
2042   di->data = from->data;
2043   di->dp = from->dp;
2044   di->ddp = from->ddp;
2045   di->idp = from->idp;
2046   di->keyp = from->keyp;
2047   di->key = from->key;
2048   di->kv = from->kv;
2049   di->repodataid = from->repodataid;
2050   di->solvid = from->solvid;
2051   di->repoid = from->repoid;
2052   di->rootlevel = from->rootlevel;
2053   memcpy(di->parents, from->parents, sizeof(from->parents));
2054   di->nparents = from->nparents;
2055   if (di->nparents)
2056     {
2057       int i;
2058       for (i = 1; i < di->nparents; i++)
2059 	di->parents[i].kv.parent = &di->parents[i - 1].kv;
2060       di->kv.parent = &di->parents[di->nparents - 1].kv;
2061     }
2062   di->dupstr = 0;
2063   di->dupstrn = 0;
2064   if (from->dupstr && from->dupstr == from->kv.str)
2065     {
2066       di->dupstrn = from->dupstrn;
2067       di->dupstr = solv_memdup(from->dupstr, from->dupstrn);
2068     }
2069 }
2070 
2071 void
dataiterator_seek(Dataiterator * di,int whence)2072 dataiterator_seek(Dataiterator *di, int whence)
2073 {
2074   if ((whence & DI_SEEK_STAY) != 0)
2075     di->rootlevel = di->nparents;
2076   switch (whence & ~DI_SEEK_STAY)
2077     {
2078     case DI_SEEK_CHILD:
2079       if (di->state != di_nextarrayelement)
2080 	break;
2081       if ((whence & DI_SEEK_STAY) != 0)
2082 	di->rootlevel = di->nparents + 1;	/* XXX: dangerous! */
2083       di->state = di_entersub;
2084       break;
2085     case DI_SEEK_PARENT:
2086       if (!di->nparents)
2087 	{
2088 	  di->state = di_bye;
2089 	  break;
2090 	}
2091       di->nparents--;
2092       if (di->rootlevel > di->nparents)
2093 	di->rootlevel = di->nparents;
2094       di->dp = di->parents[di->nparents].dp;
2095       di->kv = di->parents[di->nparents].kv;
2096       di->keyp = di->parents[di->nparents].keyp;
2097       di->key = di->data->keys + *di->keyp;
2098       di->ddp = (unsigned char *)di->kv.str;
2099       di->keyname = di->keynames[di->nparents - di->rootlevel];
2100       di->state = di_nextarrayelement;
2101       break;
2102     case DI_SEEK_REWIND:
2103       if (!di->nparents)
2104 	{
2105 	  di->state = di_bye;
2106 	  break;
2107 	}
2108       di->dp = (unsigned char *)di->kv.parent->str;
2109       di->keyp = di->data->schemadata + di->data->schemata[di->kv.parent->id];
2110       di->state = di_enterschema;
2111       break;
2112     default:
2113       break;
2114     }
2115 }
2116 
2117 void
dataiterator_skip_attribute(Dataiterator * di)2118 dataiterator_skip_attribute(Dataiterator *di)
2119 {
2120   if (di->state == di_nextsolvableattr)
2121     di->state = di_nextsolvablekey;
2122   else
2123     di->state = di_nextkey;
2124 }
2125 
2126 void
dataiterator_skip_solvable(Dataiterator * di)2127 dataiterator_skip_solvable(Dataiterator *di)
2128 {
2129   di->nparents = 0;
2130   di->kv.parent = 0;
2131   di->rootlevel = 0;
2132   di->keyname = di->keynames[0];
2133   di->state = di_nextsolvable;
2134 }
2135 
2136 void
dataiterator_skip_repo(Dataiterator * di)2137 dataiterator_skip_repo(Dataiterator *di)
2138 {
2139   di->nparents = 0;
2140   di->kv.parent = 0;
2141   di->rootlevel = 0;
2142   di->keyname = di->keynames[0];
2143   di->state = di_nextrepo;
2144 }
2145 
2146 void
dataiterator_jump_to_solvid(Dataiterator * di,Id solvid)2147 dataiterator_jump_to_solvid(Dataiterator *di, Id solvid)
2148 {
2149   di->nparents = 0;
2150   di->kv.parent = 0;
2151   di->rootlevel = 0;
2152   di->keyname = di->keynames[0];
2153   if (solvid == SOLVID_POS)
2154     {
2155       di->repo = di->pool->pos.repo;
2156       if (!di->repo)
2157 	{
2158 	  di->state = di_bye;
2159 	  return;
2160 	}
2161       di->repoid = 0;
2162       if (!di->pool->pos.repodataid && di->pool->pos.solvid == SOLVID_META) {
2163 	solvid = SOLVID_META;		/* META pos hack */
2164       } else {
2165         di->data = di->repo->repodata + di->pool->pos.repodataid;
2166         di->repodataid = 0;
2167       }
2168     }
2169   else if (solvid > 0)
2170     {
2171       di->repo = di->pool->solvables[solvid].repo;
2172       di->repoid = 0;
2173     }
2174   if (di->repoid > 0)
2175     {
2176       if (!di->pool->urepos)
2177 	{
2178 	  di->state = di_bye;
2179 	  return;
2180 	}
2181       di->repoid = 1;
2182       di->repo = di->pool->repos[di->repoid];
2183     }
2184   if (solvid != SOLVID_POS)
2185     di->repodataid = 1;
2186   di->solvid = solvid;
2187   if (solvid)
2188     di->flags |= SEARCH_THISSOLVID;
2189   di->state = di_enterrepo;
2190 }
2191 
2192 void
dataiterator_jump_to_repo(Dataiterator * di,Repo * repo)2193 dataiterator_jump_to_repo(Dataiterator *di, Repo *repo)
2194 {
2195   di->nparents = 0;
2196   di->kv.parent = 0;
2197   di->rootlevel = 0;
2198   di->repo = repo;
2199   di->repoid = 0;	/* 0 means stay at repo */
2200   di->repodataid = 1;
2201   di->solvid = 0;
2202   di->flags &= ~SEARCH_THISSOLVID;
2203   di->state = di_enterrepo;
2204 }
2205 
2206 int
dataiterator_match(Dataiterator * di,Datamatcher * ma)2207 dataiterator_match(Dataiterator *di, Datamatcher *ma)
2208 {
2209   const char *str;
2210   if (!(str = repodata_stringify(di->pool, di->data, di->key, &di->kv, di->flags)))
2211     return 0;
2212   return ma ? datamatcher_match(ma, str) : 1;
2213 }
2214 
2215 void
dataiterator_strdup(Dataiterator * di)2216 dataiterator_strdup(Dataiterator *di)
2217 {
2218   int l = -1;
2219 
2220   if (!di->kv.str || di->kv.str == di->dupstr)
2221     return;
2222   switch (di->key->type)
2223     {
2224     case_CHKSUM_TYPES:
2225     case REPOKEY_TYPE_DIRSTRARRAY:
2226       if (di->kv.num)	/* was it stringified into tmp space? */
2227         l = strlen(di->kv.str) + 1;
2228       break;
2229     default:
2230       break;
2231     }
2232   if (l < 0 && di->key->storage == KEY_STORAGE_VERTICAL_OFFSET)
2233     {
2234       switch (di->key->type)
2235 	{
2236 	case REPOKEY_TYPE_STR:
2237 	case REPOKEY_TYPE_DIRSTRARRAY:
2238 	  l = strlen(di->kv.str) + 1;
2239 	  break;
2240 	case_CHKSUM_TYPES:
2241 	  l = solv_chksum_len(di->key->type);
2242 	  break;
2243 	case REPOKEY_TYPE_BINARY:
2244 	  l = di->kv.num;
2245 	  break;
2246 	}
2247     }
2248   if (l >= 0)
2249     {
2250       if (!di->dupstrn || di->dupstrn < l)
2251 	{
2252 	  di->dupstrn = l + 16;
2253 	  di->dupstr = solv_realloc(di->dupstr, di->dupstrn);
2254 	}
2255       if (l)
2256         memcpy(di->dupstr, di->kv.str, l);
2257       di->kv.str = di->dupstr;
2258     }
2259 }
2260 
2261 /************************************************************************
2262  * data modify functions
2263  */
2264 
2265 /* extend repodata so that it includes solvables p */
2266 void
repodata_extend(Repodata * data,Id p)2267 repodata_extend(Repodata *data, Id p)
2268 {
2269   if (data->start == data->end)
2270     data->start = data->end = p;
2271   if (p >= data->end)
2272     {
2273       int old = data->end - data->start;
2274       int new = p - data->end + 1;
2275       if (data->attrs)
2276 	{
2277 	  data->attrs = solv_extend(data->attrs, old, new, sizeof(Id *), REPODATA_BLOCK);
2278 	  memset(data->attrs + old, 0, new * sizeof(Id *));
2279 	}
2280       data->incoreoffset = solv_extend(data->incoreoffset, old, new, sizeof(Id), REPODATA_BLOCK);
2281       memset(data->incoreoffset + old, 0, new * sizeof(Id));
2282       data->end = p + 1;
2283     }
2284   if (p < data->start)
2285     {
2286       int old = data->end - data->start;
2287       int new = data->start - p;
2288       if (data->attrs)
2289 	{
2290 	  data->attrs = solv_extend_resize(data->attrs, old + new, sizeof(Id *), REPODATA_BLOCK);
2291 	  memmove(data->attrs + new, data->attrs, old * sizeof(Id *));
2292 	  memset(data->attrs, 0, new * sizeof(Id *));
2293 	}
2294       data->incoreoffset = solv_extend_resize(data->incoreoffset, old + new, sizeof(Id), REPODATA_BLOCK);
2295       memmove(data->incoreoffset + new, data->incoreoffset, old * sizeof(Id));
2296       memset(data->incoreoffset, 0, new * sizeof(Id));
2297       data->start = p;
2298     }
2299 }
2300 
2301 /* shrink end of repodata */
2302 void
repodata_shrink(Repodata * data,int end)2303 repodata_shrink(Repodata *data, int end)
2304 {
2305   int i;
2306 
2307   if (data->end <= end)
2308     return;
2309   if (data->start >= end)
2310     {
2311       if (data->attrs)
2312 	{
2313 	  for (i = 0; i < data->end - data->start; i++)
2314 	    solv_free(data->attrs[i]);
2315           data->attrs = solv_free(data->attrs);
2316 	}
2317       data->incoreoffset = solv_free(data->incoreoffset);
2318       data->start = data->end = 0;
2319       return;
2320     }
2321   if (data->attrs)
2322     {
2323       for (i = end; i < data->end; i++)
2324 	solv_free(data->attrs[i - data->start]);
2325       data->attrs = solv_extend_resize(data->attrs, end - data->start, sizeof(Id *), REPODATA_BLOCK);
2326     }
2327   if (data->incoreoffset)
2328     data->incoreoffset = solv_extend_resize(data->incoreoffset, end - data->start, sizeof(Id), REPODATA_BLOCK);
2329   data->end = end;
2330 }
2331 
2332 /* extend repodata so that it includes solvables from start to start + num - 1 */
2333 void
repodata_extend_block(Repodata * data,Id start,Id num)2334 repodata_extend_block(Repodata *data, Id start, Id num)
2335 {
2336   if (!num)
2337     return;
2338   if (!data->incoreoffset)
2339     {
2340       /* this also means that data->attrs is NULL */
2341       data->incoreoffset = solv_calloc_block(num, sizeof(Id), REPODATA_BLOCK);
2342       data->start = start;
2343       data->end = start + num;
2344       return;
2345     }
2346   repodata_extend(data, start);
2347   if (num > 1)
2348     repodata_extend(data, start + num - 1);
2349 }
2350 
2351 /**********************************************************************/
2352 
2353 
2354 #define REPODATA_ATTRS_BLOCK 31
2355 #define REPODATA_ATTRDATA_BLOCK 1023
2356 #define REPODATA_ATTRIDDATA_BLOCK 63
2357 #define REPODATA_ATTRNUM64DATA_BLOCK 15
2358 
2359 
2360 Id
repodata_new_handle(Repodata * data)2361 repodata_new_handle(Repodata *data)
2362 {
2363   if (!data->nxattrs)
2364     {
2365       data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2366       data->nxattrs = 2;	/* -1: SOLVID_META */
2367     }
2368   data->xattrs = solv_extend(data->xattrs, data->nxattrs, 1, sizeof(Id *), REPODATA_BLOCK);
2369   data->xattrs[data->nxattrs] = 0;
2370   return -(data->nxattrs++);
2371 }
2372 
2373 static inline Id **
repodata_get_attrp(Repodata * data,Id handle)2374 repodata_get_attrp(Repodata *data, Id handle)
2375 {
2376   if (handle < 0)
2377     {
2378       if (handle == SOLVID_META && !data->xattrs)
2379 	{
2380 	  data->xattrs = solv_calloc_block(1, sizeof(Id *), REPODATA_BLOCK);
2381           data->nxattrs = 2;
2382 	}
2383       return data->xattrs - handle;
2384     }
2385   if (handle < data->start || handle >= data->end)
2386     repodata_extend(data, handle);
2387   if (!data->attrs)
2388     data->attrs = solv_calloc_block(data->end - data->start, sizeof(Id *), REPODATA_BLOCK);
2389   return data->attrs + (handle - data->start);
2390 }
2391 
2392 static void
repodata_insert_keyid(Repodata * data,Id handle,Id keyid,Id val,int overwrite)2393 repodata_insert_keyid(Repodata *data, Id handle, Id keyid, Id val, int overwrite)
2394 {
2395   Id *pp;
2396   Id *ap, **app;
2397   int i;
2398 
2399   app = repodata_get_attrp(data, handle);
2400   ap = *app;
2401   i = 0;
2402   if (ap)
2403     {
2404       /* Determine equality based on the name only, allows us to change
2405          type (when overwrite is set), and makes TYPE_CONSTANT work.  */
2406       for (pp = ap; *pp; pp += 2)
2407         if (data->keys[*pp].name == data->keys[keyid].name)
2408           break;
2409       if (*pp)
2410         {
2411 	  if (overwrite || data->keys[*pp].type == REPOKEY_TYPE_DELETED)
2412 	    {
2413 	      pp[0] = keyid;
2414               pp[1] = val;
2415 	    }
2416           return;
2417         }
2418       i = pp - ap;
2419     }
2420   ap = solv_extend(ap, i, 3, sizeof(Id), REPODATA_ATTRS_BLOCK);
2421   *app = ap;
2422   pp = ap + i;
2423   *pp++ = keyid;
2424   *pp++ = val;
2425   *pp = 0;
2426 }
2427 
2428 
2429 static void
repodata_set(Repodata * data,Id solvid,Repokey * key,Id val)2430 repodata_set(Repodata *data, Id solvid, Repokey *key, Id val)
2431 {
2432   Id keyid;
2433 
2434   keyid = repodata_key2id(data, key, 1);
2435   repodata_insert_keyid(data, solvid, keyid, val, 1);
2436 }
2437 
2438 void
repodata_set_id(Repodata * data,Id solvid,Id keyname,Id id)2439 repodata_set_id(Repodata *data, Id solvid, Id keyname, Id id)
2440 {
2441   Repokey key;
2442   key.name = keyname;
2443   key.type = REPOKEY_TYPE_ID;
2444   key.size = 0;
2445   key.storage = KEY_STORAGE_INCORE;
2446   repodata_set(data, solvid, &key, id);
2447 }
2448 
2449 void
repodata_set_num(Repodata * data,Id solvid,Id keyname,unsigned long long num)2450 repodata_set_num(Repodata *data, Id solvid, Id keyname, unsigned long long num)
2451 {
2452   Repokey key;
2453   key.name = keyname;
2454   key.type = REPOKEY_TYPE_NUM;
2455   key.size = 0;
2456   key.storage = KEY_STORAGE_INCORE;
2457   if (num >= 0x80000000)
2458     {
2459       data->attrnum64data = solv_extend(data->attrnum64data, data->attrnum64datalen, 1, sizeof(unsigned long long), REPODATA_ATTRNUM64DATA_BLOCK);
2460       data->attrnum64data[data->attrnum64datalen] = num;
2461       num = 0x80000000 | data->attrnum64datalen++;
2462     }
2463   repodata_set(data, solvid, &key, (Id)num);
2464 }
2465 
2466 void
repodata_set_poolstr(Repodata * data,Id solvid,Id keyname,const char * str)2467 repodata_set_poolstr(Repodata *data, Id solvid, Id keyname, const char *str)
2468 {
2469   Repokey key;
2470   Id id;
2471   if (data->localpool)
2472     id = stringpool_str2id(&data->spool, str, 1);
2473   else
2474     id = pool_str2id(data->repo->pool, str, 1);
2475   key.name = keyname;
2476   key.type = REPOKEY_TYPE_ID;
2477   key.size = 0;
2478   key.storage = KEY_STORAGE_INCORE;
2479   repodata_set(data, solvid, &key, id);
2480 }
2481 
2482 void
repodata_set_constant(Repodata * data,Id solvid,Id keyname,unsigned int constant)2483 repodata_set_constant(Repodata *data, Id solvid, Id keyname, unsigned int constant)
2484 {
2485   Repokey key;
2486   key.name = keyname;
2487   key.type = REPOKEY_TYPE_CONSTANT;
2488   key.size = constant;
2489   key.storage = KEY_STORAGE_INCORE;
2490   repodata_set(data, solvid, &key, 0);
2491 }
2492 
2493 void
repodata_set_constantid(Repodata * data,Id solvid,Id keyname,Id id)2494 repodata_set_constantid(Repodata *data, Id solvid, Id keyname, Id id)
2495 {
2496   Repokey key;
2497   key.name = keyname;
2498   key.type = REPOKEY_TYPE_CONSTANTID;
2499   key.size = id;
2500   key.storage = KEY_STORAGE_INCORE;
2501   repodata_set(data, solvid, &key, 0);
2502 }
2503 
2504 void
repodata_set_void(Repodata * data,Id solvid,Id keyname)2505 repodata_set_void(Repodata *data, Id solvid, Id keyname)
2506 {
2507   Repokey key;
2508   key.name = keyname;
2509   key.type = REPOKEY_TYPE_VOID;
2510   key.size = 0;
2511   key.storage = KEY_STORAGE_INCORE;
2512   repodata_set(data, solvid, &key, 0);
2513 }
2514 
2515 void
repodata_set_str(Repodata * data,Id solvid,Id keyname,const char * str)2516 repodata_set_str(Repodata *data, Id solvid, Id keyname, const char *str)
2517 {
2518   Repokey key;
2519   int l;
2520 
2521   l = strlen(str) + 1;
2522   key.name = keyname;
2523   key.type = REPOKEY_TYPE_STR;
2524   key.size = 0;
2525   key.storage = KEY_STORAGE_INCORE;
2526   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2527   memcpy(data->attrdata + data->attrdatalen, str, l);
2528   repodata_set(data, solvid, &key, data->attrdatalen);
2529   data->attrdatalen += l;
2530 }
2531 
2532 void
repodata_set_binary(Repodata * data,Id solvid,Id keyname,void * buf,int len)2533 repodata_set_binary(Repodata *data, Id solvid, Id keyname, void *buf, int len)
2534 {
2535   Repokey key;
2536   unsigned char *dp;
2537 
2538   if (len < 0)
2539     return;
2540   key.name = keyname;
2541   key.type = REPOKEY_TYPE_BINARY;
2542   key.size = 0;
2543   key.storage = KEY_STORAGE_INCORE;
2544   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, len + 5, 1, REPODATA_ATTRDATA_BLOCK);
2545   dp = data->attrdata + data->attrdatalen;
2546   if (len >= (1 << 14))
2547     {
2548       if (len >= (1 << 28))
2549         *dp++ = (len >> 28) | 128;
2550       if (len >= (1 << 21))
2551         *dp++ = (len >> 21) | 128;
2552       *dp++ = (len >> 14) | 128;
2553     }
2554   if (len >= (1 << 7))
2555     *dp++ = (len >> 7) | 128;
2556   *dp++ = len & 127;
2557   if (len)
2558     memcpy(dp, buf, len);
2559   repodata_set(data, solvid, &key, data->attrdatalen);
2560   data->attrdatalen = dp + len - data->attrdata;
2561 }
2562 
2563 /* add an array element consisting of entrysize Ids to the repodata. modifies attriddata
2564  * so that the caller can append entrysize new elements plus the termination zero there */
2565 static void
repodata_add_array(Repodata * data,Id handle,Id keyname,Id keytype,int entrysize)2566 repodata_add_array(Repodata *data, Id handle, Id keyname, Id keytype, int entrysize)
2567 {
2568   int oldsize;
2569   Id *ida, *pp, **ppp;
2570 
2571   /* check if it is the same as last time, this speeds things up a lot */
2572   if (handle == data->lasthandle && data->keys[data->lastkey].name == keyname && data->keys[data->lastkey].type == keytype && data->attriddatalen == data->lastdatalen)
2573     {
2574       /* great! just append the new data */
2575       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2576       data->attriddatalen--;	/* overwrite terminating 0  */
2577       data->lastdatalen += entrysize;
2578       return;
2579     }
2580 
2581   ppp = repodata_get_attrp(data, handle);
2582   pp = *ppp;
2583   if (pp)
2584     {
2585       for (; *pp; pp += 2)
2586         if (data->keys[*pp].name == keyname)
2587           break;
2588     }
2589   if (!pp || !*pp || data->keys[*pp].type != keytype)
2590     {
2591       /* not found. allocate new key */
2592       Repokey key;
2593       Id keyid;
2594       key.name = keyname;
2595       key.type = keytype;
2596       key.size = 0;
2597       key.storage = KEY_STORAGE_INCORE;
2598       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2599       keyid = repodata_key2id(data, &key, 1);
2600       repodata_insert_keyid(data, handle, keyid, data->attriddatalen, 1);
2601       data->lasthandle = handle;
2602       data->lastkey = keyid;
2603       data->lastdatalen = data->attriddatalen + entrysize + 1;
2604       return;
2605     }
2606   oldsize = 0;
2607   for (ida = data->attriddata + pp[1]; *ida; ida += entrysize)
2608     oldsize += entrysize;
2609   if (ida + 1 == data->attriddata + data->attriddatalen)
2610     {
2611       /* this was the last entry, just append it */
2612       data->attriddata = solv_extend(data->attriddata, data->attriddatalen, entrysize, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2613       data->attriddatalen--;	/* overwrite terminating 0  */
2614     }
2615   else
2616     {
2617       /* too bad. move to back. */
2618       data->attriddata = solv_extend(data->attriddata, data->attriddatalen,  oldsize + entrysize + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2619       memcpy(data->attriddata + data->attriddatalen, data->attriddata + pp[1], oldsize * sizeof(Id));
2620       pp[1] = data->attriddatalen;
2621       data->attriddatalen += oldsize;
2622     }
2623   data->lasthandle = handle;
2624   data->lastkey = *pp;
2625   data->lastdatalen = data->attriddatalen + entrysize + 1;
2626 }
2627 
2628 void
repodata_set_bin_checksum(Repodata * data,Id solvid,Id keyname,Id type,const unsigned char * str)2629 repodata_set_bin_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2630 		      const unsigned char *str)
2631 {
2632   Repokey key;
2633   int l;
2634 
2635   if (!(l = solv_chksum_len(type)))
2636     return;
2637   key.name = keyname;
2638   key.type = type;
2639   key.size = 0;
2640   key.storage = KEY_STORAGE_INCORE;
2641   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2642   memcpy(data->attrdata + data->attrdatalen, str, l);
2643   repodata_set(data, solvid, &key, data->attrdatalen);
2644   data->attrdatalen += l;
2645 }
2646 
2647 void
repodata_set_checksum(Repodata * data,Id solvid,Id keyname,Id type,const char * str)2648 repodata_set_checksum(Repodata *data, Id solvid, Id keyname, Id type,
2649 		      const char *str)
2650 {
2651   unsigned char buf[64];
2652   int l;
2653 
2654   if (!(l = solv_chksum_len(type)))
2655     return;
2656   if (l > sizeof(buf) || solv_hex2bin(&str, buf, l) != l)
2657     return;
2658   repodata_set_bin_checksum(data, solvid, keyname, type, buf);
2659 }
2660 
2661 const char *
repodata_chk2str(Repodata * data,Id type,const unsigned char * buf)2662 repodata_chk2str(Repodata *data, Id type, const unsigned char *buf)
2663 {
2664   int l;
2665 
2666   if (!(l = solv_chksum_len(type)))
2667     return "";
2668   return pool_bin2hex(data->repo->pool, buf, l);
2669 }
2670 
2671 /* rpm filenames don't contain the epoch, so strip it */
2672 static inline const char *
evrid2vrstr(Pool * pool,Id evrid)2673 evrid2vrstr(Pool *pool, Id evrid)
2674 {
2675   const char *p, *evr = pool_id2str(pool, evrid);
2676   if (!evr)
2677     return evr;
2678   for (p = evr; *p >= '0' && *p <= '9'; p++)
2679     ;
2680   return p != evr && *p == ':' && p[1] ? p + 1 : evr;
2681 }
2682 
2683 static inline void
repodata_set_poolstrn(Repodata * data,Id solvid,Id keyname,const char * str,int l)2684 repodata_set_poolstrn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2685 {
2686   Id id;
2687   if (data->localpool)
2688     id = stringpool_strn2id(&data->spool, str, l, 1);
2689   else
2690     id = pool_strn2id(data->repo->pool, str, l, 1);
2691   repodata_set_id(data, solvid, keyname, id);
2692 }
2693 
2694 static inline void
repodata_set_strn(Repodata * data,Id solvid,Id keyname,const char * str,int l)2695 repodata_set_strn(Repodata *data, Id solvid, Id keyname, const char *str, int l)
2696 {
2697   if (!str[l])
2698     repodata_set_str(data, solvid, keyname, str);
2699   else
2700     {
2701       char *s = solv_strdup(str);
2702       s[l] = 0;
2703       repodata_set_str(data, solvid, keyname, s);
2704       free(s);
2705     }
2706 }
2707 
2708 void
repodata_set_location(Repodata * data,Id solvid,int medianr,const char * dir,const char * file)2709 repodata_set_location(Repodata *data, Id solvid, int medianr, const char *dir, const char *file)
2710 {
2711   Pool *pool = data->repo->pool;
2712   Solvable *s;
2713   const char *str, *fp;
2714   int l = 0;
2715 
2716   if (medianr)
2717     repodata_set_constant(data, solvid, SOLVABLE_MEDIANR, medianr);
2718   if (!dir)
2719     {
2720       if ((dir = strrchr(file, '/')) != 0)
2721 	{
2722           l = dir - file;
2723 	  dir = file;
2724 	  file = dir + l + 1;
2725 	  if (!l)
2726 	    l++;
2727 	}
2728     }
2729   else
2730     l = strlen(dir);
2731   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2732     {
2733       dir += 2;
2734       l -= 2;
2735     }
2736   if (l == 1 && dir[0] == '.')
2737     l = 0;
2738   s = pool->solvables + solvid;
2739   if (dir && l)
2740     {
2741       str = pool_id2str(pool, s->arch);
2742       if (!strncmp(dir, str, l) && !str[l])
2743 	repodata_set_void(data, solvid, SOLVABLE_MEDIADIR);
2744       else
2745 	repodata_set_strn(data, solvid, SOLVABLE_MEDIADIR, dir, l);
2746     }
2747   fp = file;
2748   str = pool_id2str(pool, s->name);
2749   l = strlen(str);
2750   if ((!l || !strncmp(fp, str, l)) && fp[l] == '-')
2751     {
2752       fp += l + 1;
2753       str = evrid2vrstr(pool, s->evr);
2754       l = strlen(str);
2755       if ((!l || !strncmp(fp, str, l)) && fp[l] == '.')
2756 	{
2757 	  fp += l + 1;
2758 	  str = pool_id2str(pool, s->arch);
2759 	  l = strlen(str);
2760 	  if ((!l || !strncmp(fp, str, l)) && !strcmp(fp + l, ".rpm"))
2761 	    {
2762 	      repodata_set_void(data, solvid, SOLVABLE_MEDIAFILE);
2763 	      return;
2764 	    }
2765 	}
2766     }
2767   repodata_set_str(data, solvid, SOLVABLE_MEDIAFILE, file);
2768 }
2769 
2770 /* XXX: medianr is currently not stored */
2771 void
repodata_set_deltalocation(Repodata * data,Id handle,int medianr,const char * dir,const char * file)2772 repodata_set_deltalocation(Repodata *data, Id handle, int medianr, const char *dir, const char *file)
2773 {
2774   int l = 0;
2775   const char *evr, *suf, *s;
2776 
2777   if (!dir)
2778     {
2779       if ((dir = strrchr(file, '/')) != 0)
2780 	{
2781           l = dir - file;
2782 	  dir = file;
2783 	  file = dir + l + 1;
2784 	  if (!l)
2785 	    l++;
2786 	}
2787     }
2788   else
2789     l = strlen(dir);
2790   if (l >= 2 && dir[0] == '.' && dir[1] == '/' && (l == 2 || dir[2] != '/'))
2791     {
2792       dir += 2;
2793       l -= 2;
2794     }
2795   if (l == 1 && dir[0] == '.')
2796     l = 0;
2797   if (dir && l)
2798     repodata_set_poolstrn(data, handle, DELTA_LOCATION_DIR, dir, l);
2799   evr = strchr(file, '-');
2800   if (evr)
2801     {
2802       for (s = evr - 1; s > file; s--)
2803 	if (*s == '-')
2804 	  {
2805 	    evr = s;
2806 	    break;
2807 	  }
2808     }
2809   suf = strrchr(file, '.');
2810   if (suf)
2811     {
2812       for (s = suf - 1; s > file; s--)
2813 	if (*s == '.')
2814 	  {
2815 	    suf = s;
2816 	    break;
2817 	  }
2818       if (!strcmp(suf, ".delta.rpm") || !strcmp(suf, ".patch.rpm"))
2819 	{
2820 	  /* We accept one more item as suffix.  */
2821 	  for (s = suf - 1; s > file; s--)
2822 	    if (*s == '.')
2823 	      {
2824 		suf = s;
2825 	        break;
2826 	      }
2827 	}
2828     }
2829   if (!evr)
2830     suf = 0;
2831   if (suf && evr && suf < evr)
2832     suf = 0;
2833   repodata_set_poolstrn(data, handle, DELTA_LOCATION_NAME, file, evr ? evr - file : strlen(file));
2834   if (evr)
2835     repodata_set_poolstrn(data, handle, DELTA_LOCATION_EVR, evr + 1, suf ? suf - evr - 1: strlen(evr + 1));
2836   if (suf)
2837     repodata_set_poolstr(data, handle, DELTA_LOCATION_SUFFIX, suf + 1);
2838 }
2839 
2840 void
repodata_set_sourcepkg(Repodata * data,Id solvid,const char * sourcepkg)2841 repodata_set_sourcepkg(Repodata *data, Id solvid, const char *sourcepkg)
2842 {
2843   Pool *pool = data->repo->pool;
2844   Solvable *s = pool->solvables + solvid;
2845   const char *p, *sevr, *sarch, *name, *evr;
2846 
2847   p = strrchr(sourcepkg, '.');
2848   if (!p || strcmp(p, ".rpm") != 0)
2849     {
2850       if (*sourcepkg)
2851         repodata_set_str(data, solvid, SOLVABLE_SOURCENAME, sourcepkg);
2852       return;
2853     }
2854   p--;
2855   while (p > sourcepkg && *p != '.')
2856     p--;
2857   if (*p != '.' || p == sourcepkg)
2858     return;
2859   sarch = p-- + 1;
2860   while (p > sourcepkg && *p != '-')
2861     p--;
2862   if (*p != '-' || p == sourcepkg)
2863     return;
2864   p--;
2865   while (p > sourcepkg && *p != '-')
2866     p--;
2867   if (*p != '-' || p == sourcepkg)
2868     return;
2869   sevr = p + 1;
2870   pool = s->repo->pool;
2871 
2872   name = pool_id2str(pool, s->name);
2873   if (name && !strncmp(sourcepkg, name, sevr - sourcepkg - 1) && name[sevr - sourcepkg - 1] == 0)
2874     repodata_set_void(data, solvid, SOLVABLE_SOURCENAME);
2875   else
2876     repodata_set_id(data, solvid, SOLVABLE_SOURCENAME, pool_strn2id(pool, sourcepkg, sevr - sourcepkg - 1, 1));
2877 
2878   evr = evrid2vrstr(pool, s->evr);
2879   if (evr && !strncmp(sevr, evr, sarch - sevr - 1) && evr[sarch - sevr - 1] == 0)
2880     repodata_set_void(data, solvid, SOLVABLE_SOURCEEVR);
2881   else
2882     repodata_set_id(data, solvid, SOLVABLE_SOURCEEVR, pool_strn2id(pool, sevr, sarch - sevr - 1, 1));
2883 
2884   if (!strcmp(sarch, "src.rpm"))
2885     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_SRC);
2886   else if (!strcmp(sarch, "nosrc.rpm"))
2887     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, ARCH_NOSRC);
2888   else
2889     repodata_set_constantid(data, solvid, SOLVABLE_SOURCEARCH, pool_strn2id(pool, sarch, strlen(sarch) - 4, 1));
2890 }
2891 
2892 void
repodata_set_idarray(Repodata * data,Id solvid,Id keyname,Queue * q)2893 repodata_set_idarray(Repodata *data, Id solvid, Id keyname, Queue *q)
2894 {
2895   Repokey key;
2896   int i;
2897 
2898   key.name = keyname;
2899   key.type = REPOKEY_TYPE_IDARRAY;
2900   key.size = 0;
2901   key.storage = KEY_STORAGE_INCORE;
2902   repodata_set(data, solvid, &key, data->attriddatalen);
2903   data->attriddata = solv_extend(data->attriddata, data->attriddatalen, q->count + 1, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
2904   for (i = 0; i < q->count; i++)
2905     data->attriddata[data->attriddatalen++] = q->elements[i];
2906   data->attriddata[data->attriddatalen++] = 0;
2907 }
2908 
2909 void
repodata_add_dirnumnum(Repodata * data,Id solvid,Id keyname,Id dir,Id num,Id num2)2910 repodata_add_dirnumnum(Repodata *data, Id solvid, Id keyname, Id dir, Id num, Id num2)
2911 {
2912   assert(dir);
2913 #if 0
2914 fprintf(stderr, "repodata_add_dirnumnum %d %d %d %d (%d)\n", solvid, dir, num, num2, data->attriddatalen);
2915 #endif
2916   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRNUMNUMARRAY, 3);
2917   data->attriddata[data->attriddatalen++] = dir;
2918   data->attriddata[data->attriddatalen++] = num;
2919   data->attriddata[data->attriddatalen++] = num2;
2920   data->attriddata[data->attriddatalen++] = 0;
2921 }
2922 
2923 void
repodata_add_dirstr(Repodata * data,Id solvid,Id keyname,Id dir,const char * str)2924 repodata_add_dirstr(Repodata *data, Id solvid, Id keyname, Id dir, const char *str)
2925 {
2926   Id stroff;
2927   int l;
2928 
2929   assert(dir);
2930   l = strlen(str) + 1;
2931   data->attrdata = solv_extend(data->attrdata, data->attrdatalen, l, 1, REPODATA_ATTRDATA_BLOCK);
2932   memcpy(data->attrdata + data->attrdatalen, str, l);
2933   stroff = data->attrdatalen;
2934   data->attrdatalen += l;
2935 
2936 #if 0
2937 fprintf(stderr, "repodata_add_dirstr %d %d %s (%d)\n", solvid, dir, str,  data->attriddatalen);
2938 #endif
2939   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_DIRSTRARRAY, 2);
2940   data->attriddata[data->attriddatalen++] = dir;
2941   data->attriddata[data->attriddatalen++] = stroff;
2942   data->attriddata[data->attriddatalen++] = 0;
2943 }
2944 
2945 void
repodata_add_idarray(Repodata * data,Id solvid,Id keyname,Id id)2946 repodata_add_idarray(Repodata *data, Id solvid, Id keyname, Id id)
2947 {
2948 #if 0
2949 fprintf(stderr, "repodata_add_idarray %d %d (%d)\n", solvid, id, data->attriddatalen);
2950 #endif
2951   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_IDARRAY, 1);
2952   data->attriddata[data->attriddatalen++] = id;
2953   data->attriddata[data->attriddatalen++] = 0;
2954 }
2955 
2956 void
repodata_add_poolstr_array(Repodata * data,Id solvid,Id keyname,const char * str)2957 repodata_add_poolstr_array(Repodata *data, Id solvid, Id keyname,
2958 			   const char *str)
2959 {
2960   Id id;
2961   if (data->localpool)
2962     id = stringpool_str2id(&data->spool, str, 1);
2963   else
2964     id = pool_str2id(data->repo->pool, str, 1);
2965   repodata_add_idarray(data, solvid, keyname, id);
2966 }
2967 
2968 void
repodata_add_fixarray(Repodata * data,Id solvid,Id keyname,Id handle)2969 repodata_add_fixarray(Repodata *data, Id solvid, Id keyname, Id handle)
2970 {
2971   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FIXARRAY, 1);
2972   data->attriddata[data->attriddatalen++] = handle;
2973   data->attriddata[data->attriddatalen++] = 0;
2974 }
2975 
2976 void
repodata_add_flexarray(Repodata * data,Id solvid,Id keyname,Id handle)2977 repodata_add_flexarray(Repodata *data, Id solvid, Id keyname, Id handle)
2978 {
2979   repodata_add_array(data, solvid, keyname, REPOKEY_TYPE_FLEXARRAY, 1);
2980   data->attriddata[data->attriddatalen++] = handle;
2981   data->attriddata[data->attriddatalen++] = 0;
2982 }
2983 
2984 void
repodata_set_kv(Repodata * data,Id solvid,Id keyname,Id keytype,KeyValue * kv)2985 repodata_set_kv(Repodata *data, Id solvid, Id keyname, Id keytype, KeyValue *kv)
2986 {
2987   switch (keytype)
2988     {
2989     case REPOKEY_TYPE_ID:
2990       repodata_set_id(data, solvid, keyname, kv->id);
2991       break;
2992     case REPOKEY_TYPE_CONSTANTID:
2993       repodata_set_constantid(data, solvid, keyname, kv->id);
2994       break;
2995     case REPOKEY_TYPE_IDARRAY:
2996       repodata_add_idarray(data, solvid, keyname, kv->id);
2997       break;
2998     case REPOKEY_TYPE_STR:
2999       repodata_set_str(data, solvid, keyname, kv->str);
3000       break;
3001     case REPOKEY_TYPE_VOID:
3002       repodata_set_void(data, solvid, keyname);
3003       break;
3004     case REPOKEY_TYPE_NUM:
3005       repodata_set_num(data, solvid, keyname, SOLV_KV_NUM64(kv));
3006       break;
3007     case REPOKEY_TYPE_CONSTANT:
3008       repodata_set_constant(data, solvid, keyname, kv->num);
3009       break;
3010     case REPOKEY_TYPE_DIRNUMNUMARRAY:
3011       if (kv->id)
3012         repodata_add_dirnumnum(data, solvid, keyname, kv->id, kv->num, kv->num2);
3013       break;
3014     case REPOKEY_TYPE_DIRSTRARRAY:
3015       repodata_add_dirstr(data, solvid, keyname, kv->id, kv->str);
3016       break;
3017     case_CHKSUM_TYPES:
3018       repodata_set_bin_checksum(data, solvid, keyname, keytype, (const unsigned char *)kv->str);
3019       break;
3020     default:
3021       break;
3022     }
3023 }
3024 
3025 void
repodata_unset_uninternalized(Repodata * data,Id solvid,Id keyname)3026 repodata_unset_uninternalized(Repodata *data, Id solvid, Id keyname)
3027 {
3028   Id *pp, *ap, **app;
3029   app = repodata_get_attrp(data, solvid);
3030   ap = *app;
3031   if (!ap)
3032     return;
3033   if (!keyname)
3034     {
3035       *app = 0;		/* delete all attributes */
3036       return;
3037     }
3038   for (; *ap; ap += 2)
3039     if (data->keys[*ap].name == keyname)
3040       break;
3041   if (!*ap)
3042     return;
3043   pp = ap;
3044   ap += 2;
3045   for (; *ap; ap += 2)
3046     {
3047       if (data->keys[*ap].name == keyname)
3048 	continue;
3049       *pp++ = ap[0];
3050       *pp++ = ap[1];
3051     }
3052   *pp = 0;
3053 }
3054 
3055 void
repodata_unset(Repodata * data,Id solvid,Id keyname)3056 repodata_unset(Repodata *data, Id solvid, Id keyname)
3057 {
3058   Repokey key;
3059   key.name = keyname;
3060   key.type = REPOKEY_TYPE_DELETED;
3061   key.size = 0;
3062   key.storage = KEY_STORAGE_INCORE;
3063   repodata_set(data, solvid, &key, 0);
3064 }
3065 
3066 /* add all (uninternalized) attrs from src to dest */
3067 void
repodata_merge_attrs(Repodata * data,Id dest,Id src)3068 repodata_merge_attrs(Repodata *data, Id dest, Id src)
3069 {
3070   Id *keyp;
3071   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
3072     return;
3073   for (; *keyp; keyp += 2)
3074     repodata_insert_keyid(data, dest, keyp[0], keyp[1], 0);
3075 }
3076 
3077 /* add some (uninternalized) attrs from src to dest */
3078 void
repodata_merge_some_attrs(Repodata * data,Id dest,Id src,Map * keyidmap,int overwrite)3079 repodata_merge_some_attrs(Repodata *data, Id dest, Id src, Map *keyidmap, int overwrite)
3080 {
3081   Id *keyp;
3082   if (dest == src || !data->attrs || !(keyp = data->attrs[src - data->start]))
3083     return;
3084   for (; *keyp; keyp += 2)
3085     if (!keyidmap || MAPTST(keyidmap, keyp[0]))
3086       repodata_insert_keyid(data, dest, keyp[0], keyp[1], overwrite);
3087 }
3088 
3089 /* swap (uninternalized) attrs from src and dest */
3090 void
repodata_swap_attrs(Repodata * data,Id dest,Id src)3091 repodata_swap_attrs(Repodata *data, Id dest, Id src)
3092 {
3093   Id *tmpattrs;
3094   if (!data->attrs || dest == src)
3095     return;
3096   if (dest < data->start || dest >= data->end)
3097     repodata_extend(data, dest);
3098   if (src < data->start || src >= data->end)
3099     repodata_extend(data, src);
3100   tmpattrs = data->attrs[dest - data->start];
3101   data->attrs[dest - data->start] = data->attrs[src - data->start];
3102   data->attrs[src - data->start] = tmpattrs;
3103   if (data->lasthandle == src || data->lasthandle == dest)
3104     data->lasthandle = 0;
3105 }
3106 
3107 
3108 /**********************************************************************/
3109 
3110 /* TODO: unify with repo_write and repo_solv! */
3111 
3112 #define EXTDATA_BLOCK 1023
3113 
3114 struct extdata {
3115   unsigned char *buf;
3116   int len;
3117 };
3118 
3119 static void
data_addid(struct extdata * xd,Id sx)3120 data_addid(struct extdata *xd, Id sx)
3121 {
3122   unsigned int x = (unsigned int)sx;
3123   unsigned char *dp;
3124 
3125   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
3126   dp = xd->buf + xd->len;
3127 
3128   if (x >= (1 << 14))
3129     {
3130       if (x >= (1 << 28))
3131         *dp++ = (x >> 28) | 128;
3132       if (x >= (1 << 21))
3133         *dp++ = (x >> 21) | 128;
3134       *dp++ = (x >> 14) | 128;
3135     }
3136   if (x >= (1 << 7))
3137     *dp++ = (x >> 7) | 128;
3138   *dp++ = x & 127;
3139   xd->len = dp - xd->buf;
3140 }
3141 
3142 static void
data_addid64(struct extdata * xd,unsigned long long x)3143 data_addid64(struct extdata *xd, unsigned long long x)
3144 {
3145   if (x >= 0x100000000)
3146     {
3147       if ((x >> 35) != 0)
3148 	{
3149 	  data_addid(xd, (Id)(x >> 35));
3150 	  xd->buf[xd->len - 1] |= 128;
3151 	}
3152       data_addid(xd, (Id)((unsigned int)x | 0x80000000));
3153       xd->buf[xd->len - 5] = (x >> 28) | 128;
3154     }
3155   else
3156     data_addid(xd, (Id)x);
3157 }
3158 
3159 static void
data_addideof(struct extdata * xd,Id sx,int eof)3160 data_addideof(struct extdata *xd, Id sx, int eof)
3161 {
3162   unsigned int x = (unsigned int)sx;
3163   unsigned char *dp;
3164 
3165   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
3166   dp = xd->buf + xd->len;
3167 
3168   if (x >= (1 << 13))
3169     {
3170       if (x >= (1 << 27))
3171         *dp++ = (x >> 27) | 128;
3172       if (x >= (1 << 20))
3173         *dp++ = (x >> 20) | 128;
3174       *dp++ = (x >> 13) | 128;
3175     }
3176   if (x >= (1 << 6))
3177     *dp++ = (x >> 6) | 128;
3178   *dp++ = eof ? (x & 63) : (x & 63) | 64;
3179   xd->len = dp - xd->buf;
3180 }
3181 
3182 static void
data_addblob(struct extdata * xd,unsigned char * blob,int len)3183 data_addblob(struct extdata *xd, unsigned char *blob, int len)
3184 {
3185   xd->buf = solv_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
3186   memcpy(xd->buf + xd->len, blob, len);
3187   xd->len += len;
3188 }
3189 
3190 /*********************************/
3191 
3192 /* this is to reduct memory usage when internalizing oversized repos */
3193 static void
compact_attrdata(Repodata * data,int entry,int nentry)3194 compact_attrdata(Repodata *data, int entry, int nentry)
3195 {
3196   int i;
3197   unsigned int attrdatastart = data->attrdatalen;
3198   unsigned int attriddatastart = data->attriddatalen;
3199   if (attrdatastart < 1024 * 1024 * 4 && attriddatastart < 1024 * 1024)
3200     return;
3201   for (i = entry; i < nentry; i++)
3202     {
3203       Id v, *attrs = data->attrs[i];
3204       if (!attrs)
3205 	continue;
3206       for (; *attrs; attrs += 2)
3207 	{
3208 	  switch (data->keys[*attrs].type)
3209 	    {
3210 	    case REPOKEY_TYPE_STR:
3211 	    case REPOKEY_TYPE_BINARY:
3212 	    case_CHKSUM_TYPES:
3213 	      if ((unsigned int)attrs[1] < attrdatastart)
3214 		 attrdatastart = attrs[1];
3215 	      break;
3216 	    case REPOKEY_TYPE_DIRSTRARRAY:
3217 	      for (v = attrs[1]; data->attriddata[v] ; v += 2)
3218 		if ((unsigned int)data->attriddata[v + 1] < attrdatastart)
3219 		  attrdatastart = data->attriddata[v + 1];
3220 	      /* FALLTHROUGH */
3221 	    case REPOKEY_TYPE_IDARRAY:
3222 	    case REPOKEY_TYPE_DIRNUMNUMARRAY:
3223 	      if ((unsigned int)attrs[1] < attriddatastart)
3224 		attriddatastart = attrs[1];
3225 	      break;
3226 	    case REPOKEY_TYPE_FIXARRAY:
3227 	    case REPOKEY_TYPE_FLEXARRAY:
3228 	      return;
3229 	    default:
3230 	      break;
3231 	    }
3232 	}
3233     }
3234 #if 0
3235   printf("compact_attrdata %d %d\n", entry, nentry);
3236   printf("attrdatastart: %d\n", attrdatastart);
3237   printf("attriddatastart: %d\n", attriddatastart);
3238 #endif
3239   if (attrdatastart < 1024 * 1024 * 4 && attriddatastart < 1024 * 1024)
3240     return;
3241   for (i = entry; i < nentry; i++)
3242     {
3243       Id v, *attrs = data->attrs[i];
3244       if (!attrs)
3245 	continue;
3246       for (; *attrs; attrs += 2)
3247 	{
3248 	  switch (data->keys[*attrs].type)
3249 	    {
3250 	    case REPOKEY_TYPE_STR:
3251 	    case REPOKEY_TYPE_BINARY:
3252 	    case_CHKSUM_TYPES:
3253 	      attrs[1] -= attrdatastart;
3254 	      break;
3255 	    case REPOKEY_TYPE_DIRSTRARRAY:
3256 	      for (v = attrs[1]; data->attriddata[v] ; v += 2)
3257 		data->attriddata[v + 1] -= attrdatastart;
3258 	      /* FALLTHROUGH */
3259 	    case REPOKEY_TYPE_IDARRAY:
3260 	    case REPOKEY_TYPE_DIRNUMNUMARRAY:
3261 	      attrs[1] -= attriddatastart;
3262 	      break;
3263 	    default:
3264 	      break;
3265 	    }
3266 	}
3267     }
3268   if (attrdatastart)
3269     {
3270       data->attrdatalen -= attrdatastart;
3271       memmove(data->attrdata, data->attrdata + attrdatastart, data->attrdatalen);
3272       data->attrdata = solv_extend_resize(data->attrdata, data->attrdatalen, 1, REPODATA_ATTRDATA_BLOCK);
3273     }
3274   if (attriddatastart)
3275     {
3276       data->attriddatalen -= attriddatastart;
3277       memmove(data->attriddata, data->attriddata + attriddatastart, data->attriddatalen * sizeof(Id));
3278       data->attriddata = solv_extend_resize(data->attriddata, data->attriddatalen, sizeof(Id), REPODATA_ATTRIDDATA_BLOCK);
3279     }
3280 }
3281 
3282 /* internalalize some key into incore/vincore data */
3283 
3284 static void
repodata_serialize_key(Repodata * data,struct extdata * newincore,struct extdata * newvincore,Id * schema,Repokey * key,Id val)3285 repodata_serialize_key(Repodata *data, struct extdata *newincore,
3286 		       struct extdata *newvincore,
3287 		       Id *schema,
3288 		       Repokey *key, Id val)
3289 {
3290   Id *ida;
3291   struct extdata *xd;
3292   unsigned int oldvincorelen = 0;
3293   Id schemaid, *sp;
3294 
3295   xd = newincore;
3296   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3297     {
3298       xd = newvincore;
3299       oldvincorelen = xd->len;
3300     }
3301   switch (key->type)
3302     {
3303     case REPOKEY_TYPE_VOID:
3304     case REPOKEY_TYPE_CONSTANT:
3305     case REPOKEY_TYPE_CONSTANTID:
3306     case REPOKEY_TYPE_DELETED:
3307       break;
3308     case REPOKEY_TYPE_STR:
3309       data_addblob(xd, data->attrdata + val, strlen((char *)(data->attrdata + val)) + 1);
3310       break;
3311     case REPOKEY_TYPE_MD5:
3312       data_addblob(xd, data->attrdata + val, SIZEOF_MD5);
3313       break;
3314     case REPOKEY_TYPE_SHA1:
3315       data_addblob(xd, data->attrdata + val, SIZEOF_SHA1);
3316       break;
3317     case REPOKEY_TYPE_SHA224:
3318       data_addblob(xd, data->attrdata + val, SIZEOF_SHA224);
3319       break;
3320     case REPOKEY_TYPE_SHA256:
3321       data_addblob(xd, data->attrdata + val, SIZEOF_SHA256);
3322       break;
3323     case REPOKEY_TYPE_SHA384:
3324       data_addblob(xd, data->attrdata + val, SIZEOF_SHA384);
3325       break;
3326     case REPOKEY_TYPE_SHA512:
3327       data_addblob(xd, data->attrdata + val, SIZEOF_SHA512);
3328       break;
3329     case REPOKEY_TYPE_NUM:
3330       if (val & 0x80000000)
3331 	{
3332 	  data_addid64(xd, data->attrnum64data[val ^ 0x80000000]);
3333 	  break;
3334 	}
3335       /* FALLTHROUGH */
3336     case REPOKEY_TYPE_ID:
3337     case REPOKEY_TYPE_DIR:
3338       data_addid(xd, val);
3339       break;
3340     case REPOKEY_TYPE_BINARY:
3341       {
3342 	Id len;
3343 	unsigned char *dp = data_read_id(data->attrdata + val, &len);
3344 	dp += (unsigned int)len;
3345 	data_addblob(xd, data->attrdata + val, dp - (data->attrdata + val));
3346       }
3347       break;
3348     case REPOKEY_TYPE_IDARRAY:
3349       for (ida = data->attriddata + val; *ida; ida++)
3350 	data_addideof(xd, ida[0], ida[1] ? 0 : 1);
3351       break;
3352     case REPOKEY_TYPE_DIRNUMNUMARRAY:
3353       for (ida = data->attriddata + val; *ida; ida += 3)
3354 	{
3355 	  data_addid(xd, ida[0]);
3356 	  data_addid(xd, ida[1]);
3357 	  data_addideof(xd, ida[2], ida[3] ? 0 : 1);
3358 	}
3359       break;
3360     case REPOKEY_TYPE_DIRSTRARRAY:
3361       for (ida = data->attriddata + val; *ida; ida += 2)
3362 	{
3363 	  data_addideof(xd, ida[0], ida[2] ? 0 : 1);
3364 	  data_addblob(xd, data->attrdata + ida[1], strlen((char *)(data->attrdata + ida[1])) + 1);
3365 	}
3366       break;
3367     case REPOKEY_TYPE_FIXARRAY:
3368       {
3369 	int num = 0;
3370 	schemaid = 0;
3371 	for (ida = data->attriddata + val; *ida; ida++)
3372 	  {
3373 	    Id *kp;
3374 	    sp = schema;
3375 	    kp = data->xattrs[-*ida];
3376 	    if (!kp)
3377 	      continue;		/* ignore empty elements */
3378 	    num++;
3379 	    for (; *kp; kp += 2)
3380 	      *sp++ = *kp;
3381 	    *sp = 0;
3382 	    if (!schemaid)
3383 	      schemaid = repodata_schema2id(data, schema, 1);
3384 	    else if (schemaid != repodata_schema2id(data, schema, 0))
3385 	      {
3386 	 	pool_debug(data->repo->pool, SOLV_ERROR, "repodata_serialize_key: fixarray substructs with different schemas\n");
3387 		num = 0;
3388 		break;
3389 	      }
3390 	  }
3391 	data_addid(xd, num);
3392 	if (!num)
3393 	  break;
3394 	data_addid(xd, schemaid);
3395 	for (ida = data->attriddata + val; *ida; ida++)
3396 	  {
3397 	    Id *kp = data->xattrs[-*ida];
3398 	    if (!kp)
3399 	      continue;
3400 	    for (; *kp; kp += 2)
3401 	      repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
3402 	  }
3403 	break;
3404       }
3405     case REPOKEY_TYPE_FLEXARRAY:
3406       {
3407 	int num = 0;
3408 	for (ida = data->attriddata + val; *ida; ida++)
3409 	  num++;
3410 	data_addid(xd, num);
3411 	for (ida = data->attriddata + val; *ida; ida++)
3412 	  {
3413 	    Id *kp = data->xattrs[-*ida];
3414 	    if (!kp)
3415 	      {
3416 	        data_addid(xd, 0);	/* XXX */
3417 	        continue;
3418 	      }
3419 	    sp = schema;
3420 	    for (;*kp; kp += 2)
3421 	      *sp++ = *kp;
3422 	    *sp = 0;
3423 	    schemaid = repodata_schema2id(data, schema, 1);
3424 	    data_addid(xd, schemaid);
3425 	    kp = data->xattrs[-*ida];
3426 	    for (;*kp; kp += 2)
3427 	      repodata_serialize_key(data, newincore, newvincore, schema, data->keys + *kp, kp[1]);
3428 	  }
3429 	break;
3430       }
3431     default:
3432       pool_debug(data->repo->pool, SOLV_FATAL, "repodata_serialize_key: don't know how to handle type %d\n", key->type);
3433       exit(1);
3434     }
3435   if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3436     {
3437       /* put offset/len in incore */
3438       data_addid(newincore, data->lastverticaloffset + oldvincorelen);
3439       oldvincorelen = xd->len - oldvincorelen;
3440       data_addid(newincore, oldvincorelen);
3441     }
3442 }
3443 
3444 /* create a circular linked list of all keys that share
3445  * the same keyname */
3446 static Id *
calculate_keylink(Repodata * data)3447 calculate_keylink(Repodata *data)
3448 {
3449   int i, j;
3450   Id *link;
3451   Id maxkeyname = 0, *keytable = 0;
3452   link = solv_calloc(data->nkeys, sizeof(Id));
3453   if (data->nkeys <= 2)
3454     return link;
3455   for (i = 1; i < data->nkeys; i++)
3456     {
3457       Id n = data->keys[i].name;
3458       if (n >= maxkeyname)
3459 	{
3460 	  keytable = solv_realloc2(keytable, n + 128, sizeof(Id));
3461 	  memset(keytable + maxkeyname, 0, (n + 128 - maxkeyname) * sizeof(Id));
3462 	  maxkeyname = n + 128;
3463 	}
3464       j = keytable[n];
3465       if (j)
3466 	link[i] = link[j];
3467       else
3468 	j = i;
3469       link[j] = i;
3470       keytable[n] = i;
3471     }
3472   /* remove links that just point to themselfs */
3473   for (i = 1; i < data->nkeys; i++)
3474     if (link[i] == i)
3475       link[i] = 0;
3476   solv_free(keytable);
3477   return link;
3478 }
3479 
3480 void
repodata_internalize(Repodata * data)3481 repodata_internalize(Repodata *data)
3482 {
3483   Repokey *key, solvkey;
3484   Id entry, nentry;
3485   Id schemaid, keyid, *schema, *sp, oldschemaid, *keyp, *seen;
3486   Offset *oldincoreoffs = 0;
3487   int schemaidx;
3488   unsigned char *dp, *ndp;
3489   int neednewschema;
3490   struct extdata newincore;
3491   struct extdata newvincore;
3492   Id solvkeyid;
3493   Id *keylink;
3494   int haveoldkl;
3495 
3496   if (!data->attrs && !data->xattrs)
3497     return;
3498 
3499 #if 0
3500   printf("repodata_internalize %d\n", data->repodataid);
3501   printf("  attr data: %d K\n", data->attrdatalen / 1024);
3502   printf("  attrid data: %d K\n", data->attriddatalen / (1024 / 4));
3503 #endif
3504   newvincore.buf = data->vincore;
3505   newvincore.len = data->vincorelen;
3506 
3507   /* find the solvables key, create if needed */
3508   memset(&solvkey, 0, sizeof(solvkey));
3509   solvkey.name = REPOSITORY_SOLVABLES;
3510   solvkey.type = REPOKEY_TYPE_FLEXARRAY;
3511   solvkey.size = 0;
3512   solvkey.storage = KEY_STORAGE_INCORE;
3513   solvkeyid = repodata_key2id(data, &solvkey, data->end != data->start ? 1 : 0);
3514 
3515   schema = solv_malloc2(data->nkeys, sizeof(Id));
3516   seen = solv_malloc2(data->nkeys, sizeof(Id));
3517 
3518   /* Merge the data already existing (in data->schemata, ->incoredata and
3519      friends) with the new attributes in data->attrs[].  */
3520   nentry = data->end - data->start;
3521   memset(&newincore, 0, sizeof(newincore));
3522   data_addid(&newincore, 0);	/* start data at offset 1 */
3523 
3524   data->mainschema = 0;
3525   data->mainschemaoffsets = solv_free(data->mainschemaoffsets);
3526 
3527   keylink = calculate_keylink(data);
3528   /* join entry data */
3529   /* we start with the meta data, entry -1 */
3530   for (entry = -1; entry < nentry; entry++)
3531     {
3532       oldschemaid = 0;
3533       dp = data->incoredata;
3534       if (dp)
3535 	{
3536 	  dp += entry >= 0 ? data->incoreoffset[entry] : 1;
3537           dp = data_read_id(dp, &oldschemaid);
3538 	}
3539       memset(seen, 0, data->nkeys * sizeof(Id));
3540 #if 0
3541 fprintf(stderr, "oldschemaid %d\n", oldschemaid);
3542 fprintf(stderr, "schemata %d\n", data->schemata[oldschemaid]);
3543 fprintf(stderr, "schemadata %p\n", data->schemadata);
3544 #endif
3545 
3546       /* seen: -1: old data,  0: skipped,  >0: id + 1 */
3547       neednewschema = 0;
3548       sp = schema;
3549       haveoldkl = 0;
3550       for (keyp = data->schemadata + data->schemata[oldschemaid]; *keyp; keyp++)
3551 	{
3552 	  if (seen[*keyp])
3553 	    {
3554 	      /* oops, should not happen */
3555 	      neednewschema = 1;
3556 	      continue;
3557 	    }
3558 	  seen[*keyp] = -1;	/* use old marker */
3559 	  *sp++ = *keyp;
3560 	  if (keylink[*keyp])
3561 	    haveoldkl = 1;	/* potential keylink conflict */
3562 	}
3563 
3564       /* strip solvables key */
3565       if (entry < 0 && solvkeyid && seen[solvkeyid])
3566 	{
3567 	  *sp = 0;
3568 	  for (sp = keyp = schema; *sp; sp++)
3569 	    if (*sp != solvkeyid)
3570 	      *keyp++ = *sp;
3571 	  sp = keyp;
3572 	  seen[solvkeyid] = 0;
3573 	  neednewschema = 1;
3574 	}
3575 
3576       /* add new entries */
3577       if (entry >= 0)
3578 	keyp = data->attrs ? data->attrs[entry] : 0;
3579       else
3580         keyp = data->xattrs ? data->xattrs[1] : 0;
3581       if (keyp)
3582         for (; *keyp; keyp += 2)
3583 	  {
3584 	    if (!seen[*keyp])
3585 	      {
3586 	        neednewschema = 1;
3587 	        *sp++ = *keyp;
3588 		if (haveoldkl && keylink[*keyp])		/* this should be pretty rare */
3589 		  {
3590 		    Id kl;
3591 		    for (kl = keylink[*keyp]; kl != *keyp; kl = keylink[kl])
3592 		      if (seen[kl] == -1)
3593 		        {
3594 			  /* replacing old key kl, remove from schema and seen */
3595 			  Id *osp;
3596 			  for (osp = schema; osp < sp; osp++)
3597 			    if (*osp == kl)
3598 			      {
3599 			        memmove(osp, osp + 1, (sp - osp) * sizeof(Id));
3600 			        sp--;
3601 			        seen[kl] = 0;
3602 				break;
3603 			      }
3604 		        }
3605 		  }
3606 	      }
3607 	    seen[*keyp] = keyp[1] + 1;
3608 	  }
3609 
3610       /* add solvables key if needed */
3611       if (entry < 0 && data->end != data->start)
3612 	{
3613 	  *sp++ = solvkeyid;	/* always last in schema */
3614 	  neednewschema = 1;
3615 	}
3616 
3617       /* commit schema */
3618       *sp = 0;
3619       if (neednewschema)
3620         /* Ideally we'd like to sort the new schema here, to ensure
3621 	   schema equality independend of the ordering. */
3622 	schemaid = repodata_schema2id(data, schema, 1);
3623       else
3624 	schemaid = oldschemaid;
3625 
3626       if (entry < 0)
3627 	{
3628 	  data->mainschemaoffsets = solv_calloc(sp - schema, sizeof(Id));
3629 	  data->mainschema = schemaid;
3630 	}
3631 
3632       /* find offsets in old incore data */
3633       if (oldschemaid)
3634 	{
3635 	  Id *lastneeded = 0;
3636 	  for (sp = data->schemadata + data->schemata[oldschemaid]; *sp; sp++)
3637 	    if (seen[*sp] == -1)
3638 	      lastneeded = sp + 1;
3639 	  if (lastneeded)
3640 	    {
3641 	      if (!oldincoreoffs)
3642 	        oldincoreoffs = solv_malloc2(data->nkeys, 2 * sizeof(Offset));
3643 	      for (sp = data->schemadata + data->schemata[oldschemaid]; sp != lastneeded; sp++)
3644 		{
3645 		  /* Skip the data associated with this old key.  */
3646 		  key = data->keys + *sp;
3647 		  ndp = dp;
3648 		  if (key->storage == KEY_STORAGE_VERTICAL_OFFSET)
3649 		    {
3650 		      ndp = data_skip(ndp, REPOKEY_TYPE_ID);
3651 		      ndp = data_skip(ndp, REPOKEY_TYPE_ID);
3652 		    }
3653 		  else if (key->storage == KEY_STORAGE_INCORE)
3654 		    ndp = data_skip_key(data, ndp, key);
3655 		  oldincoreoffs[*sp * 2] = dp - data->incoredata;
3656 		  oldincoreoffs[*sp * 2 + 1] = ndp - dp;
3657 		  dp = ndp;
3658 		}
3659 	    }
3660 	}
3661 
3662       /* just copy over the complete old entry (including the schemaid) if there was no new data */
3663       if (entry >= 0 && !neednewschema && oldschemaid && (!data->attrs || !data->attrs[entry]) && dp)
3664 	{
3665 	  ndp = data->incoredata + data->incoreoffset[entry];
3666 	  data->incoreoffset[entry] = newincore.len;
3667 	  data_addblob(&newincore, ndp, dp - ndp);
3668 	  goto entrydone;
3669 	}
3670 
3671       /* Now create data blob.  We walk through the (possibly new) schema
3672 	 and either copy over old data, or insert the new.  */
3673       if (entry >= 0)
3674         data->incoreoffset[entry] = newincore.len;
3675       data_addid(&newincore, schemaid);
3676 
3677       /* we don't use a pointer to the schemadata here as repodata_serialize_key
3678        * may call repodata_schema2id() which might realloc our schemadata */
3679       for (schemaidx = data->schemata[schemaid]; (keyid = data->schemadata[schemaidx]) != 0; schemaidx++)
3680 	{
3681 	  if (entry < 0)
3682 	    {
3683 	      data->mainschemaoffsets[schemaidx - data->schemata[schemaid]] = newincore.len;
3684 	      if (keyid == solvkeyid)
3685 		{
3686 		  /* add flexarray entry count */
3687 		  data_addid(&newincore, data->end - data->start);
3688 		  break;	/* always the last entry */
3689 		}
3690 	    }
3691 	  if (seen[keyid] == -1)
3692 	    {
3693 	      if (oldincoreoffs[keyid * 2 + 1])
3694 		data_addblob(&newincore, data->incoredata + oldincoreoffs[keyid * 2], oldincoreoffs[keyid * 2 + 1]);
3695 	    }
3696 	  else if (seen[keyid])
3697 	    repodata_serialize_key(data, &newincore, &newvincore, schema, data->keys + keyid, seen[keyid] - 1);
3698 	}
3699 
3700 entrydone:
3701       /* free memory */
3702       if (entry >= 0 && data->attrs)
3703 	{
3704 	  if (data->attrs[entry])
3705 	    data->attrs[entry] = solv_free(data->attrs[entry]);
3706 	  if (entry && entry % 4096 == 0 && data->nxattrs <= 2 && entry + 64 < nentry)
3707 	    {
3708 	      compact_attrdata(data, entry + 1, nentry);	/* try to free some memory */
3709 #if 0
3710 	      printf("  attr data: %d K\n", data->attrdatalen / 1024);
3711 	      printf("  attrid data: %d K\n", data->attriddatalen / (1024 / 4));
3712 	      printf("  incore data: %d K\n", newincore.len / 1024);
3713 	      printf("  sum: %d K\n", (newincore.len + data->attrdatalen + data->attriddatalen * 4) / 1024);
3714 	      /* malloc_stats(); */
3715 #endif
3716 	    }
3717 	}
3718     }
3719   /* free all xattrs */
3720   for (entry = 0; entry < data->nxattrs; entry++)
3721     if (data->xattrs[entry])
3722       solv_free(data->xattrs[entry]);
3723   data->xattrs = solv_free(data->xattrs);
3724   data->nxattrs = 0;
3725 
3726   data->lasthandle = 0;
3727   data->lastkey = 0;
3728   data->lastdatalen = 0;
3729   solv_free(schema);
3730   solv_free(seen);
3731   solv_free(keylink);
3732   solv_free(oldincoreoffs);
3733   repodata_free_schemahash(data);
3734 
3735   solv_free(data->incoredata);
3736   data->incoredata = newincore.buf;
3737   data->incoredatalen = newincore.len;
3738   data->incoredatafree = 0;
3739 
3740   data->vincore = newvincore.buf;
3741   data->vincorelen = newvincore.len;
3742 
3743   data->attrs = solv_free(data->attrs);
3744   data->attrdata = solv_free(data->attrdata);
3745   data->attriddata = solv_free(data->attriddata);
3746   data->attrnum64data = solv_free(data->attrnum64data);
3747   data->attrdatalen = 0;
3748   data->attriddatalen = 0;
3749   data->attrnum64datalen = 0;
3750 #if 0
3751   printf("repodata_internalize %d done\n", data->repodataid);
3752   printf("  incore data: %d K\n", data->incoredatalen / 1024);
3753 #endif
3754 }
3755 
3756 void
repodata_disable_paging(Repodata * data)3757 repodata_disable_paging(Repodata *data)
3758 {
3759   if (maybe_load_repodata(data, 0))
3760     {
3761       repopagestore_disable_paging(&data->store);
3762       data->storestate++;
3763     }
3764 }
3765 
3766 /* call the pool's loadcallback to load a stub repodata */
3767 static void
repodata_stub_loader(Repodata * data)3768 repodata_stub_loader(Repodata *data)
3769 {
3770   Repo *repo = data->repo;
3771   Pool *pool = repo->pool;
3772   int r, i;
3773   struct s_Pool_tmpspace oldtmpspace;
3774   Datapos oldpos;
3775 
3776   if (!pool->loadcallback)
3777     {
3778       data->state = REPODATA_ERROR;
3779       return;
3780     }
3781   data->state = REPODATA_LOADING;
3782 
3783   /* save tmp space and pos */
3784   oldtmpspace = pool->tmpspace;
3785   memset(&pool->tmpspace, 0, sizeof(pool->tmpspace));
3786   oldpos = pool->pos;
3787 
3788   r = pool->loadcallback(pool, data, pool->loadcallbackdata);
3789 
3790   /* restore tmp space and pos */
3791   for (i = 0; i < POOL_TMPSPACEBUF; i++)
3792     solv_free(pool->tmpspace.buf[i]);
3793   pool->tmpspace = oldtmpspace;
3794   if (r && oldpos.repo == repo && oldpos.repodataid == data->repodataid)
3795     memset(&oldpos, 0, sizeof(oldpos));
3796   pool->pos = oldpos;
3797 
3798   data->state = r ? REPODATA_AVAILABLE : REPODATA_ERROR;
3799 }
3800 
3801 static inline void
repodata_add_stubkey(Repodata * data,Id keyname,Id keytype)3802 repodata_add_stubkey(Repodata *data, Id keyname, Id keytype)
3803 {
3804   Repokey xkey;
3805 
3806   xkey.name = keyname;
3807   xkey.type = keytype;
3808   xkey.storage = KEY_STORAGE_INCORE;
3809   xkey.size = 0;
3810   repodata_key2id(data, &xkey, 1);
3811 }
3812 
3813 static Repodata *
repodata_add_stub(Repodata ** datap)3814 repodata_add_stub(Repodata **datap)
3815 {
3816   Repodata *data = *datap;
3817   Repo *repo = data->repo;
3818   Id repodataid = data - repo->repodata;
3819   Repodata *sdata = repo_add_repodata(repo, 0);
3820   data = repo->repodata + repodataid;
3821   if (data->end > data->start)
3822     repodata_extend_block(sdata, data->start, data->end - data->start);
3823   sdata->state = REPODATA_STUB;
3824   sdata->loadcallback = repodata_stub_loader;
3825   *datap = data;
3826   return sdata;
3827 }
3828 
3829 Repodata *
repodata_create_stubs(Repodata * data)3830 repodata_create_stubs(Repodata *data)
3831 {
3832   Repo *repo = data->repo;
3833   Pool *pool = repo->pool;
3834   Repodata *sdata;
3835   int *stubdataids;
3836   Dataiterator di;
3837   Id xkeyname = 0;
3838   int i, cnt = 0;
3839 
3840   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3841   while (dataiterator_step(&di))
3842     if (di.data == data)
3843       cnt++;
3844   dataiterator_free(&di);
3845   if (!cnt)
3846     return data;
3847   stubdataids = solv_calloc(cnt, sizeof(*stubdataids));
3848   for (i = 0; i < cnt; i++)
3849     {
3850       sdata = repodata_add_stub(&data);
3851       stubdataids[i] = sdata - repo->repodata;
3852     }
3853   i = 0;
3854   dataiterator_init(&di, pool, repo, SOLVID_META, REPOSITORY_EXTERNAL, 0, 0);
3855   sdata = 0;
3856   while (dataiterator_step(&di))
3857     {
3858       if (di.data != data)
3859 	continue;
3860       if (di.key->name == REPOSITORY_EXTERNAL && !di.nparents)
3861 	{
3862 	  dataiterator_entersub(&di);
3863 	  sdata = repo->repodata + stubdataids[i++];
3864 	  xkeyname = 0;
3865 	  continue;
3866 	}
3867       repodata_set_kv(sdata, SOLVID_META, di.key->name, di.key->type, &di.kv);
3868       if (di.key->name == REPOSITORY_KEYS && di.key->type == REPOKEY_TYPE_IDARRAY)
3869 	{
3870 	  if (!xkeyname)
3871 	    {
3872 	      if (!di.kv.eof)
3873 		xkeyname = di.kv.id;
3874 	    }
3875 	  else
3876 	    {
3877 	      repodata_add_stubkey(sdata, xkeyname, di.kv.id);
3878 	      if (xkeyname == SOLVABLE_FILELIST)
3879 	        repodata_set_filelisttype(sdata, REPODATA_FILELIST_EXTENSION);
3880 	      xkeyname = 0;
3881 	    }
3882 	}
3883     }
3884   dataiterator_free(&di);
3885   for (i = 0; i < cnt; i++)
3886     repodata_internalize(repo->repodata + stubdataids[i]);
3887   solv_free(stubdataids);
3888   return data;
3889 }
3890 
3891 void
repodata_set_filelisttype(Repodata * data,int type)3892 repodata_set_filelisttype(Repodata *data, int type)
3893 {
3894   data->filelisttype = type;
3895 }
3896 
3897 unsigned int
repodata_memused(Repodata * data)3898 repodata_memused(Repodata *data)
3899 {
3900   return data->incoredatalen + data->vincorelen;
3901 }
3902 
3903