use less memory when just writing the filelist
[platform/upstream/libsolv.git] / src / repo_write.c
index 1e679b8..11002b4 100644 (file)
@@ -7,12 +7,12 @@
 
 /*
  * repo_write.c
- * 
+ *
  * Write Repo data out to a file in solv format
- * 
- * See doc/README.format for a description 
+ *
+ * See doc/README.format for a description
  * of the binary file format
- * 
+ *
  */
 
 #include <sys/types.h>
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+#include <errno.h>
 
 #include "pool.h"
 #include "util.h"
@@ -43,9 +44,9 @@ typedef struct needid {
  * increment need Id
  * idarray: array of Ids, ID_NULL terminated
  * needid: array of Id->NeedId
- * 
+ *
  * return size of array (including trailing zero)
- * 
+ *
  */
 
 static void
@@ -92,7 +93,7 @@ incneedidarray(Pool *pool, Id *idarray, NeedId *needid)
 
 
 /*
- * 
+ *
  */
 
 static int
@@ -136,15 +137,17 @@ needid_cmp_need_s(const void *ap, const void *bp, void *dp)
  */
 
 static void
-write_u32(FILE *fp, unsigned int x)
+write_u32(Repodata *data, unsigned int x)
 {
+  FILE *fp = data->fp;
+  if (data->error)
+    return;
   if (putc(x >> 24, fp) == EOF ||
       putc(x >> 16, fp) == EOF ||
       putc(x >> 8, fp) == EOF ||
       putc(x, fp) == EOF)
     {
-      perror("write error u32");
-      exit(1);
+      data->error = pool_error(data->repo->pool, -1, "write error u32: %s", strerror(errno));
     }
 }
 
@@ -154,12 +157,13 @@ write_u32(FILE *fp, unsigned int x)
  */
 
 static void
-write_u8(FILE *fp, unsigned int x)
+write_u8(Repodata *data, unsigned int x)
 {
-  if (putc(x, fp) == EOF)
+  if (data->error)
+    return;
+  if (putc(x, data->fp) == EOF)
     {
-      perror("write error u8");
-      exit(1);
+      data->error = pool_error(data->repo->pool, -1, "write error u8: %s", strerror(errno));
     }
 }
 
@@ -168,12 +172,13 @@ write_u8(FILE *fp, unsigned int x)
  */
 
 static void
-write_blob(FILE *fp, void *data, int len)
+write_blob(Repodata *data, void *blob, int len)
 {
-  if (len && fwrite(data, len, 1, fp) != 1)
+  if (data->error)
+    return;
+  if (len && fwrite(blob, len, 1, data->fp) != 1)
     {
-      perror("write error blob");
-      exit(1);
+      data->error = pool_error(data->repo->pool, -1, "write error blob: %s", strerror(errno));
     }
 }
 
@@ -182,8 +187,11 @@ write_blob(FILE *fp, void *data, int len)
  */
 
 static void
-write_id(FILE *fp, Id x)
+write_id(Repodata *data, Id x)
 {
+  FILE *fp = data->fp;
+  if (data->error)
+    return;
   if (x >= (1 << 14))
     {
       if (x >= (1 << 28))
@@ -196,28 +204,28 @@ write_id(FILE *fp, Id x)
     putc((x >> 7) | 128, fp);
   if (putc(x & 127, fp) == EOF)
     {
-      perror("write error id");
-      exit(1);
+      data->error = pool_error(data->repo->pool, -1, "write error id: %s", strerror(errno));
     }
 }
 
 static inline void
-write_id_eof(FILE *fp, Id x, int eof)
+write_id_eof(Repodata *data, Id x, int eof)
 {
   if (x >= 64)
     x = (x & 63) | ((x & ~63) << 1);
-  write_id(fp, x | (eof ? 0 : 64));
+  write_id(data, x | (eof ? 0 : 64));
 }
 
 
 
 static inline void
-write_str(FILE *fp, const char *str)
+write_str(Repodata *data, const char *str)
 {
-  if (fputs(str, fp) == EOF || putc(0, fp) == EOF)
+  if (data->error)
+    return;
+  if (fputs(str, data->fp) == EOF || putc(0, data->fp) == EOF)
     {
-      perror("write error str");
-      exit(1);
+      data->error = pool_error(data->repo->pool, -1, "write error str: %s", strerror(errno));
     }
 }
 
@@ -226,14 +234,14 @@ write_str(FILE *fp, const char *str)
  */
 
 static void
-write_idarray(FILE *fp, Pool *pool, NeedId *needid, Id *ids)
+write_idarray(Repodata *data, Pool *pool, NeedId *needid, Id *ids)
 {
   Id id;
   if (!ids)
     return;
   if (!*ids)
     {
-      write_u8(fp, 0);
+      write_u8(data, 0);
       return;
     }
   for (;;)
@@ -245,10 +253,10 @@ write_idarray(FILE *fp, Pool *pool, NeedId *needid, Id *ids)
        id = (id & 63) | ((id & ~63) << 1);
       if (!*ids)
        {
-         write_id(fp, id);
+         write_id(data, id);
          return;
        }
-      write_id(fp, id | 64);
+      write_id(data, id | 64);
     }
 }
 
@@ -262,7 +270,7 @@ cmp_ids(const void *pa, const void *pb, void *dp)
 
 #if 0
 static void
-write_idarray_sort(FILE *fp, Pool *pool, NeedId *needid, Id *ids, Id marker)
+write_idarray_sort(Repodata *data, Pool *pool, NeedId *needid, Id *ids, Id marker)
 {
   int len, i;
   Id lids[64], *sids;
@@ -271,7 +279,7 @@ write_idarray_sort(FILE *fp, Pool *pool, NeedId *needid, Id *ids, Id marker)
     return;
   if (!*ids)
     {
-      write_u8(fp, 0);
+      write_u8(data, 0);
       return;
     }
   for (len = 0; len < 64 && ids[len]; len++)
@@ -336,7 +344,7 @@ write_idarray_sort(FILE *fp, Pool *pool, NeedId *needid, Id *ids, Id marker)
         we might want to skip writing them out.  */
       if (id >= 64)
        id = (id & 63) | ((id & ~63) << 1);
-      write_id(fp, id | 64);
+      write_id(data, id | 64);
     }
   id = sids[i];
   if (id == marker)
@@ -345,7 +353,7 @@ write_idarray_sort(FILE *fp, Pool *pool, NeedId *needid, Id *ids, Id marker)
     id = id - old + 1;
   if (id >= 64)
     id = (id & 63) | ((id & ~63) << 1);
-  write_id(fp, id);
+  write_id(data, id);
   if (sids != lids)
     solv_free(sids);
 }
