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