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