Imported Upstream version 0.7.15
[platform/upstream/libsolv.git] / src / fileprovides.c
index 11ff4f5..ec80831 100644 (file)
@@ -22,6 +22,7 @@
 #include "util.h"
 #include "bitmap.h"
 
+
 struct searchfiles {
   Id *ids;
   int nfiles;
@@ -101,188 +102,450 @@ struct addfileprovides_cbdata {
   Id *ids;
   char **dirs;
   char **names;
-
   Id *dids;
 
-  Map providedids;
+  Map *providedids;
+  int provstart;
+  int provend;
 
-  Map useddirs;
+  Map *todo;
+  int todo_start;
+  int todo_end;
 };
 
-static int
-addfileprovides_cb(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *value)
+/* split filelist dep into basename and dirname */
+static void
+create_dirs_names_array(struct addfileprovides_cbdata *cbd, Pool *pool)
 {
-  struct addfileprovides_cbdata *cbd = cbdata;
   int i;
+  cbd->dirs = solv_malloc2(cbd->nfiles, sizeof(char *));
+  cbd->names = solv_malloc2(cbd->nfiles, sizeof(char *));
+  for (i = 0; i < cbd->nfiles; i++)
+    {
+      char *s = solv_strdup(pool_id2str(pool, cbd->ids[i]));
+      cbd->dirs[i] = s;
+      s = strrchr(s, '/');
+      *s = 0;
+      cbd->names[i] = s + 1;
+    }
+}
 
-  if (!cbd->useddirs.size)
+static void
+free_dirs_names_array(struct addfileprovides_cbdata *cbd)
+{
+  int i;
+  if (cbd->dirs)
     {
-      map_init(&cbd->useddirs, data->dirpool.ndirs + 1);
-      if (!cbd->dirs)
-       {
-         cbd->dirs = solv_malloc2(cbd->nfiles, sizeof(char *));
-         cbd->names = solv_malloc2(cbd->nfiles, sizeof(char *));
-         for (i = 0; i < cbd->nfiles; i++)
-           {
-             char *s = solv_strdup(pool_id2str(data->repo->pool, cbd->ids[i]));
-             cbd->dirs[i] = s;
-             s = strrchr(s, '/');
-             *s = 0;
-             cbd->names[i] = s + 1;
-           }
-       }
       for (i = 0; i < cbd->nfiles; i++)
-       {
-         Id did;
-         if (MAPTST(&cbd->providedids, cbd->ids[i]))
-           {
-             cbd->dids[i] = 0;
-             continue;
-           }
-         did = repodata_str2dir(data, cbd->dirs[i], 0);
-         cbd->dids[i] = did;
-         if (did)
-           MAPSET(&cbd->useddirs, did);
-       }
-      repodata_free_dircache(data);
+       solv_free(cbd->dirs[i]);
+      cbd->dirs = solv_free(cbd->dirs);
+      cbd->names = solv_free(cbd->names);
     }
-  if (value->id >= data->dirpool.ndirs || !MAPTST(&cbd->useddirs, value->id))
+}
+
+static void
+prune_todo_range(Repo *repo, struct addfileprovides_cbdata *cbd)
+{
+  int start = cbd->todo_start, end = cbd->todo_end;
+  while (start < end && !MAPTST(cbd->todo, start - repo->start))
+    start++;
+  while (end > start && !MAPTST(cbd->todo, end - 1 - repo->start))
+    end--;
+  cbd->todo_start = start;
+  cbd->todo_end = end;
+}
+
+static int
+repodata_intersects_todo(Repodata *data, struct addfileprovides_cbdata *cbd)
+{
+  Repo *repo;
+  int p, start = data->start, end = data->end;
+  if (start >= cbd->todo_end || end <= cbd->todo_start)
     return 0;
-  for (i = 0; i < cbd->nfiles; i++)
-    if (cbd->dids[i] == value->id && !strcmp(cbd->names[i], value->str))
-      s->provides = repo_addid_dep(s->repo, s->provides, cbd->ids[i], SOLVABLE_FILEMARKER);
+  repo = data->repo;
+  if (start < cbd->todo_start)
+    start = cbd->todo_start;
+  if (end > cbd->todo_end)
+    end = cbd->todo_end;
+  for (p = start; p < end; p++)
+    if (MAPTST(cbd->todo, p - repo->start))
+      return 1;
   return 0;
 }
 
+/* forward declaration */
+static void repodata_addfileprovides_search(Repodata *data, struct addfileprovides_cbdata *cbd);
+
+/* search a subset of the todo range */
 static void
-pool_addfileprovides_search(Pool *pool, struct addfileprovides_cbdata *cbd, struct searchfiles *sf, Repo *repoonly)
+repodata_addfileprovides_search_limited(Repodata *data, struct addfileprovides_cbdata *cbd, int start, int end)
 {
-  Id p;
-  Repodata *data;
-  Repo *repo;
-  Queue fileprovidesq;
-  int i, j, repoid, repodataid;
-  int provstart, provend;
-  Map donemap;
-  int ndone, incomplete;
 
-  if (!pool->urepos)
+  int old_todo_start = cbd->todo_start;
+  int old_todo_end = cbd->todo_end;
+  if (start < cbd->todo_start)
+    start = cbd->todo_start;
+  if (end > cbd->todo_end)
+    end = cbd->todo_end;
+  if (start >= end)
     return;
+  cbd->todo_start = start;
+  cbd->todo_end = end;
+  repodata_addfileprovides_search(data, cbd);
+  cbd->todo_start = old_todo_start;
+  cbd->todo_end = old_todo_end;
+  prune_todo_range(data->repo, cbd);
+}
 
-  cbd->nfiles = sf->nfiles;
-  cbd->ids = sf->ids;
-  cbd->dirs = 0;
-  cbd->names = 0;
-  cbd->dids = solv_realloc2(cbd->dids, sf->nfiles, sizeof(Id));
-  map_init(&cbd->providedids, pool->ss.nstrings);
-
-  repoid = 1;
-  repo = repoonly ? repoonly : pool->repos[repoid];
-  map_init(&donemap, pool->nsolvables);
-  queue_init(&fileprovidesq);
-  provstart = provend = 0;
-  for (;;)
+static void
+repodata_addfileprovides_search(Repodata *data, struct addfileprovides_cbdata *cbd)
+{
+  Repo *repo = data->repo;
+  int i, p, start, end;
+  Map useddirs;
+  Map *providedids = 0;
+
+  /* make it available */
+  if (data->state == REPODATA_STUB)
+    repodata_load(data);
+  if (data->state != REPODATA_AVAILABLE)
+    return;
+  if (!data->incoredata || !data->dirpool.ndirs)
+    return;
+
+  start = cbd->todo_start > data->start ? cbd->todo_start : data->start;
+  end = cbd->todo_end > data->end ? data->end : cbd->todo_end;
+
+  if (start >= end)
+    return;
+
+  /* deal with provideids overlap */
+  if (cbd->providedids)
     {
-      if (!repo || repo->disabled)
+      if (start >= cbd->provstart && end <= cbd->provend)
+       providedids = cbd->providedids; /* complete overlap */
+      else if (start < cbd->provend && end > cbd->provstart)
        {
-         if (repoonly || ++repoid == pool->nrepos)
-           break;
-         repo = pool->repos[repoid];
+         /* partial overlap, need to split search */
+         if (start < cbd->provstart)
+           {
+             repodata_addfileprovides_search_limited(data, cbd, start, cbd->provstart);
+             start = cbd->provstart;
+           }
+         if (end > cbd->provend)
+           {
+             repodata_addfileprovides_search_limited(data, cbd, cbd->provend, end);
+             end = cbd->provend;
+           }
+         if (start < end)
+           repodata_addfileprovides_search_limited(data, cbd, start, end);
+         return;
+       }
+    }
+
+  /* set up dirs and names array if not already done */
+  if (!cbd->dirs)
+    create_dirs_names_array(cbd, repo->pool);
+
+  /* set up useddirs map and the cbd->dids array */
+  map_init(&useddirs, data->dirpool.ndirs);
+  for (i = 0; i < cbd->nfiles; i++)
+    {
+      Id did;
+      if (providedids && MAPTST(providedids, cbd->ids[i]))
+       {
+         cbd->dids[i] = 0;     /* already included, do not add again */
          continue;
        }
-      ndone = 0;
-      FOR_REPODATAS(repo, repodataid, data)
+      cbd->dids[i] = did = repodata_str2dir(data, cbd->dirs[i], 0);
+      if (did)
+       MAPSET(&useddirs, did);
+    }
+  repodata_free_dircache(data);                /* repodata_str2dir created it */
+
+  for (p = start; p < end; p++)
+    {
+      const unsigned char *dp;
+      Solvable *s;
+      if (!MAPTST(cbd->todo, p - repo->start))
+       continue;
+      dp = repodata_lookup_packed_dirstrarray(data, p, SOLVABLE_FILELIST);
+      if (!dp)
+       continue;
+      /* now iterate through the packed array */
+      s = repo->pool->solvables + p;
+      MAPCLR(cbd->todo, p - repo->start);      /* this entry is done */
+      for (;;)
        {
-         if (ndone >= repo->nsolvables)
+         Id did = 0;
+         int c;
+         while ((c = *dp++) & 0x80)
+           did = (did << 7) ^ c ^ 0x80;
+         did = (did << 6) | (c & 0x3f);
+         if ((unsigned int)did < (unsigned int)data->dirpool.ndirs && MAPTST(&useddirs, did))
+           {
+             /* there is at least one entry with that did */
+             for (i = 0; i < cbd->nfiles; i++)
+               if (cbd->dids[i] == did && !strcmp(cbd->names[i], (const char *)dp))
+                 s->provides = repo_addid_dep(s->repo, s->provides, cbd->ids[i], SOLVABLE_FILEMARKER);
+           }
+         if (!(c & 0x40))
            break;
+         dp += strlen((const char *)dp) + 1;
+       }
+    }
+  map_free(&useddirs);
+  prune_todo_range(repo, cbd);
+}
+
+static void
+repo_addfileprovides_search_filtered(Repo *repo, struct addfileprovides_cbdata *cbd, int filteredid, Map *postpone)
+{
+  Repodata *data = repo->repodata + filteredid;
+  Map *providedids = cbd->providedids;
+  int rdid;
+  int start, end, p, i;
+  Map old_todo;
+  int old_todo_start, old_todo_end;
+
+  start = cbd->todo_start > data->start ? cbd->todo_start : data->start;
+  end = cbd->todo_end > data->end ? data->end : cbd->todo_end;
 
-         if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
+  if (providedids)
+    {
+      /* check if all solvables are in the provide range */
+      if (start < cbd->provstart || end > cbd->provend)
+       {
+         /* unclear, check each solvable */
+         for (p = start; p < end; p++)
            {
-             map_empty(&cbd->providedids);
-             for (i = 0; i < fileprovidesq.count; i++)
-               MAPSET(&cbd->providedids, fileprovidesq.elements[i]);
-             provstart = data->start;
-             provend = data->end;
-             for (i = 0; i < cbd->nfiles; i++)
-               if (!MAPTST(&cbd->providedids, cbd->ids[i]))
-                 break;
-             if (i == cbd->nfiles)
+             if (p >= cbd->provstart && p < cbd->provend)
+               continue;
+             if (data->incoreoffset[p - data->start] && MAPTST(cbd->todo, p - repo->start))
                {
-                 /* great! no need to search files */
-                 for (p = data->start; p < data->end; p++)
-                   if (pool->solvables[p].repo == repo)
-                     {
-                       if (MAPTST(&donemap, p))
-                         continue;
-                       MAPSET(&donemap, p);
-                       ndone++;
-                     }
-                 continue;
+                 providedids = 0;      /* nope, cannot prune with providedids */
+                 break;
                }
            }
+       }
+    }
 
-         if (!repodata_has_keyname(data, SOLVABLE_FILELIST))
-           continue;
+  /* check if the filtered files are enough */
+  for (i = 0; i < cbd->nfiles; i++)
+    {
+      if (providedids && MAPTST(providedids, cbd->ids[i]))     /* this one is already provided */
+       continue;
+      if (!repodata_filelistfilter_matches(data, pool_id2str(repo->pool, cbd->ids[i])))
+        break;
+    }
+  if (i < cbd->nfiles)
+    {
+      /* nope, need to search the extensions as well. postpone. */
+      for (p = start; p < end; p++)
+       {
+         if (data->incoreoffset[p - data->start] && MAPTST(cbd->todo, p - repo->start))
+           {
+             if (!postpone->size)
+               map_grow(postpone, repo->nsolvables);
+             MAPSET(postpone, p - repo->start);
+             MAPCLR(cbd->todo, p - repo->start);
+           }
+       }
+      prune_todo_range(repo, cbd);
+      return;
+    }
+
+  /* now check if there is no data marked withour EXTENSION */
+  /* limit todo to the solvables in this repodata */
+  old_todo_start = cbd->todo_start;
+  old_todo_end = cbd->todo_end;
+  old_todo = *cbd->todo;
+  map_init(cbd->todo, repo->nsolvables);
+  for (p = start; p < end; p++)
+    if (data->incoreoffset[p - data->start] && MAPTST(&old_todo, p - repo->start))
+      {
+        MAPCLR(&old_todo, p - repo->start);
+        MAPSET(cbd->todo, p - repo->start);
+      }
+  prune_todo_range(repo, cbd);
 
-         if (data->start < provstart || data->end > provend)
+  /* do the check */
+  for (rdid = repo->nrepodata - 1, data = repo->repodata + rdid; rdid > filteredid ; rdid--, data--)
+    {
+      if (data->filelisttype == REPODATA_FILELIST_EXTENSION)
+       continue;
+      if (data->start >= cbd->todo_end || data->end <= cbd->todo_start)
+       continue;
+      if (!repodata_has_keyname(data, SOLVABLE_FILELIST))
+       continue;
+      if (!repodata_intersects_todo(data, cbd))
+       continue;
+      /* oh no, this filelist data is not tagged with REPODATA_FILELIST_EXTENSION! */
+      /* postpone entries that have filelist data */
+      start = cbd->todo_start > data->start ? cbd->todo_start : data->start;
+      end = cbd->todo_end > data->end ? data->end : cbd->todo_end;
+      for (p = start; p < end; p++)
+       if (MAPTST(cbd->todo, p - repo->start))
+         if (repodata_lookup_type(data, p, SOLVABLE_FILELIST))
            {
-             map_empty(&cbd->providedids);
-             provstart = provend = 0;
+             if (!postpone->size)
+               map_grow(postpone, repo->nsolvables);
+             MAPSET(postpone, p - repo->start);
+             MAPCLR(cbd->todo, p - repo->start);
            }
+      prune_todo_range(repo, cbd);
+      if (cbd->todo_start >= cbd->todo_end)
+       break;
+    }
+
+  /* do the search over the filtered file list with the remaining entries*/
+  if (cbd->todo_start < cbd->todo_end)
+    repodata_addfileprovides_search(repo->repodata + filteredid, cbd);
+
+  /* restore todo map */
+  map_free(cbd->todo);
+  *cbd->todo = old_todo;
+  cbd->todo_start = old_todo_start;
+  cbd->todo_end = old_todo_end;
+  prune_todo_range(repo, cbd);
+}
+
+static void
+repo_addfileprovides_search(Repo *repo, struct addfileprovides_cbdata *cbd, struct searchfiles *sf)
+{
+  Repodata *data;
+  int rdid, p, i;
+  int provstart, provend;
+  Map todo;
+  Map providedids;
+
+  if (repo->end <= repo->start || !repo->nsolvables || !sf->nfiles)
+    return;
 
-         /* check if the data is incomplete */
-         incomplete = 0;
-         if (data->state == REPODATA_AVAILABLE)
+  /* update search data if changed */
+  if (cbd->nfiles != sf->nfiles || cbd->ids != sf->ids)
+    {
+      free_dirs_names_array(cbd);
+      cbd->nfiles = sf->nfiles;
+      cbd->ids = sf->ids;
+      cbd->dids = solv_realloc2(cbd->dids, sf->nfiles, sizeof(Id));
+    }
+
+  /* create todo map and range */
+  map_init(&todo, repo->end - repo->start);
+  for (p = repo->start; p < repo->end; p++)
+    if (repo->pool->solvables[p].repo == repo)
+      MAPSET(&todo, p - repo->start);
+  cbd->todo = &todo;
+  cbd->todo_start = repo->start;
+  cbd->todo_end = repo->end;
+  prune_todo_range(repo, cbd);
+
+  provstart = provend = 0;
+  map_init(&providedids, 0);
+  data = repo_lookup_repodata(repo, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES);
+  if (data)
+    {
+      Queue fileprovidesq;
+      queue_init(&fileprovidesq);
+      if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
+       {
+         map_grow(&providedids, repo->pool->ss.nstrings);
+         cbd->providedids = &providedids;
+         provstart = data->start;
+         provend = data->end;
+         for (i = 0; i < fileprovidesq.count; i++)
+           MAPSET(&providedids, fileprovidesq.elements[i]);
+         for (i = 0; i < cbd->nfiles; i++)
+           if (!MAPTST(&providedids, cbd->ids[i]))
+             break;
+         if (i == cbd->nfiles)
            {
-             for (j = 1; j < data->nkeys; j++)
-               if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
-                 break;
-             if (j < data->nkeys)
+             /* all included, clear entries from todo list */
+             if (provstart <= cbd->todo_start && provend >= cbd->todo_end)
+               cbd->todo_end = cbd->todo_start;        /* clear complete range */
+             else
                {
-#if 0
-                 for (i = 0; i < cbd->nfiles; i++)
-                   if (!MAPTST(&cbd->providedids, cbd->ids[i]) && !repodata_filelistfilter_matches(data, pool_id2str(pool, cbd->ids[i])))
-                     printf("need complete filelist because of %s\n", pool_id2str(pool, cbd->ids[i]));
-#endif
-                 for (i = 0; i < cbd->nfiles; i++)
-                   if (!MAPTST(&cbd->providedids, cbd->ids[i]) && !repodata_filelistfilter_matches(data, pool_id2str(pool, cbd->ids[i])))
-                     break;
-                 if (i < cbd->nfiles)
-                   incomplete = 1;
+                 for (p = provstart; p < provend; p++)
+                   MAPCLR(&todo, p - repo->start);
+                 prune_todo_range(repo, cbd);
                }
            }
-
-         /* do the search */
-         map_init(&cbd->useddirs, 0);
-         for (p = data->start; p < data->end; p++)
-           if (pool->solvables[p].repo == repo)
-             {
-               if (MAPTST(&donemap, p))
-                 continue;
-               repodata_search(data, p, SOLVABLE_FILELIST, 0, addfileprovides_cb, cbd);
-               if (!incomplete)
-                 {
-                   MAPSET(&donemap, p);
-                   ndone++;
-                 }
-             }
-         map_free(&cbd->useddirs);
        }
+      queue_free(&fileprovidesq);
+    }
 
-      if (repoonly || ++repoid == pool->nrepos)
-       break;
-      repo = pool->repos[repoid];
+  if (cbd->todo_start >= cbd->todo_end)
+    {
+      map_free(&todo);
+      cbd->todo = 0;
+      map_free(&providedids);
+      cbd->providedids = 0;
+      return;
     }
-  map_free(&donemap);
-  queue_free(&fileprovidesq);
-  map_free(&cbd->providedids);
-  if (cbd->dirs)
+
+  /* this is similar to repo_lookup_filelist_repodata in repo.c */
+
+  for (rdid = 1, data = repo->repodata + rdid; rdid < repo->nrepodata; rdid++, data++)
+    if (data->filelisttype == REPODATA_FILELIST_FILTERED)
+      break;
+  for (; rdid < repo->nrepodata; rdid++, data++)
+    if (data->filelisttype == REPODATA_FILELIST_EXTENSION)
+      break;
+
+  if (rdid < repo->nrepodata)
     {
-      for (i = 0; i < cbd->nfiles; i++)
-       solv_free(cbd->dirs[i]);
-      cbd->dirs = solv_free(cbd->dirs);
-      cbd->names = solv_free(cbd->names);
+      /* have at least one repodata with REPODATA_FILELIST_FILTERED followed by REPODATA_FILELIST_EXTENSION */
+      Map postpone;
+      map_init(&postpone, 0);
+      for (rdid = repo->nrepodata - 1, data = repo->repodata + rdid; rdid > 0; rdid--, data--)
+       {
+         if (data->filelisttype != REPODATA_FILELIST_FILTERED)
+           continue;
+         if (!repodata_intersects_todo(data, cbd))
+           continue;
+         if (data->state != REPODATA_AVAILABLE)
+           {
+             if (data->state != REPODATA_STUB)
+               continue;
+             repodata_load(data);
+             if (data->state != REPODATA_AVAILABLE || data->filelisttype != REPODATA_FILELIST_FILTERED)
+               continue;
+           }
+         repo_addfileprovides_search_filtered(repo, cbd, rdid, &postpone);
+       }
+      if (postpone.size)
+       {
+         /* add postponed entries back to todo */
+         map_or(&todo, &postpone);
+         cbd->todo_start = repo->start;
+         cbd->todo_end = repo->end;
+         prune_todo_range(repo, cbd);
+       }
+      map_free(&postpone);
+    }
+
+  /* search remaining entries in the standard way */
+  if (cbd->todo_start < cbd->todo_end)
+    {
+      for (rdid = repo->nrepodata - 1, data = repo->repodata + rdid; rdid > 0; rdid--, data--)
+       {
+         if (data->start >= cbd->todo_end || data->end <= cbd->todo_start)
+           continue;
+         if (!repodata_has_keyname(data, SOLVABLE_FILELIST))
+           continue;
+         if (!repodata_intersects_todo(data, cbd))
+           continue;
+         repodata_addfileprovides_search(data, cbd);
+         if (cbd->todo_start >= cbd->todo_end)
+           break;
+       }
     }
+
+  map_free(&todo);
+  cbd->todo = 0;
+  map_free(&providedids);
+  cbd->providedids = 0;
 }
 
 void
@@ -297,6 +560,7 @@ pool_addfileprovides_queue(Pool *pool, Queue *idq, Queue *idqinst)
 
   installed = pool->installed;
   now = solv_timems(0);
+  memset(&cbd, 0, sizeof(cbd));
   memset(&sf, 0, sizeof(sf));
   map_init(&sf.seen, pool->ss.nstrings + pool->nrels);
   memset(&isf, 0, sizeof(isf));
@@ -328,23 +592,22 @@ pool_addfileprovides_queue(Pool *pool, Queue *idq, Queue *idqinst)
       if (s->enhances)
         pool_addfileprovides_dep(pool, repo->idarraydata + s->enhances, &sf, isfp);
     }
