1 --- a/libbb/unarchive.c
2 +++ b/libbb/unarchive.c
9 #define CONFIG_FEATURE_TAR_OLDGNU_COMPATABILITY 1
10 #define CONFIG_FEATURE_TAR_GNU_EXTENSIONS
11 @@ -39,38 +40,15 @@ static char *linkname = NULL;
15 -#define SEEK_BUF 4096
17 -seek_by_read(FILE* fd, size_t len)
19 - ssize_t cc, total = 0;
23 - cc = fread(buf, sizeof(buf[0]),
24 - len > SEEK_BUF ? SEEK_BUF : len,
30 - if(feof(fd) || ferror(fd))
37 -seek_sub_file(FILE *fd, const int count)
38 +seek_forward(struct gzip_handle *zh, ssize_t len)
40 - archive_offset += count;
41 + ssize_t slen = gzip_seek(zh, len);
43 - /* Do not use fseek() on a pipe. It may fail with ESPIPE, leaving the
44 - * stream at an undefined location.
46 - seek_by_read(fd, count);
48 + archive_offset += len;
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
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,
64 @@ -129,14 +107,14 @@ extract_archive(FILE *src_stream, FILE *
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;
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;
81 @@ -156,7 +134,7 @@ extract_archive(FILE *src_stream, FILE *
83 error_msg("%s not created: newer or same age file exists", file_entry->name);
85 - seek_sub_file(src_stream, file_entry->size);
86 + seek_forward(src_stream, file_entry->size);
90 @@ -185,11 +163,11 @@ extract_archive(FILE *src_stream, FILE *
92 if ((dst_stream = wfopen(full_name, "w")) == NULL) {
94 - seek_sub_file(src_stream, file_entry->size);
95 + seek_forward(src_stream, file_entry->size);
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);
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);
112 /* extract_list and extract_verbose_list can be used in conjunction
113 @@ -274,8 +252,8 @@ cleanup:
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,
124 @@ -329,7 +307,7 @@ unarchive(FILE *src_stream, FILE *out_st
127 /* seek past the data entry */
128 - seek_sub_file(src_stream, file_entry->size);
129 + seek_forward(src_stream, file_entry->size);
131 free_headers(file_entry);
133 @@ -337,108 +315,9 @@ unarchive(FILE *src_stream, FILE *out_st
137 -static file_header_t *
138 -get_header_ar(FILE *src_stream)
140 - file_header_t *typed;
153 - static char *ar_long_names;
155 - if (fread(ar.raw, 1, 60, src_stream) != 60) {
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");
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*/
170 - if (fgetc(src_stream) != '\n') {
171 - error_msg("Invalid magic");
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);
179 - typed = (file_header_t *) xcalloc(1, sizeof(file_header_t));
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 */
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");
205 - typed->name = xstrdup(ar_long_names + atoi(&ar.formated.name[1]));
208 - /* short filenames */
209 - typed->name = xcalloc(1, 16);
210 - strncpy(typed->name, ar.formated.name, 16);
212 - typed->name[strcspn(typed->name, " /")]='\0';
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);
224 -free_header_ar(file_header_t *ar_entry)
226 - if (ar_entry == NULL)
229 - free(ar_entry->name);
230 - if (ar_entry->link_name)
231 - free(ar_entry->link_name);
237 static file_header_t *
238 -get_header_tar(FILE *tar_stream)
239 +get_header_tar(struct gzip_handle *tar_stream)
242 unsigned char raw[512];
243 @@ -467,10 +346,10 @@ get_header_tar(FILE *tar_stream)
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));
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
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)
263 longname[tar_entry->size] = '\0';
264 archive_offset += tar_entry->size;
265 @@ -566,7 +445,7 @@ get_header_tar(FILE *tar_stream)
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)
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;
278 + struct gzip_handle tar_outer, tar_inner;
279 + file_header_t *tar_header;
284 @@ -672,111 +554,44 @@ deb_extract(const char *package_filename
285 /* set the buffer size */
286 setvbuf(deb_stream, NULL, _IOFBF, 0x8000);
288 - /* check ar magic */
289 - fread(ar_magic, 1, 8, deb_stream);
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);
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) {
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)
313 - archive_offset = 0;
314 - output_buffer = unarchive(uncompressed_stream,
315 - out_stream, get_header_tar,
317 - extract_function, prefix,
319 - fclose(uncompressed_stream);
320 - gz_err = gz_close(gunzip_pid);
323 - free_header_ar(ar_header);
326 - if (fseek(deb_stream, ar_header->size, SEEK_CUR) == -1) {
327 - opkg_perror(ERROR, "Couldn't fseek into %s", package_filename);
329 - free_header_ar(ar_header);
332 - free_header_ar(ar_header);
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);
346 - unzipped_opkg_stream = gz_open(deb_stream, &unzipped_opkg_pid);
347 - if (unzipped_opkg_stream == NULL) {
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);
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)
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) {
370 - archive_offset = 0;
371 + archive_offset = 0;
373 - output_buffer = unarchive(uncompressed_stream,
381 + output_buffer = unarchive(&tar_inner,
390 - free_header_tar(tar_header);
391 - fclose(uncompressed_stream);
392 - gz_err = gz_close(gunzip_pid);
397 - seek_sub_file(unzipped_opkg_stream, tar_header->size);
398 free_header_tar(tar_header);
399 + gzip_close(&tar_inner);
402 - fclose(unzipped_opkg_stream);
403 - gz_err = gz_close(unzipped_opkg_pid);
410 - error_msg("%s: invalid magic", package_filename);
411 + seek_forward(&tar_outer, tar_header->size);
412 + free_header_tar(tar_header);
417 - fclose(deb_stream);
418 + gzip_close(&tar_outer);
427 + * Copyright (C) 2016 Jo-Philipp Wich <jo@mein.io>
429 + * Zlib decrompression utility routines.
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.
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.
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.
448 +#include <pthread.h>
450 +struct gzip_handle {
452 + struct gzip_handle *gzip;
456 + struct sigaction pipe_sa;
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);
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=\"
471 noinst_LIBRARIES = libbb.a
473 -libbb_a_SOURCES = gz_open.c \
480 @@ -20,7 +19,8 @@ libbb_a_SOURCES = gz_open.c \
488 libbb_la_CFLAGS = $(ALL_CFLAGS)
489 #libbb_la_LDFLAGS = -static
494 + * Copyright (C) 2016 Jo-Philipp Wich <jo@mein.io>
495 + * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
497 + * Zlib decrompression utility routines.
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.
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.
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.
520 +#include <sys/stat.h>
521 +#include <sys/wait.h>
528 + int devnull = open("/dev/null", fd ? O_WRONLY : O_RDONLY);
533 + if (devnull > STDERR_FILENO)
538 +gzip_thread(void *ptr)
540 + struct gzip_handle *zh = ptr;
546 + len = fread(buf, 1, sizeof(buf), zh->file);
548 + len = gzip_read(zh->gzip, buf, sizeof(buf));
554 + ret = write(zh->wfd, buf, len);
555 + } while (ret == -1 && errno == EINTR);
563 +gzip_exec(struct gzip_handle *zh, const char *filename)
565 + int rpipe[2] = { -1, -1 }, wpipe[2] = { -1, -1 };
566 + struct sigaction pipe_sa = { .sa_handler = SIG_IGN };
571 + if (sigaction(SIGPIPE, &pipe_sa, &zh->pipe_sa) < 0)
574 + if (pipe(rpipe) < 0)
577 + if (!filename && pipe(wpipe) < 0) {
590 + to_devnull(STDERR_FILENO);
593 + to_devnull(STDIN_FILENO);
596 + dup2(wpipe[0], STDIN_FILENO);
601 + dup2(rpipe[1], STDOUT_FILENO);
605 + execlp("gzip", "gzip", "-d", "-c", filename, NULL);
609 + zh->rfd = rpipe[0];
610 + zh->wfd = wpipe[1];
612 + fcntl(zh->rfd, F_SETFD, fcntl(zh->rfd, F_GETFD) | FD_CLOEXEC);
615 + if (zh->wfd >= 0) {
616 + fcntl(zh->wfd, F_SETFD, fcntl(zh->wfd, F_GETFD) | FD_CLOEXEC);
618 + pthread_create(&zh->thread, NULL, gzip_thread, zh);
626 +gzip_read(struct gzip_handle *zh, char *buf, ssize_t len)
631 + ret = read(zh->rfd, buf, len);
632 + } while (ret == -1 && errno != EINTR);
638 +gzip_copy(struct gzip_handle *zh, FILE *out, ssize_t len)
641 + ssize_t rlen, total = 0;
644 + rlen = gzip_read(zh, buf,
645 + (len > sizeof(buf)) ? sizeof(buf) : len);
651 + if (fwrite(buf, 1, rlen, out) != rlen)
663 +gzip_fdopen(struct gzip_handle *zh, const char *filename)
665 + memset(zh, 0, sizeof(*zh));
667 + if (!filename || gzip_exec(zh, filename) < 0)
670 + fcntl(zh->rfd, F_SETFL, fcntl(zh->rfd, F_GETFL) & ~O_NONBLOCK);
672 + return fdopen(zh->rfd, "r");
676 +gzip_close(struct gzip_handle *zh)
687 + kill(zh->pid, SIGKILL);
688 + waitpid(zh->pid, &code, 0);
695 + pthread_join(zh->thread, NULL);
697 + sigaction(SIGPIPE, &zh->pipe_sa, NULL);
699 + return WIFEXITED(code) ? WEXITSTATUS(code) : -1;
701 --- a/src/Makefile.am
702 +++ b/src/Makefile.am
703 @@ -3,4 +3,4 @@ bin_PROGRAMS = opkg-cl
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)
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