@@ -389,6 +397,7 @@ struct cbdata {
   Id lastlen;
 
   int doingsolvables;  /* working on solvables data */
+  int filelistmode;
 };
 
 #define NEEDED_BLOCK 1023
@@ -397,9 +406,11 @@ struct cbdata {
 #define EXTDATA_BLOCK 4095
 
 static inline void
-data_addid(struct extdata *xd, Id x)
+data_addid(struct extdata *xd, Id sx)
 {
+  unsigned int x = (unsigned int)sx;
   unsigned char *dp;
+
   xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
   dp = xd->buf + xd->len;
 
@@ -418,11 +429,57 @@ data_addid(struct extdata *xd, Id x)
 }
 
 static inline void
-data_addideof(struct extdata *xd, Id x, int eof)
+data_addideof(struct extdata *xd, Id sx, int eof)
 {
-  if (x >= 64)
-    x = (x & 63) | ((x & ~63) << 1);
-  data_addid(xd, (eof ? x: x | 64));
+  unsigned int x = (unsigned int)sx;
+  unsigned char *dp;
+
+  xd->buf = solv_extend(xd->buf, xd->len, 5, 1, EXTDATA_BLOCK);
+  dp = xd->buf + xd->len;
+
+  if (x >= (1 << 13))
+    {
+      if (x >= (1 << 27))
+        *dp++ = (x >> 27) | 128;
+      if (x >= (1 << 20))
+        *dp++ = (x >> 20) | 128;
+      *dp++ = (x >> 13) | 128;
+    }
+  if (x >= (1 << 6))
+    *dp++ = (x >> 6) | 128;
+  *dp++ = eof ? (x & 63) : (x & 63) | 64;
+  xd->len = dp - xd->buf;
+}
+
+static inline int
+data_addideof_len(Id sx)
+{
+  unsigned int x = (unsigned int)sx;
+  if (x >= (1 << 13))
+    {
+      if (x >= (1 << 27))
+       return 5;
+      return x >= (1 << 20) ? 4 : 3;
+    }
+  return x >= (1 << 6) ? 2 : 1;
+}
+
+static void
+data_addid64(struct extdata *xd, unsigned int x, unsigned int hx)
+{
+  if (hx)
+    {
+      if (hx > 7)
+        {
+          data_addid(xd, (Id)(hx >> 3));
+          xd->buf[xd->len - 1] |= 128;
+         hx &= 7;
+        }
+      data_addid(xd, (Id)(x | 0x80000000));
+      xd->buf[xd->len - 5] = (x >> 28) | (hx << 4) | 128;
+    }
+  else
+    data_addid(xd, (Id)x);
 }
 
 static void
@@ -499,18 +556,14 @@ data_addidarray_sort(struct extdata *xd, Pool *pool, NeedId *needid, Id *ids, Id
        }
       /* XXX If difference is zero we have multiple equal elements,
         we might want to skip writing them out.  */
-      if (id >= 64)
-       id = (id & 63) | ((id & ~63) << 1);
-      data_addid(xd, id | 64);
+      data_addideof(xd, id, 0);
     }
   id = sids[i];
   if (id == marker)
     id = 0;
   else
     id = id - old + 1;
