Imported Upstream version 0.6.35
[platform/upstream/libsolv.git] / src / fileprovides.c
1 /*
2  * Copyright (c) 2007-2016, SUSE LLC
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * fileprovides.c
10  *
11  * Add missing file dependencies to the package provides 
12  */
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <stdarg.h>
17 #include <unistd.h>
18 #include <string.h>
19
20 #include "pool.h"
21 #include "repo.h"
22 #include "util.h"
23 #include "bitmap.h"
24
25 struct searchfiles {
26   Id *ids;
27   int nfiles;
28   Map seen;
29 };
30
31 #define SEARCHFILES_BLOCK 127
32
33 static void
34 pool_addfileprovides_dep(Pool *pool, Id *ida, struct searchfiles *sf, struct searchfiles *isf)
35 {
36   Id dep, sid;
37   const char *s;
38   struct searchfiles *csf;
39
40   while ((dep = *ida++) != 0)
41     {
42       csf = sf;
43       while (ISRELDEP(dep))
44         {
45           Reldep *rd;
46           sid = pool->ss.nstrings + GETRELID(dep);
47           if (MAPTST(&csf->seen, sid))
48             {
49               dep = 0;
50               break;
51             }
52           MAPSET(&csf->seen, sid);
53           rd = GETRELDEP(pool, dep);
54           if (rd->flags < 8)
55             dep = rd->name;
56           else if (rd->flags == REL_NAMESPACE)
57             {
58               if (rd->name == NAMESPACE_SPLITPROVIDES)
59                 {
60                   csf = isf;
61                   if (!csf || MAPTST(&csf->seen, sid))
62                     {
63                       dep = 0;
64                       break;
65                     }
66                   MAPSET(&csf->seen, sid);
67                 }
68               dep = rd->evr;
69             }
70           else if (rd->flags == REL_FILECONFLICT)
71             {
72               dep = 0;
73               break;
74             }
75           else
76             {
77               Id ids[2];
78               ids[0] = rd->name;
79               ids[1] = 0;
80               pool_addfileprovides_dep(pool, ids, csf, isf);
81               dep = rd->evr;
82             }
83         }
84       if (!dep)
85         continue;
86       if (MAPTST(&csf->seen, dep))
87         continue;
88       MAPSET(&csf->seen, dep);
89       s = pool_id2str(pool, dep);
90       if (*s != '/')
91         continue;
92       if (csf != isf && pool->addedfileprovides == 1 && !repodata_filelistfilter_matches(0, s))
93         continue;       /* skip non-standard locations csf == isf: installed case */
94       csf->ids = solv_extend(csf->ids, csf->nfiles, 1, sizeof(Id), SEARCHFILES_BLOCK);
95       csf->ids[csf->nfiles++] = dep;
96     }
97 }
98
99 struct addfileprovides_cbdata {
100   int nfiles;
101   Id *ids;
102   char **dirs;
103   char **names;
104
105   Id *dids;
106
107   Map providedids;
108
109   Map useddirs;
110 };
111
112 static int
113 addfileprovides_cb(void *cbdata, Solvable *s, Repodata *data, Repokey *key, KeyValue *value)
114 {
115   struct addfileprovides_cbdata *cbd = cbdata;
116   int i;
117
118   if (!cbd->useddirs.size)
119     {
120       map_init(&cbd->useddirs, data->dirpool.ndirs + 1);
121       if (!cbd->dirs)
122         {
123           cbd->dirs = solv_malloc2(cbd->nfiles, sizeof(char *));
124           cbd->names = solv_malloc2(cbd->nfiles, sizeof(char *));
125           for (i = 0; i < cbd->nfiles; i++)
126             {
127               char *s = solv_strdup(pool_id2str(data->repo->pool, cbd->ids[i]));
128               cbd->dirs[i] = s;
129               s = strrchr(s, '/');
130               *s = 0;
131               cbd->names[i] = s + 1;
132             }
133         }
134       for (i = 0; i < cbd->nfiles; i++)
135         {
136           Id did;
137           if (MAPTST(&cbd->providedids, cbd->ids[i]))
138             {
139               cbd->dids[i] = 0;
140               continue;
141             }
142           did = repodata_str2dir(data, cbd->dirs[i], 0);
143           cbd->dids[i] = did;
144           if (did)
145             MAPSET(&cbd->useddirs, did);
146         }
147       repodata_free_dircache(data);
148     }
149   if (value->id >= data->dirpool.ndirs || !MAPTST(&cbd->useddirs, value->id))
150     return 0;
151   for (i = 0; i < cbd->nfiles; i++)
152     if (cbd->dids[i] == value->id && !strcmp(cbd->names[i], value->str))
153       s->provides = repo_addid_dep(s->repo, s->provides, cbd->ids[i], SOLVABLE_FILEMARKER);
154   return 0;
155 }
156
157 static void
158 pool_addfileprovides_search(Pool *pool, struct addfileprovides_cbdata *cbd, struct searchfiles *sf, Repo *repoonly)
159 {
160   Id p;
161   Repodata *data;
162   Repo *repo;
163   Queue fileprovidesq;
164   int i, j, repoid, repodataid;
165   int provstart, provend;
166   Map donemap;
167   int ndone, incomplete;
168
169   if (!pool->urepos)
170     return;
171
172   cbd->nfiles = sf->nfiles;
173   cbd->ids = sf->ids;
174   cbd->dirs = 0;
175   cbd->names = 0;
176   cbd->dids = solv_realloc2(cbd->dids, sf->nfiles, sizeof(Id));
177   map_init(&cbd->providedids, pool->ss.nstrings);
178
179   repoid = 1;
180   repo = repoonly ? repoonly : pool->repos[repoid];
181   map_init(&donemap, pool->nsolvables);
182   queue_init(&fileprovidesq);
183   provstart = provend = 0;
184   for (;;)
185     {
186       if (!repo || repo->disabled)
187         {
188           if (repoonly || ++repoid == pool->nrepos)
189             break;
190           repo = pool->repos[repoid];
191           continue;
192         }
193       ndone = 0;
194       FOR_REPODATAS(repo, repodataid, data)
195         {
196           if (ndone >= repo->nsolvables)
197             break;
198
199           if (repodata_lookup_idarray(data, SOLVID_META, REPOSITORY_ADDEDFILEPROVIDES, &fileprovidesq))
200             {
201               map_empty(&cbd->providedids);
202               for (i = 0; i < fileprovidesq.count; i++)
203                 MAPSET(&cbd->providedids, fileprovidesq.elements[i]);
204               provstart = data->start;
205               provend = data->end;
206               for (i = 0; i < cbd->nfiles; i++)
207                 if (!MAPTST(&cbd->providedids, cbd->ids[i]))
208                   break;
209               if (i == cbd->nfiles)
210                 {
211                   /* great! no need to search files */
212                   for (p = data->start; p < data->end; p++)
213                     if (pool->solvables[p].repo == repo)
214                       {
215                         if (MAPTST(&donemap, p))
216                           continue;
217                         MAPSET(&donemap, p);
218                         ndone++;
219                       }
220                   continue;
221                 }
222             }
223
224           if (!repodata_has_keyname(data, SOLVABLE_FILELIST))
225             continue;
226
227           if (data->start < provstart || data->end > provend)
228             {
229               map_empty(&cbd->providedids);
230               provstart = provend = 0;
231             }
232
233           /* check if the data is incomplete */
234           incomplete = 0;
235           if (data->state == REPODATA_AVAILABLE)
236             {
237               for (j = 1; j < data->nkeys; j++)
238                 if (data->keys[j].name != REPOSITORY_SOLVABLES && data->keys[j].name != SOLVABLE_FILELIST)
239                   break;
240               if (j < data->nkeys)
241                 {
242 #if 0
243                   for (i = 0; i < cbd->nfiles; i++)
244                     if (!MAPTST(&cbd->providedids, cbd->ids[i]) && !repodata_filelistfilter_matches(data, pool_id2str(pool, cbd->ids[i])))
245                       printf("need complete filelist because of %s\n", pool_id2str(pool, cbd->ids[i]));
246 #endif
247                   for (i = 0; i < cbd->nfiles; i++)
248                     if (!MAPTST(&cbd->providedids, cbd->ids[i]) && !repodata_filelistfilter_matches(data, pool_id2str(pool, cbd->ids[i])))
249                       break;
250                   if (i < cbd->nfiles)
251                     incomplete = 1;
252                 }
253             }
254
255           /* do the search */
256           map_init(&cbd->useddirs, 0);
257           for (p = data->start; p < data->end; p++)
258             if (pool->solvables[p].repo == repo)
259               {
260                 if (MAPTST(&donemap, p))
261                   continue;
262                 repodata_search(data, p, SOLVABLE_FILELIST, 0, addfileprovides_cb, cbd);
263                 if (!incomplete)
264                   {
265                     MAPSET(&donemap, p);
266                     ndone++;
267                   }
268               }
269           map_free(&cbd->useddirs);
270         }
271
272       if (repoonly || ++repoid == pool->nrepos)
273         break;
274       repo = pool->repos[repoid];
275     }
276   map_free(&donemap);
277   queue_free(&fileprovidesq);
278   map_free(&cbd->providedids);
279   if (cbd->dirs)
280     {
281       for (i = 0; i < cbd->nfiles; i++)
282         solv_free(cbd->dirs[i]);
283       cbd->dirs = solv_free(cbd->dirs);
284       cbd->names = solv_free(cbd->names);
285     }
286 }
287
288 void
289 pool_addfileprovides_queue(Pool *pool, Queue *idq, Queue *idqinst)
290 {
291   Solvable *s;
292   Repo *installed, *repo;
293   struct searchfiles sf, isf, *isfp;
294   struct addfileprovides_cbdata cbd;
295   int i;
296   unsigned int now;
297
298   installed = pool->installed;
299   now = solv_timems(0);
300   memset(&sf, 0, sizeof(sf));
301   map_init(&sf.seen, pool->ss.nstrings + pool->nrels);
302   memset(&isf, 0, sizeof(isf));
303   map_init(&isf.seen, pool->ss.nstrings + pool->nrels);
304   pool->addedfileprovides = pool->addfileprovidesfiltered ? 1 : 2;
305
306   if (idq)
307     queue_empty(idq);
308   if (idqinst)
309     queue_empty(idqinst);
310   isfp = installed ? &isf : 0;
311   for (i = 1, s = pool->solvables + i; i < pool->nsolvables; i++, s++)
312     {
313       repo = s->repo;
314       if (!repo)
315         continue;
316       if (s->obsoletes)
317         pool_addfileprovides_dep(pool, repo->idarraydata + s->obsoletes, &sf, isfp);
318       if (s->conflicts)
319         pool_addfileprovides_dep(pool, repo->idarraydata + s->conflicts, &sf, isfp);
320       if (s->requires)
321         pool_addfileprovides_dep(pool, repo->idarraydata + s->requires, &sf, isfp);
322       if (s->recommends)
323         pool_addfileprovides_dep(pool, repo->idarraydata + s->recommends, &sf, isfp);
324       if (s->suggests)
325         pool_addfileprovides_dep(pool, repo->idarraydata + s->suggests, &sf, isfp);
326       if (s->supplements)
327         pool_addfileprovides_dep(pool, repo->idarraydata + s->supplements, &sf, isfp);
328       if (s->enhances)
329         pool_addfileprovides_dep(pool, repo->idarraydata + s->enhances, &sf, isfp);
330     }
331   map_free(&sf.seen);
332   map_free(&isf.seen);
333   POOL_DEBUG(SOLV_DEBUG_STATS, "found %d file dependencies, %d installed file dependencies\n", sf.nfiles, isf.nfiles);
334   cbd.dids = 0;
335   if (sf.nfiles)
336     {
337 #if 0
338       for (i = 0; i < sf.nfiles; i++)
339         POOL_DEBUG(SOLV_DEBUG_STATS, "looking up %s in filelist\n", pool_id2str(pool, sf.ids[i]));
340 #endif
341       pool_addfileprovides_search(pool, &cbd, &sf, 0);
342       if (idq)
343         for (i = 0; i < sf.nfiles; i++)
344           queue_push(idq, sf.ids[i]);
345       if (idqinst)
346         for (i = 0; i < sf.nfiles; i++)
347           queue_push(idqinst, sf.ids[i]);
348       solv_free(sf.ids);
349     }
350   if (isf.nfiles)
351     {
352 #if 0
353       for (i = 0; i < isf.nfiles; i++)
354         POOL_DEBUG(SOLV_DEBUG_STATS, "looking up %s in installed filelist\n", pool_id2str(pool, isf.ids[i]));
355 #endif
356       if (installed)
357         pool_addfileprovides_search(pool, &cbd, &isf, installed);
358       if (installed && idqinst)
359         for (i = 0; i < isf.nfiles; i++)
360           queue_pushunique(idqinst, isf.ids[i]);
361       solv_free(isf.ids);
362     }
363   solv_free(cbd.dids);
364   pool_freewhatprovides(pool);  /* as we have added provides */
365   POOL_DEBUG(SOLV_DEBUG_STATS, "addfileprovides took %d ms\n", solv_timems(now));
366 }
367
368 void
369 pool_addfileprovides(Pool *pool)
370 {
371   pool_addfileprovides_queue(pool, 0, 0);
372 }
373