Fresh pull from upstream
[librecmc/librecmc.git] / package / system / opkg / patches / 070-use_external_gzip.patch
1 --- a/libbb/unarchive.c
2 +++ b/libbb/unarchive.c
3 @@ -28,6 +28,7 @@
4  #include <libgen.h>
5  
6  #include "libbb.h"
7 +#include "gzip.h"
8  
9  #define CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY 1
10  #define CONFIG_FEATURE_TAR_GNU_EXTENSIONS
11 @@ -39,38 +40,15 @@ static char *linkname = NULL;
12  
13  off_t archive_offset;
14  
15 -#define SEEK_BUF 4096
16  static ssize_t
17 -seek_by_read(FILE* fd, size_t len)
18 -{
19 -        ssize_t cc, total = 0;
20 -        char buf[SEEK_BUF];
21 -
22 -        while (len) {
23 -                cc = fread(buf, sizeof(buf[0]),
24 -                                len > SEEK_BUF ? SEEK_BUF : len,
25 -                                fd);
26 -
27 -                total += cc;
28 -                len -= cc;
29 -
30 -                if(feof(fd) || ferror(fd))
31 -                        break;
32 -        }
33 -        return total;
34 -}
35 -
36 -static void
37 -seek_sub_file(FILE *fd, const int count)
38 +seek_forward(struct gzip_handle *zh, ssize_t len)
39  {
40 -       archive_offset += count;
41 +       ssize_t slen = gzip_seek(zh, len);
42  
43 -       /* Do not use fseek() on a pipe. It may fail with ESPIPE, leaving the
44 -        * stream at an undefined location.
45 -        */
46 -        seek_by_read(fd, count);
47 +       if (slen == len)
48 +               archive_offset += len;
49  
50 -       return;
51 +       return slen;
52  }
53  
54  
55 @@ -87,7 +65,7 @@ seek_sub_file(FILE *fd, const int count)
56   * trailing '/' or else the last dir will be assumed to be the file prefix
57   */
58  static char *
59 -extract_archive(FILE *src_stream, FILE *out_stream,
60 +extract_archive(struct gzip_handle *src_stream, FILE *out_stream,
61                 const file_header_t *file_entry, const int function,
62                 const char *prefix,
63                 int *err)
64 @@ -129,14 +107,14 @@ extract_archive(FILE *src_stream, FILE *
65  
66         if (function & extract_to_stream) {
67                 if (S_ISREG(file_entry->mode)) {
68 -                       *err = copy_file_chunk(src_stream, out_stream, file_entry->size);
69 +                       *err = gzip_copy(src_stream, out_stream, file_entry->size);
70                         archive_offset += file_entry->size;
71                 }
72         }
73         else if (function & extract_one_to_buffer) {
74                 if (S_ISREG(file_entry->mode)) {
75                         buffer = (char *) xmalloc(file_entry->size + 1);
76 -                       fread(buffer, 1, file_entry->size, src_stream);
77 +                       gzip_read(src_stream, buffer, file_entry->size);
78                         buffer[file_entry->size] = '\0';
79                         archive_offset += file_entry->size;
80                         goto cleanup;
81 @@ -156,7 +134,7 @@ extract_archive(FILE *src_stream, FILE *
82                                         *err = -1;
83                                         error_msg("%s not created: newer or same age file exists", file_entry->name);
84                                 }
85 -                               seek_sub_file(src_stream, file_entry->size);
86 +                               seek_forward(src_stream, file_entry->size);
87                                 goto cleanup;
88                         }
89                 }
90 @@ -185,11 +163,11 @@ extract_archive(FILE *src_stream, FILE *
91                                 } else {
92                                         if ((dst_stream = wfopen(full_name, "w")) == NULL) {
93                                                 *err = -1;
94 -                                               seek_sub_file(src_stream, file_entry->size);
95 +                                               seek_forward(src_stream, file_entry->size);
96                                                 goto cleanup;
97                                         }
98                                         archive_offset += file_entry->size;
99 -                                       *err = copy_file_chunk(src_stream, dst_stream, file_entry->size);
100 +                                       *err = gzip_copy(src_stream, dst_stream, file_entry->size);
101                                         fclose(dst_stream);
102                                 }
103                                 break;
104 @@ -250,7 +228,7 @@ extract_archive(FILE *src_stream, FILE *
105                 /* If we arent extracting data we have to skip it,
106                  * if data size is 0 then then just do it anyway
107                  * (saves testing for it) */
108 -               seek_sub_file(src_stream, file_entry->size);
109 +               seek_forward(src_stream, file_entry->size);
110         }
111  
112         /* extract_list and extract_verbose_list can be used in conjunction
113 @@ -274,8 +252,8 @@ cleanup:
114  }
115  
116  static char *
117 -unarchive(FILE *src_stream, FILE *out_stream,
118 -               file_header_t *(*get_headers)(FILE *),
119 +unarchive(struct gzip_handle *src_stream, FILE *out_stream,
120 +               file_header_t *(*get_headers)(struct gzip_handle *),
121                 void (*free_headers)(file_header_t *),
122                 const int extract_function,
123                 const char *prefix,
124 @@ -329,7 +307,7 @@ unarchive(FILE *src_stream, FILE *out_st
125                         }
126                 } else {
127                         /* seek past the data entry */
128 -                       seek_sub_file(src_stream, file_entry->size);
129 +                       seek_forward(src_stream, file_entry->size);
130                 }
131                 free_headers(file_entry);
132         }
133 @@ -337,108 +315,9 @@ unarchive(FILE *src_stream, FILE *out_st
134         return buffer;
135  }
136  
137 -static file_header_t *
138 -get_header_ar(FILE *src_stream)
139 -{
140 -       file_header_t *typed;
141 -       union {
142 -               char raw[60];
143 -               struct {
144 -                       char name[16];
145 -                       char date[12];
146 -                       char uid[6];
147 -                       char gid[6];
148 -                       char mode[8];
149 -                       char size[10];
150 -                       char magic[2];
151 -               } formated;
152 -       } ar;
153 -       static char *ar_long_names;
154 -
155 -       if (fread(ar.raw, 1, 60, src_stream) != 60) {
156 -               return(NULL);
157 -       }
158 -       archive_offset += 60;
159 -       /* align the headers based on the header magic */
160 -       if ((ar.formated.magic[0] != '`') || (ar.formated.magic[1] != '\n')) {
161 -               /* some version of ar, have an extra '\n' after each data entry,
162 -                * this puts the next header out by 1 */
163 -               if (ar.formated.magic[1] != '`') {
164 -                       error_msg("Invalid magic");
165 -                       return(NULL);
166 -               }
167 -               /* read the next char out of what would be the data section,
168 -                * if its a '\n' then it is a valid header offset by 1*/
169 -               archive_offset++;
170 -               if (fgetc(src_stream) != '\n') {
171 -                       error_msg("Invalid magic");
172 -                       return(NULL);
173 -               }
174 -               /* fix up the header, we started reading 1 byte too early */
175 -               /* raw_header[60] wont be '\n' as it should, but it doesnt matter */
176 -               memmove(ar.raw, &ar.raw[1], 59);
177 -       }
178 -
179 -       typed = (file_header_t *) xcalloc(1, sizeof(file_header_t));
180 -
181 -       typed->size = (size_t) atoi(ar.formated.size);
182 -       /* long filenames have '/' as the first character */
183 -       if (ar.formated.name[0] == '/') {
184 -               if (ar.formated.name[1] == '/') {
185 -                       /* If the second char is a '/' then this entries data section
186 -                        * stores long filename for multiple entries, they are stored
187 -                        * in static variable long_names for use in future entries */
188 -                       ar_long_names = (char *) xrealloc(ar_long_names, typed->size);
189 -                       fread(ar_long_names, 1, typed->size, src_stream);
190 -                       archive_offset += typed->size;
191 -                       /* This ar entries data section only contained filenames for other records
192 -                        * they are stored in the static ar_long_names for future reference */
193 -                       return (get_header_ar(src_stream)); /* Return next header */
194 -               } else if (ar.formated.name[1] == ' ') {
195 -                       /* This is the index of symbols in the file for compilers */
196 -                       seek_sub_file(src_stream, typed->size);
197 -                       return (get_header_ar(src_stream)); /* Return next header */
198 -               } else {
199 -                       /* The number after the '/' indicates the offset in the ar data section
200 -                       (saved in variable long_name) that conatains the real filename */
201 -                       if (!ar_long_names) {
202 -                               error_msg("Cannot resolve long file name");
203 -                               return (NULL);
204 -                       }
205 -                       typed->name = xstrdup(ar_long_names + atoi(&ar.formated.name[1]));
206 -               }
207 -       } else {
208 -               /* short filenames */
209 -               typed->name = xcalloc(1, 16);
210 -               strncpy(typed->name, ar.formated.name, 16);
211 -       }
212 -       typed->name[strcspn(typed->name, " /")]='\0';
213 -
214 -       /* convert the rest of the now valid char header to its typed struct */
215 -       parse_mode(ar.formated.mode, &typed->mode);
216 -       typed->mtime = atoi(ar.formated.date);
217 -       typed->uid = atoi(ar.formated.uid);
218 -       typed->gid = atoi(ar.formated.gid);
219 -
220 -       return(typed);
221 -}
222 -
223 -static void
224 -free_header_ar(file_header_t *ar_entry)
225 -{
226 -       if (ar_entry == NULL)
227 -               return;
228 -
229 -       free(ar_entry->name);
230 -       if (ar_entry->link_name)
231 -               free(ar_entry->link_name);
232 -
233 -       free(ar_entry);
234 -}
235 -
236  
237  static file_header_t *
238 -get_header_tar(FILE *tar_stream)
239 +get_header_tar(struct gzip_handle *tar_stream)
240  {
241         union {
242                 unsigned char raw[512];
243 @@ -467,10 +346,10 @@ get_header_tar(FILE *tar_stream)
244         long sum = 0;
245  
246         if (archive_offset % 512 != 0) {
247 -               seek_sub_file(tar_stream, 512 - (archive_offset % 512));
248 +               seek_forward(tar_stream, 512 - (archive_offset % 512));
249         }
250  
251 -       if (fread(tar.raw, 1, 512, tar_stream) != 512) {
252 +       if (gzip_read(tar_stream, tar.raw, 512) != 512) {
253                 /* Unfortunately its common for tar files to have all sorts of
254                  * trailing garbage, fail silently */
255  //             error_msg("Couldnt read header");
256 @@ -557,7 +436,7 @@ get_header_tar(FILE *tar_stream)
257  # ifdef CONFIG_FEATURE_TAR_GNU_EXTENSIONS
258         case 'L': {
259                         longname = xmalloc(tar_entry->size + 1);
260 -                        if(fread(longname, tar_entry->size, 1, tar_stream) != 1)
261 +                        if(gzip_read(tar_stream, longname, tar_entry->size) != tar_entry->size)
262                                  return NULL;
263                         longname[tar_entry->size] = '\0';
264                         archive_offset += tar_entry->size;
265 @@ -566,7 +445,7 @@ get_header_tar(FILE *tar_stream)
266                 }
267         case 'K': {
268                         linkname = xmalloc(tar_entry->size + 1);
269 -                        if(fread(linkname, tar_entry->size, 1, tar_stream) != 1)
270 +                        if(gzip_read(tar_stream, linkname, tar_entry->size) != tar_entry->size)
271                                  return NULL;
272                         linkname[tar_entry->size] = '\0';
273                         archive_offset += tar_entry->size;
274 @@ -642,6 +521,9 @@ deb_extract(const char *package_filename
275         char *ared_file = NULL;
276         char ar_magic[8];
277         int gz_err;
278 +       struct gzip_handle tar_outer, tar_inner;
279 +       file_header_t *tar_header;
280 +       ssize_t len;
281  
282         *err = 0;
283  
284 @@ -672,111 +554,44 @@ deb_extract(const char *package_filename
285         /* set the buffer size */
286         setvbuf(deb_stream, NULL, _IOFBF, 0x8000);
287  
288 -       /* check ar magic */
289 -       fread(ar_magic, 1, 8, deb_stream);
290 -
291 -       if (strncmp(ar_magic,"!<arch>",7) == 0) {
292 -               archive_offset = 8;
293 +       memset(&tar_outer, 0, sizeof(tar_outer));
294 +       tar_outer.file = deb_stream;
295 +       gzip_exec(&tar_outer, NULL);
296  
297 -               while ((ar_header = get_header_ar(deb_stream)) != NULL) {
298 -                       if (strcmp(ared_file, ar_header->name) == 0) {
299 -                               int gunzip_pid = 0;
300 -                               FILE *uncompressed_stream;
301 -                               /* open a stream of decompressed data */
302 -                               uncompressed_stream = gz_open(deb_stream, &gunzip_pid);
303 -                               if (uncompressed_stream == NULL) {
304 -                                       *err = -1;
305 -                                       goto cleanup;
306 -                               }
307 +       /* walk through outer tar file to find ared_file */
308 +       while ((tar_header = get_header_tar(&tar_outer)) != NULL) {
309 +                    int name_offset = 0;
310 +                    if (strncmp(tar_header->name, "./", 2) == 0)
311 +                            name_offset = 2;
312  
313 -                               archive_offset = 0;
314 -                               output_buffer = unarchive(uncompressed_stream,
315 -                                               out_stream, get_header_tar,
316 -                                               free_header_tar,
317 -                                               extract_function, prefix,
318 -                                               file_list, err);
319 -                               fclose(uncompressed_stream);
320 -                               gz_err = gz_close(gunzip_pid);
321 -                               if (gz_err)
322 -                                       *err = -1;
323 -                               free_header_ar(ar_header);
324 -                               break;
325 -                       }
326 -                       if (fseek(deb_stream, ar_header->size, SEEK_CUR) == -1) {
327 -                               opkg_perror(ERROR, "Couldn't fseek into %s", package_filename);
328 -                               *err = -1;
329 -                               free_header_ar(ar_header);
330 -                               goto cleanup;
331 -                       }
332 -                       free_header_ar(ar_header);
333 -               }
334 -               goto cleanup;
335 -       } else if (strncmp(ar_magic, "\037\213", 2) == 0) {
336 -               /* it's a gz file, let's assume it's an opkg */
337 -               int unzipped_opkg_pid;
338 -               FILE *unzipped_opkg_stream;
339 -               file_header_t *tar_header;
340 -               archive_offset = 0;
341 -               if (fseek(deb_stream, 0, SEEK_SET) == -1) {
342 -                       opkg_perror(ERROR, "Couldn't fseek into %s", package_filename);
343 -                       *err = -1;
344 -                       goto cleanup;
345 -               }
346 -               unzipped_opkg_stream = gz_open(deb_stream, &unzipped_opkg_pid);
347 -               if (unzipped_opkg_stream == NULL) {
348 -                       *err = -1;
349 -                       goto cleanup;
350 -               }
351 +               if (strcmp(ared_file, tar_header->name+name_offset) == 0) {
352 +                       memset(&tar_inner, 0, sizeof(tar_inner));
353 +                       tar_inner.gzip = &tar_outer;
354 +                       gzip_exec(&tar_inner, NULL);
355  
356 -               /* walk through outer tar file to find ared_file */
357 -               while ((tar_header = get_header_tar(unzipped_opkg_stream)) != NULL) {
358 -                        int name_offset = 0;
359 -                        if (strncmp(tar_header->name, "./", 2) == 0)
360 -                                name_offset = 2;
361 -                       if (strcmp(ared_file, tar_header->name+name_offset) == 0) {
362 -                               int gunzip_pid = 0;
363 -                               FILE *uncompressed_stream;
364 -                               /* open a stream of decompressed data */
365 -                               uncompressed_stream = gz_open(unzipped_opkg_stream, &gunzip_pid);
366 -                               if (uncompressed_stream == NULL) {
367 -                                       *err = -1;
368 -                                       goto cleanup;
369 -                               }
370 -                               archive_offset = 0;
371 +                       archive_offset = 0;
372  
373 -                               output_buffer = unarchive(uncompressed_stream,
374 -                                                         out_stream,
375 -                                                         get_header_tar,
376 -                                                         free_header_tar,
377 -                                                         extract_function,
378 -                                                         prefix,
379 -                                                         file_list,
380 -                                                         err);
381 +                       output_buffer = unarchive(&tar_inner,
382 +                                                 out_stream,
383 +                                                 get_header_tar,
384 +                                                 free_header_tar,
385 +                                                 extract_function,
386 +                                                 prefix,
387 +                                                 file_list,
388 +                                                 err);
389  
390 -                               free_header_tar(tar_header);
391 -                               fclose(uncompressed_stream);
392 -                               gz_err = gz_close(gunzip_pid);
393 -                               if (gz_err)
394 -                                       *err = -1;
395 -                               break;
396 -                       }
397 -                       seek_sub_file(unzipped_opkg_stream, tar_header->size);
398                         free_header_tar(tar_header);
399 +                       gzip_close(&tar_inner);
400 +                       break;
401                 }
402 -               fclose(unzipped_opkg_stream);
403 -               gz_err = gz_close(unzipped_opkg_pid);
404 -               if (gz_err)
405 -                       *err = -1;
406  
407 -               goto cleanup;
408 -       } else {
409 -               *err = -1;
410 -               error_msg("%s: invalid magic", package_filename);
411 +               seek_forward(&tar_outer, tar_header->size);
412 +               free_header_tar(tar_header);
413         }
414  
415  cleanup:
416 -       if (deb_stream)
417 -               fclose(deb_stream);
418 +       gzip_close(&tar_outer);
419 +
420         if (file_list)
421                 free(file_list);
422  
423 --- /dev/null
424 +++ b/libbb/gzip.h
425 @@ -0,0 +1,41 @@
426 +/*
427 + *  Copyright (C) 2016 Jo-Philipp Wich <jo@mein.io>
428 + *
429 + *  Zlib decrompression utility routines.
430 + *
431 + *  This program is free software; you can redistribute it and/or modify
432 + *  it under the terms of the GNU General Public License as published by
433 + *  the Free Software Foundation; either version 2 of the License, or
434 + *  (at your option) any later version.
435 + *
436 + *  This program is distributed in the hope that it will be useful,
437 + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
438 + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
439 + *  GNU Library General Public License for more details.
440 + *
441 + *  You should have received a copy of the GNU General Public License
442 + *  along with this program; if not, write to the Free Software
443 + *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
444 + */
445 +
446 +#include <stdio.h>
447 +#include <signal.h>
448 +#include <pthread.h>
449 +
450 +struct gzip_handle {
451 +       FILE *file;
452 +       struct gzip_handle *gzip;
453 +
454 +       pid_t pid;
455 +       int rfd, wfd;
456 +       struct sigaction pipe_sa;
457 +       pthread_t thread;
458 +};
459 +
460 +int gzip_exec(struct gzip_handle *zh, const char *filename);
461 +ssize_t gzip_read(struct gzip_handle *zh, char *buf, ssize_t len);
462 +ssize_t gzip_copy(struct gzip_handle *zh, FILE *out, ssize_t len);
463 +int gzip_close(struct gzip_handle *zh);
464 +FILE *gzip_fdopen(struct gzip_handle *zh, const char *filename);
465 +
466 +#define gzip_seek(zh, len) gzip_copy(zh, NULL, len)
467 --- a/libbb/Makefile.am
468 +++ b/libbb/Makefile.am
469 @@ -4,9 +4,8 @@ ALL_CFLAGS=-g -O -Wall -DHOST_CPU_STR=\"
470  
471  noinst_LIBRARIES = libbb.a
472  
473 -libbb_a_SOURCES = gz_open.c \
474 +libbb_a_SOURCES = \
475         libbb.h \
476 -       unzip.c \
477         wfopen.c \
478         unarchive.c \
479         copy_file.c \
480 @@ -20,7 +19,8 @@ libbb_a_SOURCES = gz_open.c \
481         parse_mode.c \
482         time_string.c \
483         all_read.c \
484 -       mode_string.c
485 +       mode_string.c \
486 +       gzip.c
487  
488  libbb_la_CFLAGS = $(ALL_CFLAGS)
489  #libbb_la_LDFLAGS = -static
490 --- /dev/null
491 +++ b/libbb/gzip.c
492 @@ -0,0 +1,208 @@
493 +/*
494 + *  Copyright (C) 2016 Jo-Philipp Wich <jo@mein.io>
495 + *  Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
496 + *
497 + *  Zlib decrompression utility routines.
498 + *
499 + *  This program is free software; you can redistribute it and/or modify
500 + *  it under the terms of the GNU General Public License as published by
501 + *  the Free Software Foundation; either version 2 of the License, or
502 + *  (at your option) any later version.
503 + *
504 + *  This program is distributed in the hope that it will be useful,
505 + *  but WITHOUT ANY WARRANTY; without even the implied warranty of
506 + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
507 + *  GNU Library General Public License for more details.
508 + *
509 + *  You should have received a copy of the GNU General Public License
510 + *  along with this program; if not, write to the Free Software
511 + *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
512 + */
513 +
514 +#include <string.h>
515 +#include <errno.h>
516 +#include <fcntl.h>
517 +#include <unistd.h>
518 +#include <poll.h>
519 +#include <stdlib.h>
520 +#include <sys/stat.h>
521 +#include <sys/wait.h>
522 +
523 +#include "gzip.h"
524 +
525 +static void
526 +to_devnull(int fd)
527 +{
528 +       int devnull = open("/dev/null", fd ? O_WRONLY : O_RDONLY);
529 +
530 +       if (devnull >= 0)
531 +               dup2(devnull, fd);
532 +
533 +       if (devnull > STDERR_FILENO)
534 +               close(devnull);
535 +}
536 +
537 +void *
538 +gzip_thread(void *ptr)
539 +{
540 +       struct gzip_handle *zh = ptr;
541 +       char buf[4096];
542 +       int len, ret;
543 +
544 +       while (1) {
545 +               if (zh->file)
546 +                       len = fread(buf, 1, sizeof(buf), zh->file);
547 +               else if (zh->gzip)
548 +                       len = gzip_read(zh->gzip, buf, sizeof(buf));
549 +
550 +               if (len <= 0)
551 +                       break;
552 +
553 +               do {
554 +                       ret = write(zh->wfd, buf, len);
555 +               } while (ret == -1 && errno == EINTR);
556 +       }
557 +
558 +       close(zh->wfd);
559 +       zh->wfd = -1;
560 +}
561 +
562 +int
563 +gzip_exec(struct gzip_handle *zh, const char *filename)
564 +{
565 +       int rpipe[2] = { -1, -1 }, wpipe[2] = { -1, -1 };
566 +       struct sigaction pipe_sa = { .sa_handler = SIG_IGN };
567 +
568 +       zh->rfd = -1;
569 +       zh->wfd = -1;
570 +
571 +       if (sigaction(SIGPIPE, &pipe_sa, &zh->pipe_sa) < 0)
572 +               return -1;
573 +
574 +       if (pipe(rpipe) < 0)
575 +               return -1;
576 +
577 +       if (!filename && pipe(wpipe) < 0) {
578 +               close(rpipe[0]);
579 +               close(rpipe[1]);
580 +               return -1;
581 +       }
582 +
583 +       zh->pid = vfork();
584 +
585 +       switch (zh->pid) {
586 +               case -1:
587 +                       return -1;
588 +
589 +               case 0:
590 +                       to_devnull(STDERR_FILENO);
591 +
592 +                       if (filename) {
593 +                               to_devnull(STDIN_FILENO);
594 +                       }
595 +                       else {
596 +                               dup2(wpipe[0], STDIN_FILENO);
597 +                               close(wpipe[0]);
598 +                               close(wpipe[1]);
599 +                       }
600 +
601 +                       dup2(rpipe[1], STDOUT_FILENO);
602 +                       close(rpipe[0]);
603 +                       close(rpipe[1]);
604 +
605 +                       execlp("gzip", "gzip", "-d",  "-c", filename, NULL);
606 +                       exit(-1);
607 +
608 +               default:
609 +                       zh->rfd = rpipe[0];
610 +                       zh->wfd = wpipe[1];
611 +
612 +                       fcntl(zh->rfd, F_SETFD, fcntl(zh->rfd, F_GETFD) | FD_CLOEXEC);
613 +                       close(rpipe[1]);
614 +
615 +                       if (zh->wfd >= 0) {
616 +                               fcntl(zh->wfd, F_SETFD, fcntl(zh->wfd, F_GETFD) | FD_CLOEXEC);
617 +                               close(wpipe[0]);
618 +                               pthread_create(&zh->thread, NULL, gzip_thread, zh);
619 +                       }
620 +       }
621 +
622 +       return 0;
623 +}
624 +
625 +ssize_t
626 +gzip_read(struct gzip_handle *zh, char *buf, ssize_t len)
627 +{
628 +       ssize_t ret;
629 +
630 +       do {
631 +               ret = read(zh->rfd, buf, len);
632 +       } while (ret == -1 && errno != EINTR);
633 +
634 +       return ret;
635 +}
636 +
637 +ssize_t
638 +gzip_copy(struct gzip_handle *zh, FILE *out, ssize_t len)
639 +{
640 +       char buf[4096];
641 +       ssize_t rlen, total = 0;
642 +
643 +       while (len > 0) {
644 +               rlen = gzip_read(zh, buf,
645 +                                   (len > sizeof(buf)) ? sizeof(buf) : len);
646 +
647 +               if (rlen <= 0)
648 +                       break;
649 +
650 +               if (out != NULL) {
651 +                       if (fwrite(buf, 1, rlen, out) != rlen)
652 +                               break;
653 +               }
654 +
655 +               len -= rlen;
656 +               total += rlen;
657 +       }
658 +
659 +       return total;
660 +}
661 +
662 +FILE *
663 +gzip_fdopen(struct gzip_handle *zh, const char *filename)
664 +{
665 +       memset(zh, 0, sizeof(*zh));
666 +
667 +       if (!filename || gzip_exec(zh, filename) < 0)
668 +               return NULL;
669 +
670 +       fcntl(zh->rfd, F_SETFL, fcntl(zh->rfd, F_GETFL) & ~O_NONBLOCK);
671 +
672 +       return fdopen(zh->rfd, "r");
673 +}
674 +
675 +int
676 +gzip_close(struct gzip_handle *zh)
677 +{
678 +       int code = -1;
679 +
680 +       if (zh->rfd >= 0)
681 +               close(zh->rfd);
682 +
683 +       if (zh->wfd >= 0)
684 +               close(zh->wfd);
685 +
686 +       if (zh->pid > 0) {
687 +               kill(zh->pid, SIGKILL);
688 +               waitpid(zh->pid, &code, 0);
689 +       }
690 +
691 +       if (zh->file)
692 +               fclose(zh->file);
693 +
694 +       if (zh->thread)
695 +               pthread_join(zh->thread, NULL);
696 +
697 +       sigaction(SIGPIPE, &zh->pipe_sa, NULL);
698 +
699 +       return WIFEXITED(code) ? WEXITSTATUS(code) : -1;
700 +}
701 --- a/src/Makefile.am
702 +++ b/src/Makefile.am
703 @@ -3,4 +3,4 @@ bin_PROGRAMS = opkg-cl
704  
705  opkg_cl_SOURCES = opkg-cl.c
706  opkg_cl_LDADD = $(top_builddir)/libopkg/libopkg.a \
707 -                $(top_builddir)/libbb/libbb.a $(CURL_LIBS) $(GPGME_LIBS) $(OPENSSL_LIBS) $(PATHFINDER_LIBS)
708 +                $(top_builddir)/libbb/libbb.a $(CURL_LIBS) $(GPGME_LIBS) $(OPENSSL_LIBS) $(PATHFINDER_LIBS) -lpthread
709 --- a/tests/Makefile.am
710 +++ b/tests/Makefile.am
711 @@ -16,7 +16,7 @@ noinst_PROGRAMS = libopkg_test
712  #opkg_active_list_test_SOURCES = opkg_active_list_test.c
713  #opkg_active_list_test_CFLAGS = $(ALL_CFLAGS) -I$(top_srcdir)
714  
715 -libopkg_test_LDADD = $(top_builddir)/libopkg/libopkg.a $(top_builddir)/libbb/libbb.a $(CURL_LIBS) $(GPGME_LIBS) $(OPENSSL_LIBS) $(PATHFINDER_LIBS)
716 +libopkg_test_LDADD = $(top_builddir)/libopkg/libopkg.a $(top_builddir)/libbb/libbb.a $(CURL_LIBS) $(GPGME_LIBS) $(OPENSSL_LIBS) $(PATHFINDER_LIBS) -lpthread
717  libopkg_test_SOURCE = libopkg_test.c
718  libopkg_test_LDFLAGS = -static
719