-  if (id >= 64)
-    id = (id & 63) | ((id & ~63) << 1);
-  data_addid(xd, id);
+  data_addideof(xd, id, 1);
   if (sids != lids)
     solv_free(sids);
 }
@@ -642,8 +695,8 @@ repo_write_collect_needed(struct cbdata *cbdata, Repo *repo, Repodata *data, Rep
          {
            if (cbdata->oldschema)
              {
-               fprintf(stderr, "nested structs not yet implemented\n");
-               exit(1);
+               cbdata->target->error = pool_error(cbdata->repo->pool, -1, "nested fixarray structs not yet implemented");
+               return SEARCH_NEXT_KEY;
              }
            cbdata->oldschema = cbdata->schema;
            cbdata->oldsp = cbdata->sp;
@@ -733,7 +786,7 @@ repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue
   rm = cbdata->keymap[cbdata->keymapstart[data->repodataid] + (key - data->keys)];
   if (!rm)
     return SEARCH_NEXT_KEY;    /* we do not want this one */
-  
+
   if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET)
     {
       xd = cbdata->extdata + rm;       /* vertical buffer */
@@ -773,9 +826,18 @@ repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue
       case REPOKEY_TYPE_SHA1:
        data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA1);
        break;
+      case REPOKEY_TYPE_SHA224:
+       data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA224);
+       break;
       case REPOKEY_TYPE_SHA256:
        data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA256);
        break;
+      case REPOKEY_TYPE_SHA384:
+       data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA384);
+       break;
+      case REPOKEY_TYPE_SHA512:
+       data_addblob(xd, (unsigned char *)kv->str, SIZEOF_SHA512);
+       break;
       case REPOKEY_TYPE_U32:
        u32 = kv->num;
        v[0] = u32 >> 24;
@@ -785,7 +847,7 @@ repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue
        data_addblob(xd, v, 4);
        break;
       case REPOKEY_TYPE_NUM:
-       data_addid(xd, kv->num);
+       data_addid64(xd, kv->num, kv->num2);
        break;
       case REPOKEY_TYPE_DIR:
        id = kv->id;
@@ -813,8 +875,15 @@ repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue
        if (cbdata->owndirpool)
          id = putinowndirpool(cbdata, data, &data->dirpool, id);
        id = cbdata->dirused[id];
+       if (cbdata->filelistmode > 0)
+         {
+           xd->len += data_addideof_len(id) + strlen(kv->str) + 1;
+           break;
+         }
        data_addideof(xd, id, kv->eof);
        data_addblob(xd, (unsigned char *)kv->str, strlen(kv->str) + 1);
+       if (cbdata->filelistmode < 0)
+         return 0;
        break;
       case REPOKEY_TYPE_FIXARRAY:
        if (kv->eof == 0)
@@ -846,8 +915,8 @@ repo_write_adddata(struct cbdata *cbdata, Repodata *data, Repokey *key, KeyValue
          }
        break;
       default:
