opkg: vfork external gzip command to uncompress data
[openwrt/openwrt.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", filename ? filename : "-c", 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