implement pool_set_rootdir/REPO_USE_ROOTDIR instead of passing a rootdir to various...
[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   name = pool_strn2id(pool, n, ne - n, 1);
126   if (e)
127     {
128       evr = pool_strn2id(pool, e, ee - e, 1);
129       name = pool_rel2id(pool, name, evr, flags, 1);
130     }
131   if (*p == '|')
132     {
133       Id id = parseonedep(pool, p + 1);
134       if (id)
135         name = pool_rel2id(pool, name, id, REL_OR, 1);
136     }
137   return name;
138 }
139
140 static unsigned int
141 makedeps(Repo *repo, char *deps, unsigned int olddeps, Id marker)
142 {
143   Pool *pool = repo->pool;
144   char *p;
145   Id id;
146
147   while ((p = strchr(deps, ',')) != 0)
148     {
149       *p = 0;
150       olddeps = makedeps(repo, deps, olddeps, marker);
151       *p = ',';
152       deps = p + 1;
153     }
154   id = parseonedep(pool, deps);
155   if (!id)
156     return olddeps;
157   return repo_addid_dep(repo, olddeps, id, marker);
158 }
159
160
161 /* put data from control file into the solvable */
162 /* warning: does inplace changes */
163 static void
164 control2solvable(Solvable *s, Repodata *data, char *control)
165 {
166   Repo *repo = s->repo;
167   Pool *pool = repo->pool;
168   char *p, *q, *end, *tag;
169   int x, l;
170   int havesource = 0;
171   char checksum[32 * 2 + 1];
172   Id checksumtype = 0;
173   Id newtype;
174
175   p = control;
176   while (*p)
177     {
178       p = strchr(p, '\n');
179       if (!p)
180         break;
181       if (p[1] == ' ' || p[1] == '\t')
182         {
183           char *q;
184           /* continuation line */
185           q = p - 1;
186           while (q >= control && *q == ' ' && *q == '\t')
187             q--;
188           l = q + 1 - control;
189           if (l)
190             memmove(p + 1 - l, control, l);
191           control = p + 1 - l;
192           p[1] = '\n';
193           p += 2;
194           continue;
195         }
196       end = p - 1;
197       if (*p)
198         *p++ = 0;
199       /* strip trailing space */
200       while (end >= control && *end == ' ' && *end == '\t')
201         *end-- = 0;
202       tag = control;
203       control = p;
204       q = strchr(tag, ':');
205       if (!q || q - tag < 4)
206         continue;
207       *q++ = 0;
208       while (*q == ' ' || *q == '\t')
209         q++;
210       x = '@' + (tag[0] & 0x1f);
211       x = (x << 8) + '@' + (tag[1] & 0x1f);
212       switch(x)
213         {
214         case 'A' << 8 | 'R':
215           if (!strcasecmp(tag, "architecture"))
216             s->arch = pool_str2id(pool, q, 1);
217           break;
218         case 'B' << 8 | 'R':
219           if (!strcasecmp(tag, "breaks"))
220             s->conflicts = makedeps(repo, q, s->conflicts, 0);
221           break;
222         case 'C' << 8 | 'O':
223           if (!strcasecmp(tag, "conflicts"))
224             s->conflicts = makedeps(repo, q, s->conflicts, 0);
225           break;
226         case 'D' << 8 | 'E':
227           if (!strcasecmp(tag, "depends"))
228             s->requires = makedeps(repo, q, s->requires, -SOLVABLE_PREREQMARKER);
229           else if (!strcasecmp(tag, "description"))
230             {
231               char *ld = strchr(q, '\n');
232               if (ld)
233                 {
234                   *ld++ = 0;
235                   repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, ld);
236                 }
237               else
238                 repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, q);
239               repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, q);
240             }
241           break;
242         case 'E' << 8 | 'N':
243           if (!strcasecmp(tag, "enhances"))
244             s->enhances = makedeps(repo, q, s->enhances, 0);
245           break;
246         case 'F' << 8 | 'I':
247           if (!strcasecmp(tag, "filename"))
248             repodata_set_location(data, s - pool->solvables, 0, 0, q);
249           break;
250         case 'H' << 8 | 'O':
251           if (!strcasecmp(tag, "homepage"))
252             repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, q);
253           break;
254         case 'I' << 8 | 'N':
255           if (!strcasecmp(tag, "installed-size"))
256             repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(q, 0, 10) << 10);
257           break;
258         case 'M' << 8 | 'D':
259           if (!strcasecmp(tag, "md5sum") && !checksumtype && strlen(q) == 16 * 2)
260             {
261               strcpy(checksum, q);
262               checksumtype = REPOKEY_TYPE_MD5;
263             }
264           break;
265         case 'P' << 8 | 'A':
266           if (!strcasecmp(tag, "package"))
267             s->name = pool_str2id(pool, q, 1);
268           break;
269         case 'P' << 8 | 'R':
270           if (!strcasecmp(tag, "pre-depends"))
271             s->requires = makedeps(repo, q, s->requires, SOLVABLE_PREREQMARKER);
272           else if (!strcasecmp(tag, "provides"))
273             s->provides = makedeps(repo, q, s->provides, 0);
274           break;
275         case 'R' << 8 | 'E':
276           if (!strcasecmp(tag, "replaces"))
277             s->obsoletes = makedeps(repo, q, s->conflicts, 0);
278           else if (!strcasecmp(tag, "recommends"))
279             s->recommends = makedeps(repo, q, s->recommends, 0);
280           break;
281         case 'S' << 8 | 'H':
282           newtype = solv_chksum_str2type(tag);
283           if (!newtype || solv_chksum_len(newtype) * 2 != strlen(q))
284             break;
285           if (!checksumtype || (newtype == REPOKEY_TYPE_SHA1 && checksumtype != REPOKEY_TYPE_SHA256) || newtype == REPOKEY_TYPE_SHA256)
286             {
287               strcpy(checksum, q);
288               checksumtype = newtype;
289             }
290           break;
291         case 'S' << 8 | 'O':
292           if (!strcasecmp(tag, "source"))
293             {
294               char *q2;
295               /* ignore version for now */
296               for (q2 = q; *q2; q2++)
297                 if (*q2 == ' ' || *q2 == '\t')
298                   {
299                     *q2 = 0;
300                     break;
301                   }
302               if (s->name && !strcmp(q, pool_id2str(pool, s->name)))
303                 repodata_set_void(data, s - pool->solvables, SOLVABLE_SOURCENAME);
304               else
305                 repodata_set_id(data, s - pool->solvables, SOLVABLE_SOURCENAME, pool_str2id(pool, q, 1));
306               havesource = 1;
307             }
308           break;
309         case 'S' << 8 | 'U':
310           if (!strcasecmp(tag, "suggests"))
311             s->suggests = makedeps(repo, q, s->suggests, 0);
312           break;
313         case 'V' << 8 | 'E':
314           if (!strcasecmp(tag, "version"))
315             s->evr = pool_str2id(pool, q, 1);
316           break;
317         }
318     }
319   if (checksumtype)
320     repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, checksumtype, checksum);
321   if (!s->arch)
322     s->arch = ARCH_ALL;
323   if (!s->evr)
324     s->evr = ID_EMPTY;
325   if (s->name)
326     s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
327   if (s->name && !havesource)
328     repodata_set_void(data, s - pool->solvables, SOLVABLE_SOURCENAME);
329   if (s->obsoletes)
330     {
331       /* obsoletes only count when the packages also conflict */
332       int i, j, k;
333       Id d;
334       for (i = j = s->obsoletes; (d = repo->idarraydata[i]) != 0; i++)
335         {
336           if (s->conflicts)
337             {
338               for (k = s->conflicts; repo->idarraydata[k] != 0; k++)
339                 if (repo->idarraydata[k] == d)
340                   break;
341               if (repo->idarraydata[k])
342                 {
343                   repo->idarraydata[j++] = d;
344                 }
345             }
346         }
347       repo->idarraydata[j] = 0;
348     }
349 }
350
351 int
352 repo_add_debpackages(Repo *repo, FILE *fp, int flags)
353 {
354   Pool *pool = repo->pool;
355   Repodata *data;
356   char *buf, *p;
357   int bufl, l, ll;
358   Solvable *s;
359
360   data = repo_add_repodata(repo, flags);
361   buf = solv_malloc(4096);
362   bufl = 4096;
363   l = 0;
364   buf[l] = 0;
365   p = buf;
366   for (;;)
367     {
368       if (!(p = strchr(p, '\n')))
369         {
370           int l3;
371           if (l + 1024 >= bufl)
372             {
373               buf = solv_realloc(buf, bufl + 4096);
374               bufl += 4096;
375               p = buf + l;
376               continue;
377             }
378           p = buf + l;
379           ll = fread(p, 1, bufl - l - 1, fp);
380           if (ll <= 0)
381             break;
382           p[ll] = 0;
383           while ((l3 = strlen(p)) < ll)
384             p[l3] = '\n';
385           l += ll;
386           continue;
387         }
388       p++;
389       if (*p != '\n')
390         continue;
391       *p = 0;
392       ll = p - buf + 1;
393       s = pool_id2solvable(pool, repo_add_solvable(repo));
394       control2solvable(s, data, buf);
395       if (!s->name)
396         repo_free_solvable(repo, s - pool->solvables, 1);
397       if (l > ll)
398         memmove(buf, p + 1, l - ll);
399       l -= ll;
400       p = buf;
401       buf[l] = 0;
402     }
403   if (l)
404     {
405       s = pool_id2solvable(pool, repo_add_solvable(repo));
406       control2solvable(s, data, buf);
407       if (!s->name)
408         repo_free_solvable(repo, s - pool->solvables, 1);
409     }
410   solv_free(buf);
411   if (!(flags & REPO_NO_INTERNALIZE))
412     repodata_internalize(data);
413   return 0;
414 }
415
416 int
417 repo_add_debdb(Repo *repo, int flags)
418 {
419   FILE *fp;
420   const char *path = "/var/lib/dpkg/status";
421   if (flags & REPO_USE_ROOTDIR)
422     path = pool_prepend_rootdir_tmp(repo->pool, path);
423   if ((fp = fopen(path, "r")) == 0)
424     return pool_error(repo->pool, -1, "%s: %s", path, strerror(errno));
425   repo_add_debpackages(repo, fp, flags);
426   fclose(fp);
427   return 0;
428 }
429
430 Id
431 repo_add_deb(Repo *repo, const char *deb, int flags)
432 {
433   Pool *pool = repo->pool;
434   Repodata *data;
435   unsigned char buf[4096], *bp;
436   int l, l2, vlen, clen, ctarlen;
437   unsigned char *ctgz;
438   unsigned char pkgid[16];
439   unsigned char *ctar;
440   int gotpkgid;
441   FILE *fp;
442   Solvable *s;
443   struct stat stb;
444
445   data = repo_add_repodata(repo, flags);
446   if ((fp = fopen(flags & REPO_USE_ROOTDIR ? pool_prepend_rootdir_tmp(pool, deb) : deb, "r")) == 0)
447     {
448       pool_error(pool, -1, "%s: %s", deb, strerror(errno));
449       return 0;
450     }
451   if (fstat(fileno(fp), &stb))
452     {
453       pool_error(pool, -1, "fstat: %s", strerror(errno));
454       fclose(fp);
455       return 0;
456     }
457   l = fread(buf, 1, sizeof(buf), fp);
458   if (l < 8 + 60 || strncmp((char *)buf, "!<arch>\ndebian-binary   ", 8 + 16) != 0)
459     {
460       pool_error(pool, -1, "%s: not a deb package", deb);
461       fclose(fp);
462       return 0;
463     }
464   vlen = atoi((char *)buf + 8 + 48);
465   if (vlen < 0 || vlen > l)
466     {
467       pool_error(pool, -1, "%s: not a deb package", deb);
468       fclose(fp);
469       return 0;
470     }
471   vlen += vlen & 1;
472   if (l < 8 + 60 + vlen + 60)
473     {
474       pool_error(pool, -1, "%s: unhandled deb package", deb);
475       fclose(fp);
476       return 0;
477     }
478   if (strncmp((char *)buf + 8 + 60 + vlen, "control.tar.gz  ", 16) != 0)
479     {
480       pool_error(pool, -1, "%s: control.tar.gz is not second entry", deb);
481       fclose(fp);
482       return 0;
483     }
484   clen = atoi((char *)buf + 8 + 60 + vlen + 48);
485   if (clen <= 0 || clen >= 0x100000)
486     {
487       pool_error(pool, -1, "%s: control.tar.gz has illegal size", deb);
488       fclose(fp);
489       return 0;
490     }
491   ctgz = solv_calloc(1, clen + 4);
492   bp = buf + 8 + 60 + vlen + 60;
493   l -= 8 + 60 + vlen + 60;
494   if (l > clen)
495     l = clen;
496   if (l)
497     memcpy(ctgz, bp, l);
498   if (l < clen)
499     {
500       if (fread(ctgz + l, clen - l, 1, fp) != 1)
501         {
502           pool_error(pool, -1, "%s: unexpected EOF", deb);
503           solv_free(ctgz);
504           fclose(fp);
505           return 0;
506         }
507     }
508   fclose(fp);
509   gotpkgid = 0;
510   if (flags & DEBS_ADD_WITH_PKGID)
511     {
512       void *handle = solv_chksum_create(REPOKEY_TYPE_MD5);
513       solv_chksum_add(handle, ctgz, clen);
514       solv_chksum_free(handle, pkgid);
515       gotpkgid = 1;
516     }
517   if (ctgz[0] != 0x1f || ctgz[1] != 0x8b)
518     {
519       pool_error(pool, -1, "%s: control.tar.gz is not gzipped", deb);
520       solv_free(ctgz);
521       return 0;
522     }
523   if (ctgz[2] != 8 || (ctgz[3] & 0xe0) != 0)
524     {
525       pool_error(pool, -1, "%s: control.tar.gz is compressed in a strange way", deb);
526       solv_free(ctgz);
527       return 0;
528     }
529   bp = ctgz + 4;
530   bp += 6;      /* skip time, xflags and OS code */
531   if (ctgz[3] & 0x04)
532     {
533       /* skip extra field */
534       l = bp[0] | bp[1] << 8;
535       bp += l + 2;
536       if (bp >= ctgz + clen)
537         {
538           pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb);
539           solv_free(ctgz);
540           return 0;
541         }
542     }
543   if (ctgz[3] & 0x08)   /* orig filename */
544     while (*bp)
545       bp++;
546   if (ctgz[3] & 0x10)   /* file comment */
547     while (*bp)
548       bp++;
549   if (ctgz[3] & 0x02)   /* header crc */
550     bp += 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   ctar = decompress(bp, ctgz + clen - bp, &ctarlen);
558   solv_free(ctgz);
559   if (!ctar)
560     {
561       pool_error(pool, -1, "%s: control.tar.gz is corrupt", deb);
562       return 0;
563     }
564   bp = ctar;
565   l = ctarlen;
566   while (l > 512)
567     {
568       int j;
569       l2 = 0;
570       for (j = 124; j < 124 + 12; j++)
571         if (bp[j] >= '0' && bp[j] <= '7')
572           l2 = l2 * 8 + (bp[j] - '0');
573       if (!strcmp((char *)bp, "./control") || !strcmp((char *)bp, "control"))
574         break;
575       l2 = 512 + ((l2 + 511) & ~511);
576       l -= l2;
577       bp += l2;
578     }
579   if (l <= 512 || l - 512 - l2 <= 0 || l2 <= 0)
580     {
581       pool_error(pool, -1, "%s: control.tar.gz contains no control file", deb);
582       free(ctar);
583       return 0;
584     }
585   memmove(ctar, bp + 512, l2);
586   ctar = solv_realloc(ctar, l2 + 1);
587   ctar[l2] = 0;
588   s = pool_id2solvable(pool, repo_add_solvable(repo));
589   control2solvable(s, data, (char *)ctar);
590   repodata_set_location(data, s - pool->solvables, 0, 0, deb);
591   if (S_ISREG(stb.st_mode))
592     repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned long long)stb.st_size);
593   if (gotpkgid)
594     repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, pkgid);
595   solv_free(ctar);
596   if (!(flags & REPO_NO_INTERNALIZE))
597     repodata_internalize(data);
598   return s - pool->solvables;
599 }