-       fprintf(stderr, "unknown type for %d: %d\n", key->name, key->type);
-       exit(1);
+       cbdata->target->error = pool_error(cbdata->repo->pool, -1, "unknown type for %d: %d\n", key->name, key->type);
+       break;
     }
   if (cbdata->target->keys[rm].storage == KEY_STORAGE_VERTICAL_OFFSET && kv->eof)
     {
@@ -905,7 +974,7 @@ traverse_dirs(Dirpool *dp, Id *dirmap, Id n, Id dir, Id *used)
 }
 
 static void
-write_compressed_page(FILE *fp, unsigned char *page, int len)
+write_compressed_page(Repodata *data, unsigned char *page, int len)
 {
   int clen;
   unsigned char cpage[REPOPAGE_BLOBSIZE];
@@ -913,13 +982,13 @@ write_compressed_page(FILE *fp, unsigned char *page, int len)
   clen = repopagestore_compress_page(page, len, cpage, len - 1);
   if (!clen)
     {
-      write_u32(fp, len * 2);
-      write_blob(fp, page, len);
+      write_u32(data, len * 2);
+      write_blob(data, page, len);
     }
   else
     {
-      write_u32(fp, clen * 2 + 1);
-      write_blob(fp, cpage, clen);
+      write_u32(data, clen * 2 + 1);
+      write_blob(data, cpage, clen);
     }
 }
 
@@ -931,6 +1000,14 @@ static Id verticals[] = {
   SOLVABLE_EULA,
   SOLVABLE_DISKUSAGE,
   SOLVABLE_FILELIST,
+  SOLVABLE_CHECKSUM,
+  DELTA_CHECKSUM,
+  DELTA_SEQ_NUM,
+  SOLVABLE_PKGID,
+  SOLVABLE_HDRID,
+  SOLVABLE_LEADSIGID,
+  SOLVABLE_CHANGELOG_AUTHOR,
+  SOLVABLE_CHANGELOG_TEXT,
   0
 };
 
@@ -960,6 +1037,47 @@ repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata)
 }
 
 /*
+ * return true if the repodata contains the filelist (and just
+ * the filelist). The same code is used in the dataiterator. The way
+ * it is used is completely wrong, of course, as having the filelist
+ * key does not mean it is used for a specific solvable. Nevertheless
+ * it is better to have it than to write broken solv files.
+ */
+static inline int
+is_filelist_extension(Repodata *data)
+{
+  int j;
+  for (j = 1; j < data->nkeys; j++)
+    if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
+      return 0;
+  return 1;
+}
+
+
+static int
+write_compressed_extdata(Repodata *target, struct extdata *xd, unsigned char *vpage, int lpage)
+{
+  unsigned char *dp = xd->buf;
+  int l = xd->len;
+  while (l)
+    {
+      int ll = REPOPAGE_BLOBSIZE - lpage;
+      if (l < ll)
+       ll = l;
+      memcpy(vpage + lpage, dp, ll);
+      dp += ll;
+      lpage += ll;
+      l -= ll;
+      if (lpage == REPOPAGE_BLOBSIZE)
+       {
+         write_compressed_page(target, vpage, lpage);
+         lpage = 0;
+       }
+    }
+  return lpage;
+}
+
+/*
  * Repo
  */
 
@@ -975,10 +1093,10 @@ repo_write_stdkeyfilter(Repo *repo, Repokey *key, void *kfdata)
  * 5) write everything to disk
  */
 int
