- add support for archlinux packages/repos
[platform/upstream/libsolv.git] / ext / repo_arch.c
1 /*
2  * Copyright (c) 2012, 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 <errno.h>
15 #include <fcntl.h>
16
17 #include "pool.h"
18 #include "repo.h"
19 #include "util.h"
20 #include "chksum.h"
21 #include "solv_xfopen.h"
22 #include "repo_arch.h"
23
24 static long long parsenum(unsigned char *p, int cnt)
25 {
26   long long x = 0;
27   if (!cnt)
28     return -1;
29   if (*p & 0x80)
30     {
31       /* binary format */
32       x = *p & 0x40 ? (-1 << 8 | *p)  : (*p ^ 0x80);
33       while (--cnt > 0)
34         x = (x << 8) | *p++;
35       return x;
36     }
37   while (cnt > 0 && (*p == ' ' || *p == '\t'))
38     cnt--, p++;
39   if (*p == '-')
40     return -1;
41   for (; cnt > 0 && *p >= '0' && *p < '8'; cnt--, p++)
42     x = (x << 3) | (*p - '0');
43   return x;
44 }
45
46 static int readblock(FILE *fp, unsigned char *blk)
47 {
48   int r, l = 0;
49   while (l < 512)
50     {
51       r = fread(blk + l, 1, 512 - l, fp);
52       if (r <= 0)
53         return -1;
54       l += r;
55     }
56   return 0;
57 }
58
59 struct tarhead {
60   FILE *fp;
61   unsigned char blk[512];
62   int type;
63   long long length;
64   char *path;
65   int eof;
66   int ispax;
67   int off;
68   int end;
69 };
70
71 static char *getsentry(struct tarhead *th, char *s, int size)
72 {
73   char *os = s;
74   if (th->eof || size <= 1)
75     return 0;
76   size--;       /* terminating 0 */
77   for (;;)
78     {
79       int i;
80       for (i = th->off; i < th->end; i++)
81         {
82           *s++ = th->blk[i];
83           size--;
84           if (!size || th->blk[i] == '\n')
85             {
86               th->off = i + 1;
87               *s = 0;
88               return os;
89             }
90         }
91       th->off = i;
92       if (th->length <= 0)
93         return 0;
94       if (readblock(th->fp, th->blk))
95         {
96           th->eof = 1;
97           return 0;
98         }
99       th->off = 0;
100       th->end = th->length > 512 ? 512 : th->length;
101       th->length -= th->end;
102     }
103 }
104
105 static void skipentry(struct tarhead *th)
106 {
107   for (; th->length > 0; th->length -= 512)
108     {
109       if (readblock(th->fp, th->blk))
110         {
111           th->eof = 1;
112           th->length = 0;
113           return;
114         }
115     }
116   th->length = 0;
117   th->off = th->end = 0;
118 }
119
120 static void inittarhead(struct tarhead *th, FILE *fp)
121 {
122   memset(th, 0, sizeof(*th));
123   th->fp = fp;
124 }
125
126 static void freetarhead(struct tarhead *th)
127 {
128   solv_free(th->path);
129 }
130
131 static int gettarhead(struct tarhead *th)
132 {
133   int l, type;
134   long long length;
135
136   th->path = solv_free(th->path);
137   th->ispax = 0;
138   th->type = 0;
139   th->length = 0;
140   th->off = 0;
141   th->end = 0;
142   if (th->eof)
143     return 0;
144   for (;;)
145     {
146       int r = readblock(th->fp, th->blk);
147       if (r)
148         {
149           if (feof(th->fp))
150             {
151               th->eof = 1;
152               return 0;
153             }
154           return -1;
155         }
156       if (th->blk[0] == 0)
157         {
158           th->eof = 1;
159           return 0;
160         }
161       length = parsenum(th->blk + 124, 12);
162       if (length < 0)
163         return -1;
164       type = 0;
165       switch (th->blk[156])
166         {
167         case 'S': case '0':
168           type = 1;     /* file */
169           break;
170         case '1':
171           /* hard link, special length magic... */
172           if (!th->ispax)
173             length = 0;
174           break;
175         case '5':
176           type = 2;     /* dir */
177           break;
178         case '2': case '3': case '4': case '6':
179           length = 0;
180           break;
181         case 'X': case 'x': case 'L':
182           {
183             char *data, *pp;
184             if (length < 1 || length >= 1024 * 1024)
185               return -1;
186             l = length;
187             data = pp = solv_malloc(l + 512);
188             for (; l > 0; l -= 512, pp += 512)
189               if (readblock(th->fp, (unsigned char *)pp))
190                 {
191                   solv_free(data);
192                   return -1;
193                 }
194             type = 3;           /* extension */
195             if (th->blk[156] == 'L')
196               {
197                 solv_free(th->path);
198                 th->path = data;
199                 length = 0;
200                 break;
201               }
202             pp = data;
203             while (length > 0)
204               {
205                 int ll = 0;
206                 int l;
207                 for (l = 0; l < length && pp[l] >= '0' && pp[l] <= '9'; l++)
208                   ll = ll * 10 + (pp[l] - '0');
209                 if (l == length || pp[l] != ' ' || ll < 1 || ll > length || pp[ll - 1] != '\n')
210                   {
211                     solv_free(data);
212                     return -1;
213                   }
214                 length -= ll;
215                 pp += l + 1;
216                 ll -= l + 1;
217                 pp[ll - 1] = 0;
218                 if (!strncmp(pp, "path=", 5))
219                   {
220                     solv_free(th->path);
221                     th->path = solv_strdup(pp + 5);
222                   }
223                 pp += ll;
224               }
225             solv_free(data);
226             th->ispax = 1;
227             length = 0;
228             break;
229           }
230         default:
231           type = 3;     /* extension */
232           break;
233         }
234       if ((type == 1 || type == 2) && !th->path)
235         {
236           char path[157];
237           memcpy(path, th->blk, 156);
238           path[156] = 0;
239           if (!memcmp(th->blk + 257, "ustar\0\060\060", 8) && !th->path && th->blk[345])
240             {
241               /* POSIX ustar with prefix */
242               char prefix[156];
243               memcpy(prefix, th->blk + 345, 155);
244               prefix[155] = 0;
245               l = strlen(prefix);
246               if (prefix[l - 1] == '/')
247                 prefix[l - 1] = 0;
248               th->path = solv_dupjoin(prefix, "/", path);
249             }
250           else
251             th->path = solv_dupjoin(path, 0, 0);
252         }
253       if (type == 1 || type == 2)
254         {
255           l = strlen(th->path);
256           if (l && th->path[l - 1] == '/')
257             {
258               if (l > 1)
259                 th->path[l - 1] = 0;
260               type = 2;
261             }
262         }
263       if (type != 3)
264         break;
265       while (length > 0)
266         {
267           r = readblock(th->fp, th->blk);
268           if (r)
269             return r;
270           length -= 512;
271         }
272     }
273   th->type = type;
274   th->length = length;
275   return 1;
276 }
277
278 static Offset
279 adddep(Repo *repo, Offset olddeps, char *line)
280 {
281   Pool *pool = repo->pool;
282   char *p;
283   Id id;
284
285   while (*line == ' ' && *line == '\t')
286     line++;
287   p = line;
288   while (*p && *p != ' ' && *p != '\t' && *p != '<' && *p != '=' && *p != '>')
289     p++;
290   id = pool_strn2id(pool, line, p - line, 1);
291   while (*p == ' ' && *p == '\t')
292     p++;
293   if (*p == '<' || *p == '=' || *p == '>')
294     {
295       int flags = 0;
296       for (;; p++)
297         {  
298           if (*p == '<')
299             flags |= REL_LT;
300           else if (*p == '=')
301             flags |= REL_EQ;
302           else if (*p == '>')
303             flags |= REL_GT;
304           else
305             break;
306         }
307       while (*p == ' ' && *p == '\t')
308         p++;
309       line = p;
310       while (*p && *p != ' ' && *p != '\t')
311         p++;
312       id = pool_rel2id(pool, id, pool_strn2id(pool, line, p - line, 1), flags, 1);
313     }
314   return repo_addid_dep(repo, olddeps, id, 0);
315 }
316
317 Id
318 repo_add_arch_pkg(Repo *repo, const char *fn, int flags)
319 {
320   Pool *pool = repo->pool;
321   Repodata *data;
322   FILE *fp;
323   struct tarhead th;
324   char line[4096];
325   int ignoreline;
326   Solvable *s;
327   int l, fd;
328   struct stat stb;
329   void *pkgidhandle = 0;
330
331   data = repo_add_repodata(repo, flags);
332   if ((fd = open(fn, O_RDONLY, 0)) < 0)
333     {
334       pool_debug(pool, SOLV_ERROR, "repo_add_arch_pkg: %s: %s\n", fn, strerror(errno));
335       return 0;
336     }
337   if (fstat(fd, &stb))
338     {
339       pool_debug(pool, SOLV_ERROR, "repo_add_arch_pkg: %s: fstat failed\n", fn);
340       close(fd);
341       return 0;
342     }
343   if (!(fp = solv_xfopen_fd(fn, fd, "r")))
344     {
345       pool_debug(pool, SOLV_ERROR, "repo_add_arch_pkg: %s: fdopen failed\n", fn);
346       close(fd);
347       return 0;
348     }
349   s = 0;
350   inittarhead(&th, fp);
351   while (gettarhead(&th) > 0)
352     {
353       if (th.type != 1 || strcmp(th.path, ".PKGINFO") != 0)
354         {
355           skipentry(&th);
356           continue;
357         }
358       ignoreline = 0;
359       s = pool_id2solvable(pool, repo_add_solvable(repo));
360       if (flags & ARCH_ADD_WITH_PKGID)
361         pkgidhandle = solv_chksum_create(REPOKEY_TYPE_MD5);
362       while (getsentry(&th, line, sizeof(line)))
363         {
364           l = strlen(line);
365           if (l == 0)
366             continue;
367           if (pkgidhandle)
368             solv_chksum_add(pkgidhandle, line, l);
369           if (line[l - 1] != '\n')
370             {
371               ignoreline = 1;
372               continue;
373             }
374           if (ignoreline)
375             {
376               ignoreline = 0;
377               continue;
378             }
379           line[--l] = 0;
380           if (l == 0 || line[0] == '#')
381             continue;
382           if (!strncmp(line, "pkgname = ", 10))
383             s->name = pool_str2id(pool, line + 10, 1);
384           else if (!strncmp(line, "pkgver = ", 9))
385             s->evr = pool_str2id(pool, line + 9, 1);
386           else if (!strncmp(line, "pkgdesc = ", 10))
387             {
388               repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line + 10);
389               repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, line + 10);
390             }
391           else if (!strncmp(line, "url = ", 6))
392             repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, line + 6);
393           else if (!strncmp(line, "builddate = ", 12))
394             repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, strtoull(line + 12, 0, 10));
395           else if (!strncmp(line, "packager = ", 11))
396             repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_PACKAGER, line + 11);
397           else if (!strncmp(line, "size = ", 7))
398             repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(line + 7, 0, 10));
399           else if (!strncmp(line, "arch = ", 7))
400             s->arch = pool_str2id(pool, line + 7, 1);
401           else if (!strncmp(line, "license = ", 10))
402             repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_LICENSE, line + 10);
403           else if (!strncmp(line, "replaces = ", 11))
404             s->obsoletes = adddep(repo, s->obsoletes, line + 11);
405           else if (!strncmp(line, "group = ", 8))
406             repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_GROUP, line + 8);
407           else if (!strncmp(line, "depend = ", 9))
408             s->requires = adddep(repo, s->requires, line + 9);
409           else if (!strncmp(line, "optdepend = ", 12))
410             {
411               char *p = strchr(line, ':');
412               if (p)
413                 *p = 0;
414               s->suggests = adddep(repo, s->suggests, line + 12);
415             }
416           else if (!strncmp(line, "conflict = ", 11))
417             s->conflicts = adddep(repo, s->conflicts, line + 11);
418           else if (!strncmp(line, "provides = ", 11))
419             s->provides = adddep(repo, s->provides, line + 11);
420         }
421       break;
422     }
423   freetarhead(&th);
424   fclose(fp);
425   if (s && !s->name)
426     {
427       repo_free_solvable_block(repo, s - pool->solvables, 1, 1);
428       s = 0;
429     }
430   if (s)
431     {
432       if (!s->arch)
433         s->arch = ARCH_NOARCH;
434       if (!s->evr)
435         s->evr = ID_EMPTY;
436       s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
437       repodata_set_location(data, s - pool->solvables, 0, 0, fn);
438       repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, (unsigned long long)stb.st_size);
439       if (pkgidhandle)
440         {
441           unsigned char pkgid[16];
442           solv_chksum_free(pkgidhandle, pkgid);
443           repodata_set_bin_checksum(data, s - pool->solvables, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, pkgid);
444           pkgidhandle = 0;
445         }
446     }
447   if (pkgidhandle)
448     solv_chksum_free(pkgidhandle, 0);
449   if (!(flags & REPO_NO_INTERNALIZE))
450     repodata_internalize(data);
451   return s ? s - pool->solvables : 0;
452 }
453
454 static char *getsentrynl(struct tarhead *th, char *s, int size)
455 {
456   int l;
457   if (!getsentry(th, s, size))
458     {
459       *s = 0;   /* eof */
460       return 0;
461     }
462   l = strlen(s);
463   if (!l)
464     return 0;
465   if (l && s[l - 1] == '\n')
466     {
467       s[l - 1] = 0;
468       return s;
469     }
470   while (getsentry(th, s, size))
471     {
472       l = strlen(s);
473       if (!l || s[l - 1] == '\n')
474         return 0;
475     }
476   *s = 0;       /* eof */
477   return 0;
478 }
479
480 int
481 repo_add_arch_repo(Repo *repo, FILE *fp, int flags)
482 {
483   Pool *pool = repo->pool;
484   Repodata *data;
485   data = repo_add_repodata(repo, flags);
486   struct tarhead th;
487   char *lastdn = 0;
488   int l, lastdnlen = 0;
489   char line[4096];
490   Solvable *s = 0;
491   int havesha256 = 0;
492
493   inittarhead(&th, fp);
494   while (gettarhead(&th) > 0)
495     {
496       char *bn;
497       if (th.type != 1)
498         {
499           skipentry(&th);
500           continue;
501         }
502       bn = strrchr(th.path, '/');
503       if (!bn || (strcmp(bn + 1, "desc") != 0 && strcmp(bn + 1, "depends") != 0))
504         {
505           skipentry(&th);
506           continue;
507         }
508       if (!lastdn || (bn - th.path) != lastdnlen || strncmp(lastdn, th.path, lastdnlen) != 0)
509         {
510           if (s)
511             {
512               if (s && !s->name)
513                 {
514                   repo_free_solvable_block(repo, s - pool->solvables, 1, 1);
515                   s = 0;
516                 }
517               if (s)
518                 {
519                   if (!s->arch)
520                     s->arch = ARCH_NOARCH;
521                   if (!s->evr)
522                     s->evr = ID_EMPTY;
523                   s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
524                 }
525             }
526           solv_free(lastdn);
527           lastdn = solv_strdup(th.path);
528           lastdnlen = bn - th.path;
529           s = pool_id2solvable(pool, repo_add_solvable(repo));
530           havesha256 = 0;
531         }
532       while (getsentry(&th, line, sizeof(line)))
533         {
534           l = strlen(line);
535           if (l == 0 || line[l - 1] != '\n')
536             continue;
537           line[--l] = 0;
538           if (l <= 2 || line[0] != '%' || line[l - 1] != '%')
539             continue;
540           if (!strcmp(line, "%FILENAME%"))
541             {
542               if (getsentrynl(&th, line, sizeof(line)))
543                 repodata_set_location(data, s - pool->solvables, 0, 0, line);
544             }
545           else if (!strcmp(line, "%NAME%"))
546             {
547               if (getsentrynl(&th, line, sizeof(line)))
548                 s->name = pool_str2id(pool, line, 1);
549             }
550           else if (!strcmp(line, "%VERSION%"))
551             {
552               if (getsentrynl(&th, line, sizeof(line)))
553                 s->evr = pool_str2id(pool, line, 1);
554             }
555           else if (!strcmp(line, "%DESC%"))
556             {
557               if (getsentrynl(&th, line, sizeof(line)))
558                 {
559                   repodata_set_str(data, s - pool->solvables, SOLVABLE_SUMMARY, line);
560                   repodata_set_str(data, s - pool->solvables, SOLVABLE_DESCRIPTION, line);
561                 }
562             }
563           else if (!strcmp(line, "%GROUPS%"))
564             {
565               if (getsentrynl(&th, line, sizeof(line)))
566                 repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_GROUP, line);
567             }
568           else if (!strcmp(line, "%CSIZE%"))
569             {
570               if (getsentrynl(&th, line, sizeof(line)))
571                 repodata_set_num(data, s - pool->solvables, SOLVABLE_DOWNLOADSIZE, strtoull(line, 0, 10));
572             }
573           else if (!strcmp(line, "%ISIZE%"))
574             {
575               if (getsentrynl(&th, line, sizeof(line)))
576                 repodata_set_num(data, s - pool->solvables, SOLVABLE_INSTALLSIZE, strtoull(line, 0, 10));
577             }
578           else if (!strcmp(line, "%MD5SUM%"))
579             {
580               if (getsentrynl(&th, line, sizeof(line)) && !havesha256)
581                 repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, REPOKEY_TYPE_MD5, line);
582             }
583           else if (!strcmp(line, "%SHA256SUM%"))
584             {
585               if (getsentrynl(&th, line, sizeof(line)))
586                 {
587                   repodata_set_checksum(data, s - pool->solvables, SOLVABLE_CHECKSUM, REPOKEY_TYPE_SHA256, line);
588                   havesha256 = 1;
589                 }
590             }
591           else if (!strcmp(line, "%URL%"))
592             {
593               if (getsentrynl(&th, line, sizeof(line)))
594                 repodata_set_str(data, s - pool->solvables, SOLVABLE_URL, line);
595             }
596           else if (!strcmp(line, "%LICENSE%"))
597             {
598               if (getsentrynl(&th, line, sizeof(line)))
599                 repodata_set_str(data, s - pool->solvables, SOLVABLE_LICENSE, line);
600             }
601           else if (!strcmp(line, "%ARCH%"))
602             {
603               if (getsentrynl(&th, line, sizeof(line)))
604                 s->arch = pool_str2id(pool, line, 1);
605             }
606           else if (!strcmp(line, "%BUILDDATE%"))
607             {
608               if (getsentrynl(&th, line, sizeof(line)))
609                 repodata_set_num(data, s - pool->solvables, SOLVABLE_BUILDTIME, strtoull(line, 0, 10));
610             }
611           else if (!strcmp(line, "%PACKAGER%"))
612             {
613               if (getsentrynl(&th, line, sizeof(line)))
614                 repodata_set_poolstr(data, s - pool->solvables, SOLVABLE_PACKAGER, line);
615             }
616           else if (!strcmp(line, "%REPLACES%"))
617             {
618               while (getsentrynl(&th, line, sizeof(line)) && *line)
619                 s->obsoletes = adddep(repo, s->obsoletes, line);
620             }
621           else if (!strcmp(line, "%DEPENDS%"))
622             {
623               while (getsentrynl(&th, line, sizeof(line)) && *line)
624                 s->requires = adddep(repo, s->requires, line);
625             }
626           else if (!strcmp(line, "%CONFLICTS%"))
627             {
628               while (getsentrynl(&th, line, sizeof(line)) && *line)
629                 s->conflicts = adddep(repo, s->conflicts, line);
630             }
631           else if (!strcmp(line, "%PROVIDES%"))
632             {
633               while (getsentrynl(&th, line, sizeof(line)) && *line)
634                 s->provides = adddep(repo, s->provides, line);
635             }
636           else if (!strcmp(line, "%OPTDEPENDS%"))
637             {
638               while (getsentrynl(&th, line, sizeof(line)) && *line)
639                 {
640                   char *p = strchr(line, ':');
641                   if (p && p > line)
642                     *p = 0;
643                   s->suggests = adddep(repo, s->suggests, line);
644                 }
645             }
646           while (*line)
647             getsentrynl(&th, line, sizeof(line));
648         }
649     }
650   if (s)
651     {
652       if (s && !s->name)
653         {
654           repo_free_solvable_block(repo, s - pool->solvables, 1, 1);
655           s = 0;
656         }
657       if (s)
658         {
659           if (!s->arch)
660             s->arch = ARCH_NOARCH;
661           if (!s->evr)
662             s->evr = ID_EMPTY;
663           s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
664         }
665     }
666   if (!(flags & REPO_NO_INTERNALIZE))
667     repodata_internalize(data);
668   return 0;
669 }
670