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