1 /*
2  * Copyright (c) 2013, SUSE Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7 
8 /*
9  * linkedpkg.c
10  *
11  * Linked packages are "pseudo" packages that are bound to real packages but
12  * contain different information (name/summary/description). They are normally
13  * somehow generated from the real packages, either when the repositories are
14  * created or automatically from the packages by looking at the provides.
15  *
16  * We currently support:
17  *
18  * application:
19  *   created from AppStream appdata xml in the repository (which is generated
20  *   from files in /usr/share/appdata)
21  *
22  * product:
23  *   created from product data in the repository (which is generated from files
24  *   in /etc/products.d). In the future we may switch to using product()
25  *   provides of packages.
26  *
27  * pattern:
28  *   created from pattern() provides of packages.
29  *
30  */
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <string.h>
36 #include <assert.h>
37 
38 #include "pool.h"
39 #include "repo.h"
40 #include "solver.h"
41 #include "evr.h"
42 #include "bitmap.h"
43 #include "linkedpkg.h"
44 
45 #ifdef ENABLE_LINKED_PKGS
46 
47 void
find_application_link(Pool * pool,Solvable * s,Id * reqidp,Queue * qr,Id * prvidp,Queue * qp)48 find_application_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
49 {
50   Id req = 0;
51   Id prv = 0;
52   Id p, pp;
53   Id pkgname = 0, appdataid = 0;
54 
55   /* find appdata requires */
56   if (s->requires)
57     {
58       Id *reqp = s->repo->idarraydata + s->requires;
59       while ((req = *reqp++) != 0)            /* go through all requires */
60 	{
61 	  if (ISRELDEP(req))
62 	    continue;
63 	  if (!strncmp("appdata(", pool_id2str(pool, req), 8))
64 	    appdataid = req;
65 	  else
66 	    pkgname = req;
67 	}
68     }
69   req = appdataid ? appdataid : pkgname;
70   if (!req)
71     return;
72   /* find application-appdata provides */
73   if (s->provides)
74     {
75       Id *prvp = s->repo->idarraydata + s->provides;
76       const char *reqs = pool_id2str(pool, req);
77       const char *prvs;
78       while ((prv = *prvp++) != 0)            /* go through all provides */
79 	{
80 	  if (ISRELDEP(prv))
81 	    continue;
82 	  prvs = pool_id2str(pool, prv);
83 	  if (strncmp("application-appdata(", prvs, 20))
84 	    continue;
85 	  if (appdataid)
86 	    {
87 	      if (!strcmp(prvs + 12, reqs))
88 		break;
89 	    }
90 	  else
91 	    {
92 	      int reqsl = strlen(reqs);
93 	      if (!strncmp(prvs + 20, reqs, reqsl) && !strcmp(prvs + 20 + reqsl, ")"))
94 		break;
95 	    }
96 	}
97     }
98   if (!prv)
99     return;	/* huh, no provides found? */
100   /* now link em */
101   FOR_PROVIDES(p, pp, req)
102     if (pool->solvables[p].repo == s->repo)
103       if (!pkgname || pool->solvables[p].name == pkgname)
104         queue_push(qr, p);
105   if (!qr->count && pkgname && appdataid)
106     {
107       /* huh, no matching package? try without pkgname filter */
108       FOR_PROVIDES(p, pp, req)
109 	if (pool->solvables[p].repo == s->repo)
110           queue_push(qr, p);
111     }
112   if (qp)
113     {
114       FOR_PROVIDES(p, pp, prv)
115 	if (pool->solvables[p].repo == s->repo)
116 	  queue_push(qp, p);
117     }
118   if (reqidp)
119     *reqidp = req;
120   if (prvidp)
121     *prvidp = prv;
122 }
123 
124 void
find_product_link(Pool * pool,Solvable * s,Id * reqidp,Queue * qr,Id * prvidp,Queue * qp)125 find_product_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
126 {
127   Id p, pp, namerelid;
128   char *str;
129   unsigned int sbt = 0;
130 
131   /* search for project requires */
132   namerelid = 0;
133   if (s->requires)
134     {
135       Id req, *reqp = s->repo->idarraydata + s->requires;
136       const char *nn = pool_id2str(pool, s->name);
137       int nnl = strlen(nn);
138       while ((req = *reqp++) != 0)            /* go through all requires */
139 	if (ISRELDEP(req))
140 	  {
141 	    const char *rn;
142 	    Reldep *rd = GETRELDEP(pool, req);
143 	    if (rd->flags != REL_EQ || rd->evr != s->evr)
144 	      continue;
145 	    rn = pool_id2str(pool, rd->name);
146 	    if (!strncmp(rn, "product(", 8) && !strncmp(rn + 8, nn + 8, nnl - 8) && !strcmp( rn + nnl, ")"))
147 	      {
148 		namerelid = req;
149 		break;
150 	      }
151 	  }
152     }
153   if (!namerelid)
154     {
155       /* too bad. construct from scratch */
156       str = pool_tmpjoin(pool, pool_id2str(pool, s->name), ")", 0);
157       str[7] = '(';
158       namerelid = pool_rel2id(pool, pool_str2id(pool, str, 1), s->evr, REL_EQ, 1);
159     }
160   FOR_PROVIDES(p, pp, namerelid)
161     {
162       Solvable *ps = pool->solvables + p;
163       if (ps->repo != s->repo || ps->arch != s->arch)
164 	continue;
165       queue_push(qr, p);
166     }
167   if (qr->count > 1)
168     {
169       /* multiple providers. try buildtime filter */
170       sbt = solvable_lookup_num(s, SOLVABLE_BUILDTIME, 0);
171       if (sbt)
172 	{
173 	  unsigned int bt;
174 	  int i, j;
175 	  int filterqp = 1;
176 	  for (i = j = 0; i < qr->count; i++)
177 	    {
178 	      bt = solvable_lookup_num(pool->solvables + qr->elements[i], SOLVABLE_BUILDTIME, 0);
179 	      if (!bt)
180 		filterqp = 0;	/* can't filter */
181 	      if (!bt || bt == sbt)
182 		qr->elements[j++] = qr->elements[i];
183 	    }
184 	  if (j)
185 	    qr->count = j;
186 	  if (!j || !filterqp)
187 	    sbt = 0;	/* filter failed */
188 	}
189     }
190   if (!qr->count && s->repo == pool->installed)
191     {
192       /* oh no! Look up reference file */
193       Dataiterator di;
194       const char *refbasename = solvable_lookup_str(s, PRODUCT_REFERENCEFILE);
195       if (refbasename)
196 	{
197 	  dataiterator_init(&di, pool, s->repo, 0, SOLVABLE_FILELIST, refbasename, SEARCH_STRING);
198 	  while (dataiterator_step(&di))
199 	    {
200 	      if (di.key->type != REPOKEY_TYPE_DIRSTRARRAY)
201 		continue;
202 	      if (strcmp(repodata_dir2str(di.data, di.kv.id, 0), "/etc/products.d") != 0)
203 		continue;
204 	      queue_push(qr, di.solvid);
205 	    }
206 	  dataiterator_free(&di);
207 	  if (qp)
208 	    {
209 	      dataiterator_init(&di, pool, s->repo, 0, PRODUCT_REFERENCEFILE, refbasename, SEARCH_STRING);
210 	      while (dataiterator_step(&di))
211 		queue_push(qp, di.solvid);
212 	      dataiterator_free(&di);
213 	    }
214 	}
215     }
216   else if (qp)
217     {
218       /* find qp */
219       FOR_PROVIDES(p, pp, s->name)
220 	{
221 	  Solvable *ps = pool->solvables + p;
222 	  if (s->name != ps->name || ps->repo != s->repo || ps->arch != s->arch || s->evr != ps->evr)
223 	    continue;
224 	  if (sbt && solvable_lookup_num(ps, SOLVABLE_BUILDTIME, 0) != sbt)
225 	    continue;
226 	  queue_push(qp, p);
227 	}
228     }
229   if (reqidp)
230     *reqidp = namerelid;
231   if (prvidp)
232     *prvidp = solvable_selfprovidedep(s);
233 }
234 
235 void
find_pattern_link(Pool * pool,Solvable * s,Id * reqidp,Queue * qr,Id * prvidp,Queue * qp)236 find_pattern_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
237 {
238   Id p, pp, *pr, apevr = 0, aprel = 0;
239 
240   /* check if autopattern */
241   if (!s->provides)
242     return;
243   for (pr = s->repo->idarraydata + s->provides; (p = *pr++) != 0; )
244     if (ISRELDEP(p))
245       {
246 	Reldep *rd = GETRELDEP(pool, p);
247 	if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autopattern()"))
248 	  {
249 	    aprel = p;
250 	    apevr = rd->evr;
251 	    break;
252 	  }
253       }
254   if (!apevr)
255     return;
256   FOR_PROVIDES(p, pp, apevr)
257     {
258       Solvable *s2 = pool->solvables + p;
259       if (s2->repo == s->repo && s2->name == apevr && s2->evr == s->evr && s2->vendor == s->vendor)
260         queue_push(qr, p);
261     }
262   if (qp)
263     {
264       FOR_PROVIDES(p, pp, aprel)
265 	{
266 	  Solvable *s2 = pool->solvables + p;
267 	  if (s2->repo == s->repo && s2->evr == s->evr && s2->vendor == s->vendor)
268 	    queue_push(qp, p);
269 	}
270     }
271   if (reqidp)
272     *reqidp = apevr;
273   if (prvidp)
274     *prvidp = aprel;
275 }
276 
277 /* the following two functions are used in solvable_lookup_str_base to do
278  * translated lookups on the product/pattern packages
279  */
280 Id
find_autopattern_name(Pool * pool,Solvable * s)281 find_autopattern_name(Pool *pool, Solvable *s)
282 {
283   Id prv, *prvp;
284   if (!s->provides)
285     return 0;
286   for (prvp = s->repo->idarraydata + s->provides; (prv = *prvp++) != 0; )
287     if (ISRELDEP(prv))
288       {
289         Reldep *rd = GETRELDEP(pool, prv);
290         if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autopattern()"))
291           return strncmp(pool_id2str(pool, rd->evr), "pattern:", 8) != 0 ? rd->evr : 0;
292       }
293   return 0;
294 }
295 
296 Id
find_autoproduct_name(Pool * pool,Solvable * s)297 find_autoproduct_name(Pool *pool, Solvable *s)
298 {
299   Id prv, *prvp;
300   if (!s->provides)
301     return 0;
302   for (prvp = s->repo->idarraydata + s->provides; (prv = *prvp++) != 0; )
303     if (ISRELDEP(prv))
304       {
305         Reldep *rd = GETRELDEP(pool, prv);
306         if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autoproduct()"))
307           return strncmp(pool_id2str(pool, rd->evr), "product:", 8) != 0 ? rd->evr : 0;
308       }
309   return 0;
310 }
311 
312 void
find_package_link(Pool * pool,Solvable * s,Id * reqidp,Queue * qr,Id * prvidp,Queue * qp)313 find_package_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp)
314 {
315   const char *name = pool_id2str(pool, s->name);
316   if (name[0] == 'a' && !strncmp("application:", name, 12))
317     find_application_link(pool, s, reqidp, qr, prvidp, qp);
318   else if (name[0] == 'p' && !strncmp("pattern:", name, 7))
319     find_pattern_link(pool, s, reqidp, qr, prvidp, qp);
320   else if (name[0] == 'p' && !strncmp("product:", name, 8))
321     find_product_link(pool, s, reqidp, qr, prvidp, qp);
322 }
323 
324 static int
name_min_max(Pool * pool,Solvable * s,Id * namep,Id * minp,Id * maxp)325 name_min_max(Pool *pool, Solvable *s, Id *namep, Id *minp, Id *maxp)
326 {
327   Queue q;
328   Id qbuf[4];
329   Id name, min, max;
330   int i;
331 
332   queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
333   find_package_link(pool, s, 0, &q, 0, 0);
334   if (!q.count)
335     {
336       queue_free(&q);
337       return 0;
338     }
339   s = pool->solvables + q.elements[0];
340   name = s->name;
341   min = max = s->evr;
342   for (i = 1; i < q.count; i++)
343     {
344       s = pool->solvables + q.elements[i];
345       if (s->name != name)
346 	{
347           queue_free(&q);
348 	  return 0;
349 	}
350       if (s->evr == min || s->evr == max)
351 	continue;
352       if (pool_evrcmp(pool, min, s->evr, EVRCMP_COMPARE) >= 0)
353 	min = s->evr;
354       else if (min == max || pool_evrcmp(pool, max, s->evr, EVRCMP_COMPARE) <= 0)
355 	max = s->evr;
356     }
357   queue_free(&q);
358   *namep = name;
359   *minp = min;
360   *maxp = max;
361   return 1;
362 }
363 
364 int
pool_link_evrcmp(Pool * pool,Solvable * s1,Solvable * s2)365 pool_link_evrcmp(Pool *pool, Solvable *s1, Solvable *s2)
366 {
367   Id name1, evrmin1, evrmax1;
368   Id name2, evrmin2, evrmax2;
369 
370   if (s1->name != s2->name)
371     return 0;	/* can't compare */
372   if (!name_min_max(pool, s1, &name1, &evrmin1, &evrmax1))
373     return 0;
374   if (!name_min_max(pool, s2, &name2, &evrmin2, &evrmax2))
375     return 0;
376   /* compare linked names */
377   if (name1 != name2)
378     return 0;
379   if (evrmin1 == evrmin2 && evrmax1 == evrmax2)
380     return 0;
381   /* now compare evr intervals */
382   if (evrmin1 == evrmax1 && evrmin2 == evrmax2)
383     return pool_evrcmp(pool, evrmin1, evrmax2, EVRCMP_COMPARE);
384   if (evrmin1 != evrmax2 && pool_evrcmp(pool, evrmin1, evrmax2, EVRCMP_COMPARE) > 0)
385     return 1;
386   if (evrmax1 != evrmin2 && pool_evrcmp(pool, evrmax1, evrmin2, EVRCMP_COMPARE) < 0)
387     return -1;
388   return 0;
389 }
390 
391 void
extend_updatemap_to_buddies(Solver * solv)392 extend_updatemap_to_buddies(Solver *solv)
393 {
394   Pool *pool = solv->pool;
395   Repo *installed = solv->installed;
396   Solvable *s;
397   int p, ip;
398 
399   if (!installed)
400     return;
401   if (!solv->updatemap.size || !solv->instbuddy)
402     return;
403   FOR_REPO_SOLVABLES(installed, p, s)
404     {
405       if (!MAPTST(&solv->updatemap, p - installed->start))
406 	continue;
407       if ((ip = solv->instbuddy[p - installed->start]) <= 1)
408 	continue;
409       if (!has_package_link(pool, s))	/* only look at pseudo -> real relations */
410 	continue;
411       if (ip < installed->start || ip >= installed->end || pool->solvables[ip].repo != installed)
412 	continue;			/* just in case... */
413       MAPSET(&solv->updatemap, ip - installed->start);
414     }
415 }
416 
417 #endif
418