2 * Copyright (c) 2007, Novell Inc.
4 * This program is licensed under the BSD license, read LICENSE.BSD
5 * for further information
11 * Write Repo data out to binary file
13 * See doc/README.format for a description
14 * of the binary file format
18 #include <sys/types.h>
28 #include "repo_write.h"
30 /*------------------------------------------------------------------*/
31 /* Id map optimizations */
33 typedef struct needid {
39 #define RELOFF(id) (needid[0].map + GETRELID(id))
43 * idarray: array of Ids, ID_NULL terminated
44 * needid: array of Id->NeedId
51 incneedid(Pool *pool, Id id, NeedId *needid)
55 Reldep *rd = GETRELDEP(pool, id);
56 needid[RELOFF(id)].need++;
57 if (ISRELDEP(rd->evr))
58 incneedid(pool, rd->evr, needid);
60 needid[rd->evr].need++;
67 incneedidarray(Pool *pool, Id *idarray, NeedId *needid)
74 while ((id = *idarray++) != 0)
79 Reldep *rd = GETRELDEP(pool, id);
80 needid[RELOFF(id)].need++;
81 if (ISRELDEP(rd->evr))
82 incneedid(pool, rd->evr, needid);
84 needid[rd->evr].need++;
98 needid_cmp_need(const void *ap, const void *bp)
100 const NeedId *a = ap;
101 const NeedId *b = bp;
103 r = b->need - a->need;
106 return a->map - b->map;
109 static Stringpool *cmp_spool;
112 needid_cmp_need_s(const void *ap, const void *bp)
114 const NeedId *a = ap;
115 const NeedId *b = bp;
117 r = b->need - a->need;
120 const char *as = cmp_spool->stringspace + cmp_spool->strings[a->map];
121 const char *bs = cmp_spool->stringspace + cmp_spool->strings[b->map];
122 return strcmp(as, bs);
126 /*------------------------------------------------------------------*/
127 /* output helper routines */
134 write_u32(FILE *fp, unsigned int x)
136 if (putc(x >> 24, fp) == EOF ||
137 putc(x >> 16, fp) == EOF ||
138 putc(x >> 8, fp) == EOF ||
141 perror("write error u32");
152 write_u8(FILE *fp, unsigned int x)
154 if (putc(x, fp) == EOF)
156 perror("write error u8");
166 write_blob(FILE *fp, void *data, int len)
168 if (len && fwrite(data, len, 1, fp) != 1)
170 perror("write error blob");
180 write_id(FILE *fp, Id x)
185 putc((x >> 28) | 128, fp);
187 putc((x >> 21) | 128, fp);
188 putc((x >> 14) | 128, fp);
191 putc((x >> 7) | 128, fp);
192 if (putc(x & 127, fp) == EOF)
194 perror("write error id");
200 write_id_eof(FILE *fp, Id x, int eof)
203 x = (x & 63) | ((x & ~63) << 1);
204 write_id(fp, x | (eof ? 0 : 64));
211 write_str(FILE *fp, const char *str)
213 if (fputs (str, fp) == EOF || putc (0, fp) == EOF)
215 perror("write error");
226 write_idarray(FILE *fp, Pool *pool, NeedId *needid, Id *ids)
240 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
242 id = (id & 63) | ((id & ~63) << 1);
248 write_id(fp, id | 64);
253 cmp_ids (const void *pa, const void *pb)
262 write_idarray_sort(FILE *fp, Pool *pool, NeedId *needid, Id *ids, Id marker)
274 for (len = 0; len < 64 && ids[len]; len++)
278 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
283 for (i = len + 1; ids[i]; i++)
285 sids = sat_malloc2(i, sizeof(Id));
286 memcpy(sids, lids, 64 * sizeof(Id));
287 for (; ids[len]; len++)
291 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
298 /* That bloody solvable:prereqmarker needs to stay in position :-( */
300 marker = needid[marker].need;
301 for (i = 0; i < len; i++)
302 if (sids[i] == marker)
305 qsort(sids, i, sizeof (Id), cmp_ids);
307 qsort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids);
311 /* The differencing above produces many runs of ones and twos. I tried
312 fairly elaborate schemes to RLE those, but they give only very mediocre
313 improvements in compression, as coding the escapes costs quite some
314 space. Even if they are coded only as bits in IDs. The best improvement
315 was about 2.7% for the whole .solv file. It's probably better to
316 invest some complexity into sharing idarrays, than RLEing. */
317 for (i = 0; i < len - 1; i++)
320 /* Ugly PREREQ handling. A "difference" of 0 is the prereq marker,
321 hence all real differences are offsetted by 1. Otherwise we would
322 have to handle negative differences, which would cost code space for
323 the encoding of the sign. We loose the exact mapping of prereq here,
324 but we know the result, so we can recover from that in the reader. */
332 /* XXX If difference is zero we have multiple equal elements,
333 we might want to skip writing them out. */
335 id = (id & 63) | ((id & ~63) << 1);
336 write_id(fp, id | 64);
344 id = (id & 63) | ((id & ~63) << 1);
360 Stringpool *ownspool;
372 Id *schema; /* schema construction space */
373 Id *sp; /* pointer in above */
374 Id *oldschema, *oldsp;
382 Id schematacache[256];
390 struct extdata *extdata;
397 #define NEEDED_BLOCK 1023
398 #define SCHEMATA_BLOCK 31
399 #define SCHEMATADATA_BLOCK 255
400 #define EXTDATA_BLOCK 4095
403 data_addid(struct extdata *xd, Id x)
406 xd->buf = sat_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
407 dp = xd->buf + xd->len;
412 *dp++ = (x >> 28) | 128;
414 *dp++ = (x >> 21) | 128;
415 *dp++ = (x >> 14) | 128;
418 *dp++ = (x >> 7) | 128;
420 xd->len = dp - xd->buf;
424 data_addideof(struct extdata *xd, Id x, int eof)
427 x = (x & 63) | ((x & ~63) << 1);
428 data_addid(xd, (eof ? x: x | 64));
432 data_addidarray_sort(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id marker)
444 for (len = 0; len < 64 && ids[len]; len++)
448 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
453 for (i = len + 1; ids[i]; i++)
455 sids = sat_malloc2(i, sizeof(Id));
456 memcpy(sids, lids, 64 * sizeof(Id));
457 for (; ids[len]; len++)
461 id = needid[ISRELDEP(id) ? RELOFF(id) : id].need;
468 /* That bloody solvable:prereqmarker needs to stay in position :-( */
470 marker = needid[marker].need;
471 for (i = 0; i < len; i++)
472 if (sids[i] == marker)
475 qsort(sids, i, sizeof (Id), cmp_ids);
477 qsort(sids + i + 1, len - i - 1, sizeof(Id), cmp_ids);
481 /* The differencing above produces many runs of ones and twos. I tried
482 fairly elaborate schemes to RLE those, but they give only very mediocre
483 improvements in compression, as coding the escapes costs quite some
484 space. Even if they are coded only as bits in IDs. The best improvement
485 was about 2.7% for the whole .solv file. It's probably better to
486 invest some complexity into sharing idarrays, than RLEing. */
487 for (i = 0; i < len - 1; i++)
490 /* Ugly PREREQ handling. A "difference" of 0 is the prereq marker,
491 hence all real differences are offsetted by 1. Otherwise we would
492 have to handle negative differences, which would cost code space for
493 the encoding of the sign. We loose the exact mapping of prereq here,
494 but we know the result, so we can recover from that in the reader. */
502 /* XXX If difference is zero we have multiple equal elements,
503 we might want to skip writing them out. */
505 id = (id & 63) | ((id & ~63) << 1);
506 data_addid(xd, id | 64);
514 id = (id & 63) | ((id & ~63) << 1);
521 data_addblob(struct extdata *xd, unsigned char *blob, int len)
523 xd->buf = sat_extend(xd->buf, xd->len, len, 1, EXTDATA_BLOCK);
524 memcpy(xd->buf + xd->len, blob, len);
529 data_addu32(struct extdata *xd, unsigned int num)
536 data_addblob(xd, d, 4);
540 addschema(struct cbdata *cbdata, Id *schema)
545 for (sp = schema, len = 0, h = 0; *sp; len++)
550 cid = cbdata->schematacache[h];
553 if (!memcmp(cbdata->myschemadata + cbdata->myschemata[cid], schema, len * sizeof(Id)))
556 for (cid = 1; cid < cbdata->nmyschemata; cid++)
557 if (!memcmp(cbdata->myschemadata + cbdata->myschemata[cid], schema, len * sizeof(Id)))
560 /* a new one. make room. */
561 if (!cbdata->nmyschemata)
563 /* allocate schema 0, it is always empty */
564 cbdata->myschemadata = sat_extend(cbdata->myschemadata, cbdata->myschemadatalen, 1, sizeof(Id), SCHEMATADATA_BLOCK);
565 cbdata->myschemata = sat_extend(cbdata->myschemata, cbdata->nmyschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
566 cbdata->myschemata[0] = 0;
567 cbdata->myschemadata[0] = 0;
568 cbdata->nmyschemata = 1;
569 cbdata->myschemadatalen = 1;
572 cbdata->myschemadata = sat_extend(cbdata->myschemadata, cbdata->myschemadatalen, len, sizeof(Id), SCHEMATADATA_BLOCK);
573 cbdata->myschemata = sat_extend(cbdata->myschemata, cbdata->nmyschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
574 memcpy(cbdata->myschemadata + cbdata->myschemadatalen, schema, len * sizeof(Id));
575 cbdata->myschemata[cbdata->nmyschemata] = cbdata->myschemadatalen;
576 cbdata->myschemadatalen += len;
577 cbdata->schematacache[h] = cbdata->nmyschemata;
578 return cbdata->nmyschemata++;
582 putinownpool(struct cbdata *cbdata, Stringpool *ss, Id id)
584 const char *str = stringpool_id2str(ss, id);
585 id = stringpool_str2id(cbdata->ownspool, str, 1);
586 if (id >= cbdata->needid[0].map)
588 int oldoff = cbdata->needid[0].map;
589 int newoff = (id + 1 + NEEDED_BLOCK) & ~NEEDED_BLOCK;
590 int nrels = cbdata->repo->pool->nrels;
591 cbdata->needid = sat_realloc2(cbdata->needid, newoff + nrels, sizeof(NeedId));
593 memmove(cbdata->needid + newoff, cbdata->needid + oldoff, nrels * sizeof(NeedId));
594 memset(cbdata->needid + oldoff, 0, (newoff - oldoff) * sizeof(NeedId));
595 cbdata->needid[0].map = newoff;
601 putinowndirpool(struct cbdata *cbdata, Repodata *data, Dirpool *dp, Id dir)
605 parent = dirpool_parent(dp, dir);
607 parent = putinowndirpool(cbdata, data, dp, parent);
608 compid = dp->dirs[dir];
609 if (cbdata->ownspool && compid > 1)
610 compid = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, compid);
611 return dirpool_add_dir(cbdata->owndirpool, parent, compid, 1);
615 setdirused(struct cbdata *cbdata, Dirpool *dp, Id dir)
617 if (cbdata->dirused[dir])
619 cbdata->dirused[dir] = 1;
620 while ((dir = dirpool_parent(dp, dir)) != 0)
622 if (cbdata->dirused[dir] == 2)
624 if (cbdata->dirused[dir])
626 cbdata->dirused[dir] = 2;
629 cbdata->dirused[dir] = 2;
631 cbdata->dirused[0] = 2;
635 repo_write_collect_needed(struct cbdata *cbdata, Repo *repo, Repodata *data, Repokey *key, KeyValue *kv)
640 rm = cbdata->keymap[cbdata->keymapstart[data - data->repo->repodata] + (key - data->keys)];
642 return SEARCH_NEXT_KEY; /* we do not want this one */
643 /* record key in schema */
644 if ((key->type != REPOKEY_TYPE_COUNTED || kv->eof == 0)
645 && (cbdata->sp == cbdata->schema || cbdata->sp[-1] != rm))
649 case REPOKEY_TYPE_ID:
650 case REPOKEY_TYPE_IDARRAY:
652 if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
653 id = putinownpool(cbdata, data->localpool ? &data->spool : &repo->pool->ss, id);
654 incneedid(repo->pool, id, cbdata->needid);
656 case REPOKEY_TYPE_DIR:
657 case REPOKEY_TYPE_DIRNUMNUMARRAY:
658 case REPOKEY_TYPE_DIRSTRARRAY:
660 if (cbdata->owndirpool)
661 putinowndirpool(cbdata, data, &data->dirpool, id);
663 setdirused(cbdata, &data->dirpool, id);
665 case REPOKEY_TYPE_COUNTED:
668 if (cbdata->oldschema)
670 fprintf(stderr, "nested structs not yet implemented\n");
673 cbdata->oldschema = cbdata->schema;
674 cbdata->oldsp = cbdata->sp;
675 cbdata->schema = sat_calloc(cbdata->nmykeys, sizeof(Id));
676 cbdata->sp = cbdata->schema;
678 else if (kv->eof == 1)
680 cbdata->current_sub++;
682 cbdata->subschemata = sat_extend(cbdata->subschemata, cbdata->nsubschemata, 1, sizeof(Id), SCHEMATA_BLOCK);
683 cbdata->subschemata[cbdata->nsubschemata++] = addschema(cbdata, cbdata->schema);
685 fprintf(stderr, "Have schema %d\n", cbdata->subschemata[cbdata->nsubschemata-1]);
687 cbdata->sp = cbdata->schema;
691 sat_free(cbdata->schema);
692 cbdata->schema = cbdata->oldschema;
693 cbdata->sp = cbdata->oldsp;
694 cbdata->oldsp = cbdata->oldschema = 0;
704 repo_write_cb_needed(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
706 struct cbdata *cbdata = vcbdata;
707 Repo *repo = s ? s->repo : 0;
709 fprintf(stderr, "solvable %d (%s): key (%d)%s %d\n", s ? s - s->repo->pool->solvables : 0, s ? id2str(s->repo->pool, s->name) : "", key->name, id2str(repo->pool, key->name), key->type);
711 return repo_write_collect_needed(cbdata, repo, data, key, kv);
715 repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue *kv)
723 rm = cbdata->keymap[cbdata->keymapstart[data - data->repo->repodata] + (key - data->keys)];
725 return 0; /* we do not want this one */
727 if (cbdata->mykeys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET)
729 xd = cbdata->extdata + rm; /* vertical buffer */
730 if (cbdata->vstart == -1)
731 cbdata->vstart = xd->len;
734 xd = cbdata->extdata + 0; /* incore buffer */
737 case REPOKEY_TYPE_VOID:
738 case REPOKEY_TYPE_CONSTANT:
739 case REPOKEY_TYPE_CONSTANTID:
741 case REPOKEY_TYPE_ID:
743 if (!ISRELDEP(id) && cbdata->ownspool && id > 1)
744 id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
745 id = cbdata->needid[id].need;
748 case REPOKEY_TYPE_IDARRAY:
750 if (cbdata->ownspool && id > 1)
751 id = putinownpool(cbdata, data->localpool ? &data->spool : &data->repo->pool->ss, id);
752 id = cbdata->needid[id].need;
753 data_addideof(xd, id, kv->eof);
755 case REPOKEY_TYPE_STR:
756 data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
758 case REPOKEY_TYPE_MD5:
759 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_MD5);
761 case REPOKEY_TYPE_SHA1:
762 data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA1);
764 case REPOKEY_TYPE_U32:
770 data_addblob(xd, v, 4);
772 case REPOKEY_TYPE_NUM:
773 data_addid(xd, kv->num);
775 case REPOKEY_TYPE_DIR:
777 if (cbdata->owndirpool)
778 id = putinowndirpool(cbdata, data, &data->dirpool, id);
779 id = cbdata->dirused[id];
782 case REPOKEY_TYPE_DIRNUMNUMARRAY:
784 if (cbdata->owndirpool)
785 id = putinowndirpool(cbdata, data, &data->dirpool, id);
786 id = cbdata->dirused[id];
788 data_addid(xd, kv->num);
789 data_addideof(xd, kv->num2, kv->eof);
791 case REPOKEY_TYPE_DIRSTRARRAY:
793 if (cbdata->owndirpool)
794 id = putinowndirpool(cbdata, data, &data->dirpool, id);
795 id = cbdata->dirused[id];
796 data_addideof(xd, id, kv->eof);
797 data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
799 case REPOKEY_TYPE_COUNTED:
804 data_addid(xd, kv->num);
805 data_addid(xd, cbdata->subschemata[cbdata->current_sub]);
807 fprintf(stderr, "writing %d %d\n", kv->num, cbdata->subschemata[cbdata->current_sub]);
811 else if (kv->eof == 1)
813 cbdata->current_sub++;
820 fprintf(stderr, "unknown type for %d: %d\n", key->name, key->type);
823 if (cbdata->mykeys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET && kv->eof)
825 /* we can re-use old data in the blob here! */
826 data_addid(cbdata->extdata + 0, cbdata->vstart); /* add offset into incore data */
827 data_addid(cbdata->extdata + 0, xd->len - cbdata->vstart); /* add length into incore data */
834 repo_write_cb_adddata(void *vcbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *kv)
836 struct cbdata *cbdata = vcbdata;
837 return repo_write_adddata(cbdata, data, key, kv);
841 traverse_dirs(Dirpool *dp, Id *dirmap, Id n, Id dir, Id *used)
847 /* special case for '/', which has to come first */
850 for (sib = dir; sib; sib = dirpool_sibling(dp, sib))
852 if (used && !used[sib])
854 if (sib == 1 && parent == 1)
855 continue; /* already did that one above */
859 for (; parent < lastn; parent++)
861 sib = dirmap[parent];
862 if (used && used[sib] != 2)
864 child = dirpool_child(dp, sib);
867 dirmap[n++] = -parent;
868 n = traverse_dirs(dp, dirmap, n, child, used);
874 #define BLOB_PAGEBITS 15
875 #define BLOB_PAGESIZE (1 << BLOB_PAGEBITS)
878 write_compressed_page(FILE *fp, unsigned char *page, int len)
881 unsigned char cpage[BLOB_PAGESIZE];
883 clen = repodata_compress_page(page, len, cpage, len - 1);
886 write_u32(fp, len * 2);
887 write_blob(fp, page, len);
891 write_u32(fp, clen * 2 + 1);
892 write_blob(fp, cpage, clen);
897 static Id subfilekeys[] = {
898 REPODATA_INFO, REPOKEY_TYPE_VOID,
899 REPODATA_EXTERNAL, REPOKEY_TYPE_VOID,
900 REPODATA_KEYS, REPOKEY_TYPE_IDARRAY,
901 REPODATA_LOCATION, REPOKEY_TYPE_STR,
902 REPODATA_ADDEDFILEPROVIDES, REPOKEY_TYPE_REL_IDARRAY,
903 REPODATA_RPMDBCOOKIE, REPOKEY_TYPE_SHA256,
912 repo_write(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Repodatafile *fileinfo, int nsubfiles)
914 Pool *pool = repo->pool;
920 unsigned int solv_flags;
929 unsigned char *repodataused;
932 struct cbdata cbdata;
935 int poolusage, dirpoolusage, idused, dirused;
938 Repodata *data, *dirpooldata = 0;
939 Stringpool ownspool, *spool;
940 Dirpool owndirpool, *dirpool;
943 Id *repodataschemata = 0;
949 Id type_constantid = 0;
951 /* If we're given a fileinfo structure, but have no subfiles, then we're
952 writing a subfile and our callers wants info about it. */
953 if (fileinfo && nsubfiles == 0)
956 repodataschemata = sat_calloc(nsubfiles, sizeof(Id));
958 memset(&cbdata, 0, sizeof(cbdata));
961 /* go through all repodata and find the keys we need */
962 /* also unify keys */
963 /* creates: mykeys - key array, still has global pool ids */
964 /* keymapstart - maps repo number to keymap offset */
965 /* keymap - maps repo key to my key, 0 -> not used */
967 /* start with all KEY_STORAGE_SOLVABLE ids */
970 for (i = 0; i < repo->nrepodata; i++)
971 n += repo->repodata[i].nkeys;
972 cbdata.mykeys = sat_calloc(n, sizeof(Repokey));
973 cbdata.keymap = sat_calloc(n, sizeof(Id));
974 cbdata.keymapstart = sat_calloc(repo->nrepodata, sizeof(Id));
975 repodataused = sat_calloc(repo->nrepodata, 1);
980 for (i = SOLVABLE_NAME; i <= RPM_RPMDBID; i++)
982 key = cbdata.mykeys + i;
984 if (i < SOLVABLE_PROVIDES)
985 key->type = REPOKEY_TYPE_ID;
986 else if (i < RPM_RPMDBID)
987 key->type = REPOKEY_TYPE_REL_IDARRAY;
989 key->type = REPOKEY_TYPE_U32;
991 key->storage = KEY_STORAGE_SOLVABLE;
994 key->storage = keyfilter(repo, key, kfdata);
995 if (key->storage == KEY_STORAGE_DROPPED)
997 key->storage = KEY_STORAGE_SOLVABLE;
1000 if (key->type == REPOKEY_TYPE_IDARRAY || key->type == REPOKEY_TYPE_REL_IDARRAY)
1002 cbdata.keymap[i] = i;
1006 /* If we store subfile info, generate the necessary keys. */
1009 for (i = 0; subfilekeys[i]; i += 2)
1011 key = cbdata.mykeys + cbdata.nmykeys;
1012 key->name = subfilekeys[i];
1013 key->type = subfilekeys[i + 1];
1015 key->storage = KEY_STORAGE_SOLVABLE;
1016 cbdata.keymap[key->name] = cbdata.nmykeys++;
1024 n = ID_NUM_INTERNAL;
1025 for (i = 0; i < repo->nrepodata; i++)
1027 data = repo->repodata + i;
1028 cbdata.keymapstart[i] = n;
1029 cbdata.keymap[n++] = 0; /* key 0 */
1032 for (j = 1; j < data->nkeys; j++, n++)
1034 key = data->keys + j;
1035 /* see if we already had this one, should use hash for fast miss */
1036 for (k = 0; k < cbdata.nmykeys; k++)
1038 if (key->name == cbdata.mykeys[k].name && key->type == cbdata.mykeys[k].type)
1040 if ((key->type == REPOKEY_TYPE_CONSTANT || key->type == REPOKEY_TYPE_CONSTANTID) && key->size != cbdata.mykeys[k].size)
1045 if (k < cbdata.nmykeys)
1046 cbdata.keymap[n] = k;
1049 /* found a new key! */
1050 cbdata.mykeys[cbdata.nmykeys] = *key;
1051 key = cbdata.mykeys + cbdata.nmykeys;
1052 key->storage = KEY_STORAGE_INCORE;
1053 if (key->type != REPOKEY_TYPE_CONSTANT && key->type != REPOKEY_TYPE_CONSTANTID)
1057 key->storage = keyfilter(repo, key, kfdata);
1058 if (key->storage == KEY_STORAGE_DROPPED)
1060 cbdata.keymap[n] = 0;
1064 cbdata.keymap[n] = cbdata.nmykeys++;
1066 /* load repodata if not already loaded */
1067 if (data->state == REPODATA_STUB)
1069 if (data->loadcallback)
1070 data->loadcallback(data);
1072 data->state = REPODATA_ERROR;
1073 if (data->state != REPODATA_ERROR)
1075 /* redo this repodata! */
1077 n = cbdata.keymapstart[i];
1081 if (data->state == REPODATA_ERROR)
1084 cbdata.keymap[n] = 0;
1088 repodataused[i] = 1;
1089 anyrepodataused = 1;
1090 if (key->type != REPOKEY_TYPE_STR
1091 && key->type != REPOKEY_TYPE_U32
1092 && key->type != REPOKEY_TYPE_MD5
1093 && key->type != REPOKEY_TYPE_SHA1)
1095 if (key->type == REPOKEY_TYPE_DIR || key->type == REPOKEY_TYPE_DIRNUMNUMARRAY || key->type == REPOKEY_TYPE_DIRSTRARRAY)
1097 /* make sure we know that key */
1098 if (data->localpool)
1100 stringpool_str2id(&data->spool, id2str(pool, key->name), 1);
1101 stringpool_str2id(&data->spool, id2str(pool, key->type), 1);
1102 if (key->type == REPOKEY_TYPE_CONSTANTID)
1103 stringpool_str2id(&data->spool, id2str(pool, key->size), 1);
1108 if (data->localpool)
1111 poolusage = 3; /* need local pool */
1115 spool = &data->spool;
1122 else if (poolusage != 1)
1123 poolusage = 3; /* need local pool */
1129 dirpoolusage = 3; /* need local dirpool */
1133 dirpool = &data->dirpool;
1140 /* 0: no pool needed at all */
1141 /* 1: use global pool */
1142 /* 2: use repodata local pool */
1143 /* 3: need own pool */
1149 /* hack: reuse global pool so we don't have to map rel ids */
1150 stringpool_clone(spool, &repo->pool->ss);
1153 stringpool_init_empty(spool);
1154 cbdata.ownspool = spool;
1156 else if (poolusage == 0 || poolusage == 1)
1159 spool = &repo->pool->ss;
1161 if (dirpoolusage == 3)
1163 dirpool = &owndirpool;
1165 dirpool_create(dirpool);
1166 cbdata.owndirpool = dirpool;
1169 cbdata.dirused = sat_calloc(dirpool->ndirs, sizeof(Id));
1172 /********************************************************************/
1174 fprintf(stderr, "poolusage: %d\n", poolusage);
1175 fprintf(stderr, "dirpoolusage: %d\n", dirpoolusage);
1176 fprintf(stderr, "nmykeys: %d\n", cbdata.nmykeys);
1177 for (i = 1; i < cbdata.nmykeys; i++)
1178 fprintf(stderr, " %2d: %s[%d] %d %d %d\n", i, id2str(pool, cbdata.mykeys[i].name), cbdata.mykeys[i].name, cbdata.mykeys[i].type, cbdata.mykeys[i].size, cbdata.mykeys[i].storage);
1181 /********************************************************************/
1183 /* set needed count of all strings and rels,
1184 * find which keys are used in the solvables
1185 * put all strings in own spool
1188 reloff = spool->nstrings;
1190 reloff = (reloff + NEEDED_BLOCK) & ~NEEDED_BLOCK;
1192 needid = calloc(reloff + pool->nrels, sizeof(*needid));
1193 needid[0].map = reloff;
1195 cbdata.needid = needid;
1196 cbdata.schema = sat_calloc(cbdata.nmykeys, sizeof(Id));
1197 cbdata.sp = cbdata.schema;
1198 cbdata.solvschemata = sat_calloc(repo->nsolvables, sizeof(Id));
1199 cbdata.extraschemata = sat_calloc(repo->nextra, sizeof(Id));
1201 idarraydata = repo->idarraydata;
1203 for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1205 if (s->repo != repo)
1208 /* set schema info, keep in sync with further down */
1210 if (cbdata.keymap[SOLVABLE_NAME])
1212 *sp++ = SOLVABLE_NAME;
1213 needid[s->name].need++;
1215 if (cbdata.keymap[SOLVABLE_ARCH])
1217 *sp++ = SOLVABLE_ARCH;
1218 needid[s->arch].need++;
1220 if (cbdata.keymap[SOLVABLE_EVR])
1222 *sp++ = SOLVABLE_EVR;
1223 needid[s->evr].need++;
1225 if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1227 *sp++ = SOLVABLE_VENDOR;
1228 needid[s->vendor].need++;
1230 if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1232 *sp++ = SOLVABLE_PROVIDES;
1233 cbdata.mykeys[SOLVABLE_PROVIDES].size += incneedidarray(pool, idarraydata + s->provides, needid);
1235 if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1237 *sp++ = SOLVABLE_OBSOLETES;
1238 cbdata.mykeys[SOLVABLE_OBSOLETES].size += incneedidarray(pool, idarraydata + s->obsoletes, needid);
1240 if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1242 *sp++ = SOLVABLE_CONFLICTS;
1243 cbdata.mykeys[SOLVABLE_CONFLICTS].size += incneedidarray(pool, idarraydata + s->conflicts, needid);
1245 if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1247 *sp++ = SOLVABLE_REQUIRES;
1248 cbdata.mykeys[SOLVABLE_REQUIRES].size += incneedidarray(pool, idarraydata + s->requires, needid);
1250 if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1252 *sp++ = SOLVABLE_RECOMMENDS;
1253 cbdata.mykeys[SOLVABLE_RECOMMENDS].size += incneedidarray(pool, idarraydata + s->recommends, needid);
1255 if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1257 *sp++ = SOLVABLE_SUGGESTS;
1258 cbdata.mykeys[SOLVABLE_SUGGESTS].size += incneedidarray(pool, idarraydata + s->suggests, needid);
1260 if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1262 *sp++ = SOLVABLE_SUPPLEMENTS;
1263 cbdata.mykeys[SOLVABLE_SUPPLEMENTS].size += incneedidarray(pool, idarraydata + s->supplements, needid);
1265 if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1267 *sp++ = SOLVABLE_ENHANCES;
1268 cbdata.mykeys[SOLVABLE_ENHANCES].size += incneedidarray(pool, idarraydata + s->enhances, needid);
1270 if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1272 *sp++ = RPM_RPMDBID;
1273 cbdata.mykeys[RPM_RPMDBID].size++;
1277 if (anyrepodataused)
1279 for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1281 if (!repodataused[j])
1283 if (i < data->start || i >= data->end)
1285 repodata_search(data, i - data->start, 0, repo_write_cb_needed, &cbdata);
1286 needid = cbdata.needid;
1290 cbdata.solvschemata[n] = addschema(&cbdata, cbdata.schema);
1293 if (repo->nextra && anyrepodataused)
1294 for (i = -1; i >= -repo->nextra; i--)
1297 dataiterator_init(&di, repo, i, 0, 0, SEARCH_EXTRA | SEARCH_NO_STORAGE_SOLVABLE);
1298 cbdata.sp = cbdata.schema;
1299 while (dataiterator_step(&di))
1300 repo_write_collect_needed(&cbdata, repo, di.data, di.key, &di.kv);
1302 cbdata.extraschemata[-1 - i] = addschema(&cbdata, cbdata.schema);
1305 /* If we have fileinfos to write, setup schemas and increment needid[]
1306 of the right strings. */
1307 for (i = 0; i < nsubfiles; i++)
1313 if (fileinfo[i].addedfileprovides || fileinfo[i].rpmdbcookie)
1315 /* extra info about this file */
1316 *sp++ = cbdata.keymap[REPODATA_INFO];
1317 if (fileinfo[i].addedfileprovides)
1319 *sp++ = cbdata.keymap[REPODATA_ADDEDFILEPROVIDES];
1320 for (j = 0; fileinfo[i].addedfileprovides[j]; j++)
1322 cbdata.mykeys[cbdata.keymap[REPODATA_ADDEDFILEPROVIDES]].size += j + 1;
1324 if (fileinfo[i].rpmdbcookie)
1325 *sp++ = cbdata.keymap[REPODATA_RPMDBCOOKIE];
1329 *sp++ = cbdata.keymap[REPODATA_EXTERNAL];
1330 *sp++ = cbdata.keymap[REPODATA_KEYS];
1331 if (fileinfo[i].location)
1332 *sp++ = cbdata.keymap[REPODATA_LOCATION];
1335 repodataschemata[i] = addschema(&cbdata, schema);
1336 cbdata.mykeys[cbdata.keymap[REPODATA_KEYS]].size += 2 * fileinfo[i].nkeys + 1;
1337 for (j = 1; j < fileinfo[i].nkeys; j++)
1339 needid[fileinfo[i].keys[j].type].need++;
1340 needid[fileinfo[i].keys[j].name].need++;
1344 /********************************************************************/
1346 /* remove unused keys, convert ids to local ids and increment their needid */
1347 keyused = sat_calloc(cbdata.nmykeys, sizeof(Id));
1348 for (i = 0; i < cbdata.myschemadatalen; i++)
1349 keyused[cbdata.myschemadata[i]] = 1;
1351 for (n = i = 1; i < cbdata.nmykeys; i++)
1357 cbdata.mykeys[n] = cbdata.mykeys[i];
1358 if (cbdata.mykeys[n].type == REPOKEY_TYPE_CONSTANTID)
1360 if (!type_constantid)
1361 type_constantid = poolusage > 1 ? stringpool_str2id(spool, id2str(repo->pool, cbdata.mykeys[n].type), 1) : REPOKEY_TYPE_CONSTANTID;
1363 cbdata.mykeys[n].size = stringpool_str2id(spool, id2str(repo->pool, cbdata.mykeys[n].size), 1);
1364 needid[cbdata.mykeys[n].size].need++;
1368 cbdata.mykeys[n].name = stringpool_str2id(spool, id2str(repo->pool, cbdata.mykeys[n].name), 1);
1369 cbdata.mykeys[n].type = stringpool_str2id(spool, id2str(repo->pool, cbdata.mykeys[n].type), 1);
1371 needid[cbdata.mykeys[n].name].need++;
1372 needid[cbdata.mykeys[n].type].need++;
1376 for (i = 0; i < cbdata.myschemadatalen; i++)
1377 cbdata.myschemadata[i] = keyused[cbdata.myschemadata[i]];
1378 for (i = 0; i < cbdata.nkeymap; i++)
1379 cbdata.keymap[i] = keyused[cbdata.keymap[i]];
1380 keyused = sat_free(keyused);
1382 /********************************************************************/
1384 /* increment need id for used dir components */
1385 if (cbdata.dirused && !cbdata.dirused[0])
1387 /* no dirs used at all */
1388 cbdata.dirused = sat_free(cbdata.dirused);
1393 for (i = 1; i < dirpool->ndirs; i++)
1396 fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
1398 id = dirpool->dirs[i];
1401 if (cbdata.dirused && !cbdata.dirused[i])
1403 if (cbdata.ownspool && dirpooldata && id > 1)
1405 id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1406 needid = cbdata.needid;
1412 reloff = needid[0].map;
1415 /********************************************************************/
1418 * create mapping table, new keys are sorted by needid[].need
1420 * needid[key].need : old key -> new key
1421 * needid[key].map : new key -> old key
1424 /* zero out id 0 and rel 0 just in case */
1427 needid[reloff].need = 0;
1429 for (i = 1; i < reloff + pool->nrels; i++)
1434 qsort(needid + 1, spool->nstrings - 1, sizeof(*needid), needid_cmp_need_s);
1436 /* make first entry '' */
1438 qsort(needid + 2, spool->nstrings - 2, sizeof(*needid), needid_cmp_need_s);
1440 qsort(needid + reloff, pool->nrels, sizeof(*needid), needid_cmp_need);
1443 for (i = 1; i < reloff; i++)
1445 if (!needid[i].need)
1448 sizeid += strlen(spool->stringspace + spool->strings[needid[i].map]) + 1;
1452 for (i = 1; i < nstrings; i++)
1453 needid[needid[i].map].need = i;
1455 for (i = 0; i < pool->nrels; i++)
1457 if (!needid[reloff + i].need)
1460 needid[reloff + i].need = 0;
1464 for (i = 0; i < nrels; i++)
1465 needid[needid[reloff + i].map].need = nstrings + i;
1468 /********************************************************************/
1470 /* create dir map */
1475 if (cbdata.dirused && !cbdata.dirused[1])
1476 cbdata.dirused[1] = 1; /* always want / entry */
1477 dirmap = sat_calloc(dirpool->ndirs, sizeof(Id));
1479 ndirmap = traverse_dirs(dirpool, dirmap, 1, dirpool_child(dirpool, 0), cbdata.dirused);
1480 if (!cbdata.dirused)
1481 cbdata.dirused = sat_malloc2(dirpool->ndirs, sizeof(Id));
1482 memset(cbdata.dirused, 0, dirpool->ndirs * sizeof(Id));
1483 for (i = 1; i < ndirmap; i++)
1487 cbdata.dirused[dirmap[i]] = i;
1488 id = dirpool->dirs[dirmap[i]];
1489 if (cbdata.ownspool && dirpooldata && id > 1)
1490 id = putinownpool(&cbdata, dirpooldata->localpool ? &dirpooldata->spool : &pool->ss, id);
1491 dirmap[i] = needid[id].need;
1495 /********************************************************************/
1496 cbdata.extdata = sat_calloc(cbdata.nmykeys, sizeof(struct extdata));
1498 cbdata.current_sub = 0;
1499 xd = cbdata.extdata;
1501 for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
1503 if (s->repo != repo)
1505 entrysize = xd->len;
1506 data_addid(xd, cbdata.solvschemata[n]);
1507 if (cbdata.keymap[SOLVABLE_NAME])
1508 data_addid(xd, needid[s->name].need);
1509 if (cbdata.keymap[SOLVABLE_ARCH])
1510 data_addid(xd, needid[s->arch].need);
1511 if (cbdata.keymap[SOLVABLE_EVR])
1512 data_addid(xd, needid[s->evr].need);
1513 if (s->vendor && cbdata.keymap[SOLVABLE_VENDOR])
1514 data_addid(xd, needid[s->vendor].need);
1515 if (s->provides && cbdata.keymap[SOLVABLE_PROVIDES])
1516 data_addidarray_sort(xd, pool, needid, idarraydata + s->provides, SOLVABLE_FILEMARKER);
1517 if (s->obsoletes && cbdata.keymap[SOLVABLE_OBSOLETES])
1518 data_addidarray_sort(xd, pool, needid, idarraydata + s->obsoletes, 0);
1519 if (s->conflicts && cbdata.keymap[SOLVABLE_CONFLICTS])
1520 data_addidarray_sort(xd, pool, needid, idarraydata + s->conflicts, 0);
1521 if (s->requires && cbdata.keymap[SOLVABLE_REQUIRES])
1522 data_addidarray_sort(xd, pool, needid, idarraydata + s->requires, SOLVABLE_PREREQMARKER);
1523 if (s->recommends && cbdata.keymap[SOLVABLE_RECOMMENDS])
1524 data_addidarray_sort(xd, pool, needid, idarraydata + s->recommends, 0);
1525 if (s->suggests && cbdata.keymap[SOLVABLE_SUGGESTS])
1526 data_addidarray_sort(xd, pool, needid, idarraydata + s->suggests, 0);
1527 if (s->supplements && cbdata.keymap[SOLVABLE_SUPPLEMENTS])
1528 data_addidarray_sort(xd, pool, needid, idarraydata + s->supplements, 0);
1529 if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
1530 data_addidarray_sort(xd, pool, needid, idarraydata + s->enhances, 0);
1531 if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
1532 data_addu32(xd, repo->rpmdbid[i - repo->start]);
1533 if (anyrepodataused)
1536 for (j = 0, data = repo->repodata; j < repo->nrepodata; j++, data++)
1538 if (!repodataused[j])
1540 if (i < data->start || i >= data->end)
1542 repodata_search(data, i - data->start, 0, repo_write_cb_adddata, &cbdata);
1545 entrysize = xd->len - entrysize;
1546 if (entrysize > maxentrysize)
1547 maxentrysize = entrysize;
1551 if (repo->nextra && anyrepodataused)
1552 for (i = -1; i >= -repo->nextra; i--)
1555 dataiterator_init(&di, repo, i, 0, 0, SEARCH_EXTRA | SEARCH_NO_STORAGE_SOLVABLE);
1556 entrysize = xd->len;
1557 data_addid(xd, cbdata.extraschemata[-1 - i]);
1559 while (dataiterator_step(&di))
1560 repo_write_adddata(&cbdata, di.data, di.key, &di.kv);
1561 entrysize = xd->len - entrysize;
1562 if (entrysize > maxentrysize)
1563 maxentrysize = entrysize;
1566 /********************************************************************/
1570 /* write file header */
1571 write_u32(fp, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
1572 write_u32(fp, repo->nextra ? SOLV_VERSION_7 : SOLV_VERSION_6);
1576 write_u32(fp, nstrings);
1577 write_u32(fp, nrels);
1578 write_u32(fp, ndirmap);
1579 write_u32(fp, repo->nsolvables);
1580 write_u32(fp, cbdata.nmykeys);
1581 write_u32(fp, cbdata.nmyschemata);
1582 write_u32(fp, nsubfiles); /* info blocks. */
1585 write_u32(fp, repo->nextra);
1586 write_u32(fp, SOLV_CONTENT_VERSION);
1589 solv_flags |= SOLV_FLAG_PREFIX_POOL;
1590 write_u32(fp, solv_flags);
1592 /* Build the prefix-encoding of the string pool. We need to know
1593 the size of that before writing it to the file, so we have to
1594 build a separate buffer for that. As it's temporarily possible
1595 that this actually is an expansion we can't easily reuse the
1596 stringspace for this. The max expansion per string is 1 byte,
1597 so it will fit into sizeid+nstrings bytes. */
1598 char *prefix = sat_malloc(sizeid + nstrings);
1601 for (i = 1; i < nstrings; i++)
1603 char *str = spool->stringspace + spool->strings[needid[i].map];
1606 for (same = 0; same < 255; same++)
1607 if (!old_str[same] || !str[same] || old_str[same] != str[same])
1610 len = strlen(str + same) + 1;
1611 memcpy (pp, str + same, len);
1619 write_u32(fp, sizeid);
1620 write_u32(fp, pp - prefix);
1623 if (fwrite(prefix, pp - prefix, 1, fp) != 1)
1625 perror("write error prefix");
1634 for (i = 0; i < nrels; i++)
1636 ran = pool->rels + (needid[reloff + i].map - reloff);
1637 write_id(fp, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
1638 write_id(fp, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
1639 write_u8(fp, ran->flags);
1643 * write dirs (skip both root and / entry)
1645 for (i = 2; i < ndirmap; i++)
1648 write_id(fp, dirmap[i]);
1650 write_id(fp, nstrings - dirmap[i]);
1659 fileinfo->nkeys = cbdata.nmykeys;
1660 fileinfo->keys = sat_calloc(fileinfo->nkeys, sizeof (*fileinfo->keys));
1662 for (i = 1; i < cbdata.nmykeys; i++)
1664 write_id(fp, needid[cbdata.mykeys[i].name].need);
1665 write_id(fp, needid[cbdata.mykeys[i].type].need);
1666 if (cbdata.mykeys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
1668 if (cbdata.mykeys[i].type == type_constantid)
1669 write_id(fp, needid[cbdata.mykeys[i].size].need);
1671 write_id(fp, cbdata.mykeys[i].size);
1674 write_id(fp, cbdata.extdata[i].len);
1675 write_id(fp, cbdata.mykeys[i].storage);
1677 fileinfo->keys[i] = cbdata.mykeys[i];
1683 write_id(fp, cbdata.myschemadatalen);
1684 if (cbdata.nmyschemata)
1686 for (i = 1; i < cbdata.nmyschemata; i++)
1687 write_idarray(fp, pool, 0, cbdata.myschemadata + cbdata.myschemata[i]);
1701 for (i = 0; i < nsubfiles; i++)
1706 data_addid(&xd, repodataschemata[i]);
1707 if (fileinfo[i].addedfileprovides || fileinfo[i].rpmdbcookie)
1709 if (fileinfo[i].addedfileprovides)
1710 data_addidarray_sort(&xd, pool, needid, fileinfo[i].addedfileprovides, 0);
1711 if (fileinfo[i].rpmdbcookie)
1712 data_addblob(&xd, fileinfo[i].rpmdbcookie, 32);
1716 /* key,type array + location, write idarray */
1717 for (j = 1; j < fileinfo[i].nkeys; j++)
1719 data_addideof(&xd, needid[fileinfo[i].keys[j].name].need, 0);
1720 data_addideof(&xd, needid[fileinfo[i].keys[j].type].need, j == fileinfo[i].nkeys - 1);
1722 if (fileinfo[i].location)
1723 data_addblob(&xd, (unsigned char *)fileinfo[i].location, strlen(fileinfo[i].location) + 1);
1730 write_id(fp, xd.len);
1731 write_blob(fp, xd.buf, xd.len);
1735 /********************************************************************/
1739 * write Solvable data
1741 if (repo->nsolvables || repo->nextra)
1743 write_id(fp, maxentrysize);
1744 write_id(fp, cbdata.extdata[0].len);
1745 write_blob(fp, cbdata.extdata[0].buf, cbdata.extdata[0].len);
1747 sat_free(cbdata.extdata[0].buf);
1749 /* write vertical data */
1750 for (i = 1; i < cbdata.nmykeys; i++)
1751 if (cbdata.extdata[i].len)
1753 if (i < cbdata.nmykeys)
1755 unsigned char *dp, vpage[BLOB_PAGESIZE];
1756 int l, ll, lpage = 0;
1758 write_u32(fp, BLOB_PAGESIZE);
1759 for (i = 1; i < cbdata.nmykeys; i++)
1761 if (!cbdata.extdata[i].len)
1763 l = cbdata.extdata[i].len;
1764 dp = cbdata.extdata[i].buf;
1767 ll = BLOB_PAGESIZE - lpage;
1770 memcpy(vpage + lpage, dp, ll);
1774 if (lpage == BLOB_PAGESIZE)
1776 write_compressed_page(fp, vpage, lpage);
1782 write_compressed_page(fp, vpage, lpage);
1786 /* write vertical_offset entries */
1787 write_u32(fp, 0); /* no paging */
1788 for (i = 1; i < cbdata.nmykeys; i++)
1789 if (cbdata.extdata[i].len)
1790 write_blob(fp, cbdata.extdata[i].buf, cbdata.extdata[i].len);
1793 /* Fill fileinfo for our caller. */
1796 fileinfo->checksum = 0;
1797 fileinfo->nchecksum = 0;
1798 fileinfo->checksumtype = 0;
1799 fileinfo->location = 0;
1802 for (i = 1; i < cbdata.nmykeys; i++)
1803 sat_free(cbdata.extdata[i].buf);
1804 sat_free(cbdata.extdata);
1806 if (cbdata.ownspool)
1808 sat_free(cbdata.ownspool->strings);
1809 sat_free(cbdata.ownspool->stringspace);
1810 sat_free(cbdata.ownspool->stringhashtbl);
1812 if (cbdata.owndirpool)
1814 sat_free(cbdata.owndirpool->dirs);
1815 sat_free(cbdata.owndirpool->dirtraverse);
1818 sat_free(cbdata.extraschemata);
1819 sat_free(cbdata.solvschemata);
1820 sat_free(cbdata.myschemadata);
1821 sat_free(cbdata.myschemata);
1822 sat_free(cbdata.schema);
1824 sat_free(cbdata.mykeys);
1825 sat_free(cbdata.keymap);
1826 sat_free(cbdata.keymapstart);
1827 sat_free(cbdata.dirused);
1828 sat_free(repodataused);
1829 sat_free(repodataschemata);