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