-repo_write(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Id **keyarrayp)
+repo_write_filtered(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
 {
   Pool *pool = repo->pool;
-  int i, j, n;
+  int i, j, n, lastfilelistn;
   Solvable *s;
   NeedId *needid;
   int nstrings, nrels;
@@ -995,7 +1113,7 @@ repo_write(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void
   unsigned char *repodataused;
   int anyrepodataused = 0;
   int anysolvableused = 0;
-  
+
   struct cbdata cbdata;
   int clonepool;
   Repokey *key;
@@ -1015,10 +1133,6 @@ repo_write(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void
 
   Id type_constantid = REPOKEY_TYPE_CONSTANTID;
 
-  unsigned char *prefixcomp;
-  unsigned int compsum;
-  char *old_str;
-
 
   memset(&cbdata, 0, sizeof(cbdata));
   cbdata.repo = repo;
@@ -1053,7 +1167,7 @@ repo_write(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void
       else if (i < RPM_RPMDBID)
         keyd.type = REPOKEY_TYPE_REL_IDARRAY;
       else
-        keyd.type = REPOKEY_TYPE_U32;
+        keyd.type = REPOKEY_TYPE_NUM;
       keyd.size = 0;
       keyd.storage = KEY_STORAGE_SOLVABLE;
       if (keyfilter)
@@ -1084,6 +1198,7 @@ repo_write(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void
   dirpool = 0;
   dirpooldata = 0;
   n = ID_NUM_INTERNAL;
+  lastfilelistn = 0;
   FOR_REPODATAS(repo, i, data)
     {
       cbdata.keymapstart[i] = n;
@@ -1174,6 +1289,20 @@ repo_write(Repo *repo, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void
              idused = 1;       /* dirs also use ids */
              dirused = 1;
            }
+         if (key->type == REPOKEY_TYPE_DIRSTRARRAY && key->name == SOLVABLE_FILELIST)
+           {
+             /* is this a file list extension */
+             if (is_filelist_extension(data))
+               {
+                 /* hmm, we have a file list extension. Kill filelist of other repodata.
+                  * XXX: this is wrong, as the extension does not need to cover all
+                  * solvables of the other repodata */
+                 if (lastfilelistn)
+                   cbdata.keymap[lastfilelistn] = 0;
+               }
+             else
+               lastfilelistn = n;
+           }
        }
       if (idused)
        {
@@ -1250,14 +1379,11 @@ for (i = 1; i < target.nkeys; i++)
 #endif
 
   /* copy keys if requested */
-  if (keyarrayp)
+  if (keyq)
     {
-      *keyarrayp = solv_calloc(2 * target.nkeys + 1, sizeof(Id));
+      queue_empty(keyq);
       for (i = 1; i < target.nkeys; i++)
-       {
-          (*keyarrayp)[2 * i - 2] = target.keys[i].name;
-          (*keyarrayp)[2 * i - 1] = target.keys[i].type;
-       }
+       queue_push2(keyq, target.keys[i].name, target.keys[i].type);
     }
 
   if (poolusage > 1)
@@ -1453,21 +1579,17 @@ for (i = 1; i < target.nkeys; i++)
       if (i != n)
        {
          target.keys[n] = target.keys[i];
-         if (keyarrayp)
+         if (keyq)
            {
-             (*keyarrayp)[2 * n - 2] = (*keyarrayp)[2 * i - 2];
-             (*keyarrayp)[2 * n - 1] = (*keyarrayp)[2 * i - 1];
+             keyq->elements[2 * n - 2] = keyq->elements[2 * i - 2];
+             keyq->elements[2 * n - 1] = keyq->elements[2 * i - 1];
            }
        }
       n++;
     }
   target.nkeys = n;
-  if (keyarrayp)
-    {
-      /* terminate array */
-      (*keyarrayp)[2 * n - 2] = 0;
-      (*keyarrayp)[2 * n - 1] = 0;
-    }
+  if (keyq)
+    queue_truncate(keyq, 2 * n - 2);
 
   /* update schema data to the new key ids */
   for (i = 1; i < target.schemadatalen; i++)
@@ -1654,6 +1776,19 @@ fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
     {
       data_addid(xd, repo->nsolvables);        /* FLEXARRAY nentries */
       cbdata.doingsolvables = 1;
+
+      /* check if we can do the special filelist memory optimization */
+      if (anyrepodataused)
+       {
+         for (i = 1; i < target.nkeys; i++)
+           if (target.keys[i].storage == KEY_STORAGE_VERTICAL_OFFSET)
+             cbdata.filelistmode |= cbdata.filelistmode == 0 && target.keys[i].type == REPOKEY_TYPE_DIRSTRARRAY ? 1 : 2;
+           else if (target.keys[i].type == REPOKEY_TYPE_DIRSTRARRAY)
+             cbdata.filelistmode = 2;
+         if (cbdata.filelistmode != 1)
+           cbdata.filelistmode = 0;
+       }
+
       for (i = repo->start, s = pool->solvables + i, n = 0; i < repo->end; i++, s++)
        {
          if (s->repo != repo)
@@ -1684,7 +1819,7 @@ fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
          if (s->enhances && cbdata.keymap[SOLVABLE_ENHANCES])
            data_addidarray_sort(xd, pool, needid, idarraydata + s->enhances, 0);
          if (repo->rpmdbid && cbdata.keymap[RPM_RPMDBID])
-           data_addu32(xd, repo->rpmdbid[i - repo->start]);
+           data_addid(xd, repo->rpmdbid[i - repo->start]);
          if (anyrepodataused)
            {
              cbdata.vstart = -1;
@@ -1714,101 +1849,68 @@ fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
 
 /********************************************************************/
 
+  target.fp = fp;
+
   /* write header */
 
   /* write file header */
-  write_u32(fp, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
-  write_u32(fp, SOLV_VERSION_8);
+  write_u32(&target, 'S' << 24 | 'O' << 16 | 'L' << 8 | 'V');
+  write_u32(&target, SOLV_VERSION_8);
 
 
   /* write counts */
-  write_u32(fp, nstrings);
-  write_u32(fp, nrels);
-  write_u32(fp, ndirmap);
-  write_u32(fp, anysolvableused ? repo->nsolvables : 0);
-  write_u32(fp, target.nkeys);
-  write_u32(fp, target.nschemata);
+  write_u32(&target, nstrings);
+  write_u32(&target, nrels);
+  write_u32(&target, ndirmap);
+  write_u32(&target, anysolvableused ? repo->nsolvables : 0);
+  write_u32(&target, target.nkeys);
+  write_u32(&target, target.nschemata);
   solv_flags = 0;
   solv_flags |= SOLV_FLAG_PREFIX_POOL;
-  write_u32(fp, solv_flags);
+  solv_flags |= SOLV_FLAG_SIZE_BYTES;
+  write_u32(&target, solv_flags);
 
-  /*
-   * calculate prefix encoding of the strings
-   */
-  prefixcomp = solv_malloc(nstrings);
-  compsum = 0;
-  old_str = "";
-  
-  prefixcomp[0] = 0;
-  for (i = 1; i < nstrings; i++)
-    {
-      char *str = spool->stringspace + spool->strings[needid[i].map];
-      int same;
-      for (same = 0; same < 255; same++)
-       if (!old_str[same] || old_str[same] != str[same])
-         break;
-      prefixcomp[i] = same;
-      compsum += same;
-      old_str = str;
-    }
-
-  /*
-   * write strings
-   */
-  write_u32(fp, sizeid);
-  /* we save compsum bytes but need 1 extra byte for every string */
-  write_u32(fp, sizeid + (nstrings ? nstrings - 1 : 0) - compsum);
-  if (sizeid + (nstrings ? nstrings - 1 : 0) != compsum)
+  if (nstrings)
     {
+      /*
+       * calculate prefix encoding of the strings
+       */
+      unsigned char *prefixcomp = solv_malloc(nstrings);
+      unsigned int compsum = 0;
+      char *old_str = "";
+
+      prefixcomp[0] = 0;
       for (i = 1; i < nstrings; i++)
        {
          char *str = spool->stringspace + spool->strings[needid[i].map];
-         write_u8(fp, prefixcomp[i]);
-         write_str(fp, str + prefixcomp[i]);
+         int same;
+         for (same = 0; same < 255; same++)
+           if (!old_str[same] || old_str[same] != str[same])
+             break;
+         prefixcomp[i] = same;
+         compsum += same;
+         old_str = str;
        }
-    }
-  solv_free(prefixcomp);
-
-#if 0
-  /* Build the prefix-encoding of the string pool.  We need to know
-     the size of that before writing it to the file, so we have to
-     build a separate buffer for that.  As it's temporarily possible
-     that this actually is an expansion we can't easily reuse the 
-     stringspace for this.  The max expansion per string is 1 byte,
-     so it will fit into sizeid+nstrings bytes.  */
-  char *prefix = solv_malloc(sizeid + nstrings);
-  char *pp = prefix;
-  char *old_str = "";
-  for (i = 1; i < nstrings; i++)
-    {
-      char *str = spool->stringspace + spool->strings[needid[i].map];
-      int same;
-      size_t len;
-      for (same = 0; same < 255; same++)
-       if (!old_str[same] || !str[same] || old_str[same] != str[same])
-         break;
-      *pp++ = same;
-      len = strlen(str + same) + 1;
-      memcpy(pp, str + same, len);
-      pp += len;
-      old_str = str;
-    }
 
-  /*
-   * write strings
-   */
-  write_u32(fp, sizeid);
-  write_u32(fp, pp - prefix);
-  if (pp != prefix)
-    {
-      if (fwrite(prefix, pp - prefix, 1, fp) != 1)
+      /*
+       * write strings
+       */
+      write_u32(&target, sizeid);
+      /* we save compsum bytes but need 1 extra byte for every string */
+      write_u32(&target, sizeid + nstrings - 1 - compsum);
+      for (i = 1; i < nstrings; i++)
        {
-         perror("write error prefix");
-         exit(1);
+         char *str = spool->stringspace + spool->strings[needid[i].map];
+         write_u8(&target, prefixcomp[i]);
+         write_str(&target, str + prefixcomp[i]);
        }
+      solv_free(prefixcomp);
+    }
+  else
+    {
+      write_u32(&target, 0);
+      write_u32(&target, 0);
     }
-  solv_free(prefix);
-#endif
 
   /*
    * write RelDeps
@@ -1816,9 +1918,9 @@ fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
   for (i = 0; i < nrels; i++)
     {
       ran = pool->rels + (needid[reloff + i].map - reloff);
-      write_id(fp, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
-      write_id(fp, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
-      write_u8(fp, ran->flags);
+      write_id(&target, needid[ISRELDEP(ran->name) ? RELOFF(ran->name) : ran->name].need);
+      write_id(&target, needid[ISRELDEP(ran->evr) ? RELOFF(ran->evr) : ran->evr].need);
+      write_u8(&target, ran->flags);
     }
 
   /*
@@ -1827,9 +1929,9 @@ fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
   for (i = 2; i < ndirmap; i++)
     {
       if (dirmap[i] > 0)
-        write_id(fp, dirmap[i]);
+        write_id(&target, dirmap[i]);
       else
-        write_id(fp, nstrings - dirmap[i]);
+        write_id(&target, nstrings - dirmap[i]);
     }
   solv_free(dirmap);
 
@@ -1838,33 +1940,33 @@ fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
    */
   for (i = 1; i < target.nkeys; i++)
     {
-      write_id(fp, needid[target.keys[i].name].need);
-      write_id(fp, needid[target.keys[i].type].need);
+      write_id(&target, needid[target.keys[i].name].need);
+      write_id(&target, needid[target.keys[i].type].need);
       if (target.keys[i].storage != KEY_STORAGE_VERTICAL_OFFSET)
        {
          if (target.keys[i].type == type_constantid)
-            write_id(fp, needid[target.keys[i].size].need);
+            write_id(&target, needid[target.keys[i].size].need);
          else
-            write_id(fp, target.keys[i].size);
+            write_id(&target, target.keys[i].size);
        }
       else
-        write_id(fp, cbdata.extdata[i].len);
-      write_id(fp, target.keys[i].storage);
+        write_id(&target, cbdata.extdata[i].len);
+      write_id(&target, target.keys[i].storage);
     }
 
   /*
    * write schemata
    */
-  write_id(fp, target.schemadatalen);  /* XXX -1? */
+  write_id(&target, target.schemadatalen);     /* XXX -1? */
   for (i = 1; i < target.nschemata; i++)
-    write_idarray(fp, pool, 0, repodata_id2schema(&target, i));
+    write_idarray(&target, pool, 0, repodata_id2schema(&target, i));
 
 /********************************************************************/
 
-  write_id(fp, cbdata.maxdata);
-  write_id(fp, cbdata.extdata[0].len);
+  write_id(&target, cbdata.maxdata);
+  write_id(&target, cbdata.extdata[0].len);
   if (cbdata.extdata[0].len)
-    write_blob(fp, cbdata.extdata[0].buf, cbdata.extdata[0].len);
+    write_blob(&target, cbdata.extdata[0].buf, cbdata.extdata[0].len);
   solv_free(cbdata.extdata[0].buf);
 
   /* do we have vertical data? */
@@ -1874,40 +1976,56 @@ fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
   if (i < target.nkeys)
     {
       /* yes, write it in pages */
-      unsigned char *dp, vpage[REPOPAGE_BLOBSIZE];
-      int l, ll, lpage = 0;
+      unsigned char vpage[REPOPAGE_BLOBSIZE];
+      int lpage = 0;
 
-      write_u32(fp, REPOPAGE_BLOBSIZE);
+      write_u32(&target, REPOPAGE_BLOBSIZE);
       for (i = 1; i < target.nkeys; i++)
+       if (cbdata.extdata[i].len)
+         {
+           if (cbdata.filelistmode)
+             break;
+           lpage = write_compressed_extdata(&target, cbdata.extdata + i, vpage, lpage);
+         }
+      if (cbdata.filelistmode && i < target.nkeys)
        {
-         if (!cbdata.extdata[i].len)
-           continue;
-         l = cbdata.extdata[i].len;
-         dp = cbdata.extdata[i].buf;
-         while (l)
+         /* ok, just this single extdata, which is a filelist */
+         xd = cbdata.extdata + i;
+         xd->len = 0;
+         cbdata.filelistmode = -1;
+         for (j = 0; j < cbdata.nkeymap; j++)
+           if (cbdata.keymap[j] != i)
+             cbdata.keymap[j] = 0;
+         for (i = repo->start, s = pool->solvables + i; i < repo->end; i++, s++)
            {
-             ll = REPOPAGE_BLOBSIZE - lpage;
-             if (l < ll)
-               ll = l;
-             memcpy(vpage + lpage, dp, ll);
-             dp += ll;
-             lpage += ll;
-             l -= ll;
-             if (lpage == REPOPAGE_BLOBSIZE)
+             if (s->repo != repo)
+               continue;
+             FOR_REPODATAS(repo, j, data)
+               {
+                 if (!repodataused[j])
+                   continue;
+                 if (i < data->start || i >= data->end)
+                   continue;
+                 repodata_search(data, i, 0, SEARCH_SUB|SEARCH_ARRAYSENTINEL, repo_write_cb_adddata, &cbdata);
+               }
+             if (xd->len > 1024 * 1024)
                {
-                 write_compressed_page(fp, vpage, lpage);
-                 lpage = 0;
+                 lpage = write_compressed_extdata(&target, xd, vpage, lpage);
+                 xd->len = 0;
                }
            }
+         if (xd->len)
+           lpage = write_compressed_extdata(&target, xd, vpage, lpage);
        }
       if (lpage)
-       write_compressed_page(fp, vpage, lpage);
+       write_compressed_page(&target, vpage, lpage);
     }
 
   for (i = 1; i < target.nkeys; i++)
     solv_free(cbdata.extdata[i].buf);
   solv_free(cbdata.extdata);
 
+  target.fp = 0;
   repodata_freedata(&target);
 
   solv_free(needid);
@@ -1918,7 +2036,7 @@ fprintf(stderr, "dir %d used %d\n", i, cbdata.dirused ? cbdata.dirused[i] : 1);
   solv_free(cbdata.keymapstart);
   solv_free(cbdata.dirused);
   solv_free(repodataused);
-  return 0;
+  return target.error;
 }
 
 struct repodata_write_data {
@@ -1943,12 +2061,24 @@ repodata_write_keyfilter(Repo *repo, Repokey *key, void *kfdata)
 }
 
 int
-repodata_write(Repodata *data, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata)
+repodata_write_filtered(Repodata *data, FILE *fp, int (*keyfilter)(Repo *repo, Repokey *key, void *kfdata), void *kfdata, Queue *keyq)
 {
   struct repodata_write_data wd;
 
   wd.keyfilter = keyfilter;
   wd.kfdata = kfdata;
   wd.repodataid = data->repodataid;
-  return repo_write(data->repo, fp, repodata_write_keyfilter, &wd, 0);
+  return repo_write_filtered(data->repo, fp, repodata_write_keyfilter, &wd, keyq);
+}
+
+int
+repodata_write(Repodata *data, FILE *fp)
+{
+  return repodata_write_filtered(data, fp, repo_write_stdkeyfilter, 0, 0);
+}
+
+int
+repo_write(Repo *repo, FILE *fp)
+{
+  return repo_write_filtered(repo, fp, repo_write_stdkeyfilter, 0, 0);
 }