+
   map_free(&sf.seen);
   map_free(&isf.seen);
   POOL_DEBUG(SOLV_DEBUG_STATS, "found %d file dependencies, %d installed file dependencies\n", sf.nfiles, isf.nfiles);
-  cbd.dids = 0;
   if (sf.nfiles)
     {
 #if 0
       for (i = 0; i < sf.nfiles; i++)
        POOL_DEBUG(SOLV_DEBUG_STATS, "looking up %s in filelist\n", pool_id2str(pool, sf.ids[i]));
 #endif
-      pool_addfileprovides_search(pool, &cbd, &sf, 0);
+      FOR_REPOS(i, repo)
+        repo_addfileprovides_search(repo, &cbd, &sf);
       if (idq)
-        for (i = 0; i < sf.nfiles; i++)
-         queue_push(idq, sf.ids[i]);
+       queue_insertn(idq, idq->count, sf.nfiles, sf.ids);
       if (idqinst)
-        for (i = 0; i < sf.nfiles; i++)
-         queue_push(idqinst, sf.ids[i]);
+       queue_insertn(idqinst, idqinst->count, sf.nfiles, sf.ids);
       solv_free(sf.ids);
     }
   if (isf.nfiles)
@@ -354,12 +617,13 @@ pool_addfileprovides_queue(Pool *pool, Queue *idq, Queue *idqinst)
        POOL_DEBUG(SOLV_DEBUG_STATS, "looking up %s in installed filelist\n", pool_id2str(pool, isf.ids[i]));
 #endif
       if (installed)
-        pool_addfileprovides_search(pool, &cbd, &isf, installed);
+        repo_addfileprovides_search(installed, &cbd, &isf);
       if (installed && idqinst)
         for (i = 0; i < isf.nfiles; i++)
          queue_pushunique(idqinst, isf.ids[i]);
       solv_free(isf.ids);
     }
+  free_dirs_names_array(&cbd);
   solv_free(cbd.dids);
   pool_freewhatprovides(pool); /* as we have added provides */
   POOL_DEBUG(SOLV_DEBUG_STATS, "addfileprovides took %d ms\n", solv_timems(now));