implement SOLVER_FLAG_BREAK_ORPHANS
[platform/upstream/libsolv.git] / ext / repo_deb.c
1 /*
2  * Copyright (c) 2009, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <zlib.h>
15 #include <errno.h>
16
17 #include "pool.h"
18 #include "repo.h"
19 #include "util.h"
20 #include "chksum.h"
21 #include "repo_deb.h"
22
23 static unsigned char *
24 decompress(unsigned char *in, int inl, int *outlp)
25 {
26   z_stream strm;
27   int outl, ret;
28   unsigned char *out;
29
30   memset(&strm, 0, sizeof(strm));
31   strm.next_in = in;
32   strm.avail_in = inl;
33   out = solv_malloc(4096);
34   strm.next_out = out;
35   strm.avail_out = 4096;
36   outl = 0;
37   ret = inflateInit2(&strm, -MAX_WBITS);
38   if (ret != Z_OK)
39     {
40       free(out);
41       return 0;
42     }
43   for (;;)
44     {
45       if (strm.avail_out == 0)
46         {
47           outl += 4096;
48           out = solv_realloc(out, outl + 4096);
49           strm.next_out = out + outl;
50           strm.avail_out = 4096;
51         }
52       ret = inflate(&strm, Z_NO_FLUSH);
53       if (ret == Z_STREAM_END)
54         break;
55       if (ret != Z_OK)
56         {
57           free(out);
58           return 0;
59         }
60     }
61   outl += 4096 - strm.avail_out;
62   inflateEnd(&strm);
63   *outlp = outl;
64   return out;
65 }
66
67 static Id
68 parseonedep(Pool *pool, char *p)
69 {
70   char *n, *ne, *e, *ee;
71   Id name, evr;
72   int flags;
73
74   while (*p == ' ' || *p == '\t' || *p == '\n')
75     p++;
76   if (!*p || *p == '(')
77     return 0;
78   n = p;
79   /* find end of name */
80   while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != '(' && *p != '|')
81     p++;
82   ne = p;
83   while (*p == ' ' || *p == '\t' || *p == '\n')
84     p++;
85   evr = 0;
86   flags = 0;
87   e = ee = 0;
88   if (*p == '(')
89     {
90       p++;
91       while (*p == ' ' || *p == '\t' || *p == '\n')
92         p++;
93       if (*p == '>')
94         flags |= REL_GT;
95       else if (*p == '=')
96         flags |= REL_EQ;
97       else if (*p == '<')
98         flags |= REL_LT;
99       if (flags)
100         {
101           p++;
102           if (*p == '>')
103             flags |= REL_GT;
104           else if (*p == '=')
105             flags |= REL_EQ;
106           else if (*p == '<')
107             flags |= REL_LT;
108           else
109             p--;
110           p++;
111         }
112       while (*p == ' ' || *p == '\t' || *p == '\n')
113         p++;
114       e = p;
115       while (*p && *p != ' ' && *p != '\t' && *p != '\n' && *p != ')')
116         p++;
117       ee = p;
118       while (*p && *p != ')')
119         p++;
120       if (*p)
121         p++;
122       while (*p == ' ' || *p == '\t' || *p == '\n')
123         p++;
124     }
125   if (ne - n > 4 && ne[-4] == ':' && !strncmp(ne - 4, ":any", 4))
126     {
127       /* multiarch annotation */
128       name = pool_strn2id(pool, n, ne - n - 4, 1);
129       name = pool_rel2id(pool, name, ARCH_ANY, REL_MULTIARCH, 1);
130     }
131   else
132     name = pool_strn2id(pool, n, ne - n, 1);
133   if (e)
134     {
135       evr = pool_strn2id(pool, e, ee - e, 1);
136       name = pool_rel2id(pool, name, evr, flags, 1);
137     }
138   if (*p == '|')
139     {
140       Id id = parseonedep(pool, p + 1);
141       if (id)
142         name = pool_rel2id(pool, name, id, REL_OR, 1);
143     }
144   return name;
145 }
146
147 static unsigned int
148 makedeps(Repo *repo, char *deps, unsigned int olddeps, Id marker)
149 {
150   Pool *pool = repo->pool;
151   char *p;
152   Id id;
153
154   while ((p = strchr(deps, ',')) != 0)
155     {
156       *p = 0;
157       olddeps = makedeps(repo, deps, olddeps, marker);
158       *p = ',';
159       deps = p + 1;
160     }
161   id = parseonedep(pool, deps);
162   if (!id)
163     return olddeps;
164   return repo_addid_dep(repo, olddeps, id, marker);
165 }
166
167
168 /* put data from control file into the solvable */
169 /* warning: does inplace changes */
170 static void
171 control2solvable(Solvable *s, Repodata *data, char *control)
172 {
173   Repo *repo = s->repo;
174   Pool *pool = repo->pool;
175   char *p, *q, *end, *tag;
176   int x, l;
177   int havesource = 0;
178   char checksum[32 * 2 + 1];
179   Id checksumtype = 0;
180   Id newtype;
181
182   p = control;
183   while (*p)
184     {
185       p = strchr(p, '\n');
186       if (!p)
187         break;
188       if (p[1] == ' ' || p[1] == '\t')
189         {
190           char *q;
191           /* continuation line */
192           q = p - 1;
193           while (q >= control && *q == ' ' && *q == '\t')
194             q--;
195           l = q + 1 - control;
196           if (l)
197             memmove(p + 1 - l, control, l);
198           control = p + 1 - l;
199           p[1] = '\n';
200           p += 2;
201           continue;
202         }
203       end = p - 1;
204       if (*p)
205         *p++ = 0;
206       /* strip trailing space */
207       while (end >= control && *end == ' ' && *end == '\t')
208         *end-- = 0;
209       tag = control;
210       control = p;
211       q = strchr(tag, ':');
212       if (!q || q - tag < 4)
213         continue;
214       *q++ = 0;
215       while (*q == ' ' || *q == '\t')
216         q++;
217       x = '@' + (tag[0] & 0x1f);
218       x = (x << 8) + '@' + (tag[1] & 0x1f);
219       switch(x)
220         {
221         case 'A' << 8 | 'R':
222           if (!strcasecmp(tag, "architecture"))
223             s->arch = pool_str2id(pool, q, 1);
224           break;
225         case 'B' << 8 | 'R':
226           if (!strcasecmp(tag, "breaks"))
227             s->conflicts = makedeps(repo, q, s->conflicts, 0);
228           break;
229         case 'C' << 8 | 'O':
230           if (!strcasecmp(tag, "conflicts"))
231             s->conflicts = makedeps(repo, q, s->conflicts, 0);
232           break;
233         case 'D' << 8 | 'E':
234           if (!strcasecmp(tag, "depends"))
235             s->requires = makedeps(repo, q, s->requires, -SOLVABLE_PREREQMARKER);
236           else if (!strcasecmp(tag, "description"))
237             {
238               char *ld = strchr(q, '\n');
239               if (ld)
240                 {
241                   *ld++ = 0;
242                   repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, ld);
243                 }
244               else
245                 repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, q);
246               repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, q);
247             }
248           break;
249         case 'E' << 8 | 'N':
250           if (!strcasecmp(tag, "enhances"))
251             s->enhances = makedeps(repo, q, s->enhances, 0);
252           break;
253         case 'F' << 8 | 'I':
254           if (!strcasecmp(tag, "filename"))
255             repodata_set_location(data, s - pool->solvables, 0, 0, q);
256           break;
257         case 'H' << 8 | 'O':
258           if (!strcasecmp(tag, "homepage"))
259             repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, q);
260           break;
261         case 'I' << 8 | 'N':
262           if (!strcasecmp(tag, "installed-size"))
263             repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(q, 0, 10) << 10);
264           break;
265         case 'M' << 8 | 'D':
266           if (!strcasecmp(tag, "md5sum") && !checksumtype && strlen(q) == 16 * 2)
267             {
268               strcpy(checksum, q);
269               checksumtype = REPOKEY_TYPE_MD5;
270             }
271           break;
272         case 'P' << 8 | 'A':
273           if (!strcasecmp(tag, "package"))
274             s->name = pool_str2id(pool, q, 1);
275           break;
276         case 'P' << 8 | 'R':
277           if (!strcasecmp(tag, "pre-depends"))
278             s->requires = makedeps(repo, q, s->requires, SOLVABLE_PREREQMARKER);
279           else if (!strcasecmp(tag, "provides"))
280             s->provides = makedeps(repo, q, s->provides, 0);
281           break;
282         case 'R' << 8 | 'E':
283           if (!strcasecmp(tag, "replaces"))
284             s->obsoletes = makedeps(repo, q, s->obsoletes, 0);
285           else if (!strcasecmp(tag, "recommends"))
286             s->recommends = makedeps(repo, q, s->recommends, 0);
287           break;
288         case 'S' << 8 | 'H':
289           newtype = solv_chksum_str2type(tag);
290           if (!newtype || solv_chksum_len(newtype) * 2 != strlen(q))
291             break;
292           if (!checksumtype || (newtype == REPOKEY_TYPE_SHA1 && checksumtype != REPOKEY_TYPE_SHA256) || newtype == REPOKEY_TYPE_SHA256)
293             {
294               strcpy(checksum, q);
295               checksumtype = newtype;
296             }
297           break;
298         case 'S' << 8 | 'O':
299           if (!strcasecmp(tag, "source"))
300             {
301               char *q2;
302               /* ignore version for now */
303               for (q2 = q; *q2; q2++)
304                 if (*q2 == ' ' || *q2 == '\t')
305                   {
306                     *q2 = 0;
307                     break;
308                   }
309               if (s->name && !strcmp(q, pool_id2str(pool, s->name)))
310                 repodata_set_void(data, s - pool->solvables, SOLVABLE_SOURCENAME);
311               else
312                 repodata_set_id(data, s - pool->solvables, SOLVABLE_SOURCENAME, pool_str2id(pool, q, 1));
313               havesource = 1;
314             }
315           break;
316         case 'S' << 8 | 'U':
317           if (!strcasecmp(tag, "suggests"))
318             s->suggests = makedeps(repo, q, s->suggests, 0);
319           break;
320         case 'V' << 8 | 'E':
321           if (!strcasecmp(tag, "version"))
322             s->evr = pool_str2id(pool, q, 1);
323           break;
324         }
325     }
326   if (checksumtype)
327     repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, checksumtype, checksum);
328   if (!s->arch)
329     s->arch = ARCH_ALL;
330   if (!s->evr)
331     s->evr = ID_EMPTY;
332   if (s->name)
333     s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
334   if (s->name && !havesource)
335     repodata_set_void(data, s - pool->solvables, SOLVABLE_SOURCENAME);
336   if (s->obsoletes)
337     {
338       /* obsoletes only count when the packages also conflict */
339       /* XXX: should not transcode here */
340       int i, j, k;
341       Id d, cid;
342       for (i = j = s->obsoletes; (d = repo->idarraydata[i]) != 0; i++)
343         {
344           if (!s->conflicts)
345             continue;
346           for (k = s->conflicts; (cid = repo->idarraydata[k]) != 0; k++)
347             {
348               if (repo->idarraydata[k] == cid)
349                 break;
350               if (ISRELDEP(cid))
351                 {
352                   Reldep *rd = GETRELDEP(pool, cid);
353                   if (rd->flags < 8 && rd->name == d)
354                     break;      /* specialize obsoletes */
355                 }
356             }
357           if (cid)
358             repo->idarraydata[j++] = cid;
359         }
360       repo->idarraydata[j] = 0;
361       if (j == s->obsoletes)
362         s->obsoletes = 0;
363     }
364 }
365
366 int
367 repo_add_debpackages(Repo *repo, FILE *fp, int flags)
368 {
369   Pool *pool = repo->pool;
370   Repodata *data;
371   char *buf, *p;
372   int bufl, l, ll;
373   Solvable *s;
374
375   data = repo_add_repodata(repo, flags);
376   buf = solv_malloc(4096);
377   bufl = 4096;
378   l = 0;
379   buf[l] = 0;
380   p = buf;
381   for (;;)
382     {
383       if (!(p = strchr(p, '\n')))
384         {
385           int l3;
386           if (l + 1024 >= bufl)
387             {
388               buf = solv_realloc(buf, bufl + 4096);
389               bufl += 4096;
390               p = buf + l;
391               continue;
392             }
393           p = buf + l;
394           ll = fread(p, 1, bufl - l - 1, fp);
395           if (ll <= 0)
396             break;
397           p[ll] = 0;
398           while ((l3 = strlen(p)) < ll)
399             p[l3] = '\n';
400           l += ll;
401           continue;
402         }
403       p++;
404       if (*p != '\n')
405         continue;
406       *p = 0;
407       ll = p - buf + 1;
408       s = pool_id2solvable(pool, repo_add_solvable(repo));
409       control2solvable(s, data, buf);
410       if (!s->name)
411         repo_free_solvable(repo, s - pool->solvables, 1);
412       if (l > ll)
413         memmove(buf, p + 1, l - ll);
414       l -= ll;
415       p = buf;
416       buf[l] = 0;
417     }
418   if (l)
419     {
420       s = pool_id2solvable(pool, repo_add_solvable(repo));
421       control2solvable(s, data, buf);
422       if (!s->name)
423         repo_free_solvable(repo, s - pool->solvables, 1);
424     }
425   solv_free(buf);
426   if (!(flags & REPO_NO_INTERNALIZE))
427     repodata_internalize(data);
428   return 0;
429 }
430
431 int
432 repo_add_debdb(Repo *repo, int flags)
433 {
434   FILE *fp;
435   const char *path = "/var/lib/dpkg/status";
436   if (flags & REPO_USE_ROOTDIR)
437     path = pool_prepend_rootdir_tmp(repo->pool, path);
438   if ((fp = fopen(path, "r")) == 0)
439     return pool_error(repo->pool, -1, "%s: %s", path, strerror(errno));
440   repo_add_debpackages(repo, fp, flags);
441   fclose(fp);
442   return 0;
443 }
444
445 Id
446 repo_add_deb(Repo *repo, const char *deb, int flags)
447 {
448   Pool *pool = repo->pool;
449   Repodata *data;
450   unsigned char buf[4096], *bp;
451   int l, l2, vlen, clen, ctarlen;
452   unsigned char *ctgz;
453   unsigned char pkgid[16];
454   unsigned char *ctar;
455   int gotpkgid;
456   FILE *fp;
457   Solvable *s;
458   struct stat stb;
459
460   data = repo_add_repodata(repo, flags);
461   if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, deb) : deb, "r")) == 0)
462     {
463       pool_error(pool, -1, "%s: %s", deb, strerror(errno));
464       return 0;
465     }
466   if (fstat(fileno(fp), &stb))
467     {
468       pool_error(pool, -1, "fstat: %s", strerror(errno));
469       fclose(fp);
470       return 0;
471     }
472   l = fread(buf, 1, sizeof(buf), fp);
473   if (l < 8 + 60 || strncmp((char *)buf, "!<arch>\ndebian-binary   ", 8 + 16) != 0)
474     {
475       pool_error(pool, -1, "%s: not a deb package", deb);
476       fclose(fp);
477       return 0;
478     }
479   vlen = atoi((char *)buf + 8 + 48);
480   if (vlen < 0 || vlen > l)
481     {
482       pool_error(pool, -1, "%s: not a deb package", deb);
483       fclose(fp);
484       return 0;
485     }
486   vlen += vlen & 1;
487   if (l < 8 + 60 + vlen + 60)
488     {
489       pool_error(pool, -1, "%s: unhandled deb package", deb);
490       fclose(fp);
491       return 0;
492     }
493   if (strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz  ", 16) != 0)
494     {
495       pool_error(pool, -1, "%s: control.tar.gz is not second entry", deb);
496       fclose(fp);
497       return 0;
498     }
499   clen = atoi((char *)buf + 8 + 60 + vlen + 48);
500   if (clen <= 0 || clen >= 0x100000)
501     {
502       pool_error(pool, -1, "%s: control.tar.gz has illegal size", deb);
503       fclose(fp);
504       return 0;
505     }
506   ctgz = solv_calloc(1, clen + 4);
507   bp = buf + 8 + 60 + vlen + 60;
508   l -= 8 + 60 + vlen + 60;
509   if (l > clen)
510     l = clen;
511   if (l)
512     memcpy(ctgz, bp, l);
513   if (l < clen)
514     {
515       if (fread(ctgz + l, clen - l, 1, fp) != 1)
516         {
517           pool_error(pool, -1, "%s: unexpected EOF", deb);
518           solv_free(ctgz);
519           fclose(fp);
520           return 0;
521         }
522     }
523   fclose(fp);
524   gotpkgid = 0;
525   if (flags & DEBS_ADD_WITH_PKGID)
526     {
527       Chksum *chk = solv_chksum_create(REPOKEY_TYPE_MD5);
528       solv_chksum_add(chk, ctgz, clen);
529       solv_chksum_free(chk, pkgid);
530       gotpkgid = 1;
531     }
532   if (ctgz[0] != 0x1f || ctgz[1] != 0x8b)
533     {
534       pool_error(pool, -1, "%s: control.tar.gz is not gzipped", deb);
535       solv_free(ctgz);
536       return 0;
537     }
538   if (ctgz[2] != 8 || (ctgz[3] & 0xe0) != 0)
539     {
540       pool_error(pool, -1, "%s: control.tar.gz is compressed in a strange way", deb);
541       solv_free(ctgz);
542       return 0;
543     }
544   bp = ctgz + 4;
545   bp += 6;      /* skip time, xflags and OS code */
546   if (ctgz[3] & 0x04)
547     {
548       /* skip extra field */
549       l = bp[0] | bp[1] << 8;
550       bp += l + 2;
551       if (bp >= ctgz + clen)
552         {
553           pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb);
554           solv_free(ctgz);
555           return 0;
556         }
557     }
558   if (ctgz[3] & 0x08)   /* orig filename */
559     while (*bp)
560       bp++;
561   if (ctgz[3] & 0x10)   /* file comment */
562     while (*bp)
563       bp++;
564   if (ctgz[3] & 0x02)   /* header crc */
565     bp += 2;
566   if (bp >= ctgz + clen)
567     {
568       pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb);
569       solv_free(ctgz);
570       return 0;
571     }
572   ctar = decompress(bp, ctgz + clen - bp, &ctarlen);
573   solv_free(ctgz);
574   if (!ctar)
575     {
576       pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb);
577       return 0;
578     }
579   bp = ctar;
580   l = ctarlen;
581   while (l > 512)
582     {
583       int j;
584       l2 = 0;
585       for (j = 124; j < 124 + 12; j++)
586         if (bp[j] >= '0' && bp[j] <= '7')
587           l2 = l2 * 8 + (bp[j] - '0');
588       if (!strcmp((char *)bp, "./control") || !strcmp((char *)bp, "control"))
589         break;
590       l2 = 512 + ((l2 + 511) & ~511);
591       l -= l2;
592       bp += l2;
593     }
594   if (l <= 512 || l - 512 - l2 <= 0 || l2 <= 0)
595     {
596       pool_error(pool, -1, "%s: control.tar.gz contains no control file", deb);
597       free(ctar);
598       return 0;
599     }
600   memmove(ctar, bp + 512, l2);
601   ctar = solv_realloc(ctar, l2 + 1);
602   ctar[l2] = 0;
603   s = pool_id2solvable(pool, repo_add_solvable(repo));
604   control2solvable(s, data, (char *)ctar);
605   if (!(flags & REPO_NO_LOCATION))
606     repodata_set_location(data, s - pool->solvables, 0, 0, deb);
607   if (S_ISREG(stb.st_mode))
608     repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned long long)stb.st_size);
609   if (gotpkgid)
610     repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, pkgid);
611   solv_free(ctar);
612   if (!(flags & REPO_NO_INTERNALIZE))
613     repodata_internalize(data);
614   return s - pool->solvables;
615 }