diff options
author | Timo Teras <timo.teras@iki.fi> | 2008-11-07 17:11:08 +0200 |
---|---|---|
committer | Timo Teras <timo.teras@iki.fi> | 2008-11-07 17:11:08 +0200 |
commit | aef0f036f08f8949e4e2a5b84c9199ef8ec40595 (patch) | |
tree | 423ba9a4e5db4b8b38b6ccf2578c8c205ff7db4d | |
parent | 6967c28b96784d474e6c2f50b075895d89f9ce02 (diff) | |
download | apk-tools-aef0f036f08f8949e4e2a5b84c9199ef8ec40595.tar.gz apk-tools-aef0f036f08f8949e4e2a5b84c9199ef8ec40595.tar.bz2 apk-tools-aef0f036f08f8949e4e2a5b84c9199ef8ec40595.tar.xz apk-tools-aef0f036f08f8949e4e2a5b84c9199ef8ec40595.zip |
use zlib internally to decompress
-rw-r--r-- | TODO | 3 | ||||
-rw-r--r-- | src/Makefile | 4 | ||||
-rw-r--r-- | src/apk_archive.h | 21 | ||||
-rw-r--r-- | src/apk_io.h | 38 | ||||
-rw-r--r-- | src/apk_package.h | 3 | ||||
-rw-r--r-- | src/archive.c | 241 | ||||
-rw-r--r-- | src/database.c | 40 | ||||
-rw-r--r-- | src/gunzip.c | 93 | ||||
-rw-r--r-- | src/io.c | 221 | ||||
-rw-r--r-- | src/package.c | 27 |
10 files changed, 469 insertions, 222 deletions
@@ -1,5 +1,4 @@ -- Internal gunzip (via lib) to speed up stuff, and avoid threads - - mmap() if possible for reading +- mmapped block stream from file - Index/pkginfo reader: same field multiple times -> memleak diff --git a/src/Makefile b/src/Makefile index 15eeb00..62041c2 100644 --- a/src/Makefile +++ b/src/Makefile @@ -15,6 +15,8 @@ apk_OBJS = \ package.o \ archive.o \ version.o \ + io.o \ + gunzip.o \ blob.o \ hash.o \ md5.o \ @@ -30,7 +32,7 @@ ALL_OBJS = $(apk_OBJS) all: $(TARGETS) apk: $(apk_OBJS) - $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) /usr/lib/libz.a clean:: @rm -f $(TARGETS) $(ALL_OBJS) diff --git a/src/apk_archive.h b/src/apk_archive.h index 2e8d274..b51aee5 100644 --- a/src/apk_archive.h +++ b/src/apk_archive.h @@ -13,8 +13,8 @@ #define APK_ARCHIVE #include <sys/types.h> -#include <pthread.h> #include "apk_blob.h" +#include "apk_io.h" struct apk_archive_entry { char *name; @@ -27,16 +27,19 @@ struct apk_archive_entry { mode_t mode; time_t mtime; dev_t device; - int read_fd; }; -typedef int (*apk_archive_entry_parser)(struct apk_archive_entry *entry, void *ctx); +typedef int (*apk_archive_entry_parser)(void *ctx, + const struct apk_archive_entry *ae, + struct apk_istream *istream); -pid_t apk_open_gz(int *fd); -int apk_parse_tar(int fd, apk_archive_entry_parser parser, void *ctx); -int apk_parse_tar_gz(int fd, apk_archive_entry_parser parser, void *ctx); -apk_blob_t apk_archive_entry_read(struct apk_archive_entry *ae); -int apk_archive_entry_extract(struct apk_archive_entry *ae, const char *to); -pthread_t apk_checksum_and_tee(int *fd, void *ptr); +struct apk_istream *apk_gunzip_bstream(struct apk_bstream *); + +int apk_parse_tar(struct apk_istream *, apk_archive_entry_parser parser, void *ctx); +int apk_parse_tar_gz(struct apk_bstream *, apk_archive_entry_parser parser, void *ctx); + +int apk_archive_entry_extract(const struct apk_archive_entry *ae, + struct apk_istream *is, + const char *to); #endif diff --git a/src/apk_io.h b/src/apk_io.h new file mode 100644 index 0000000..a0ceac2 --- /dev/null +++ b/src/apk_io.h @@ -0,0 +1,38 @@ +/* apk_io.h - Alpine Package Keeper (APK) + * + * Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. See http://www.gnu.org/ for details. + */ + +#ifndef APK_IO +#define APK_IO + +#include "apk_defines.h" +#include "apk_blob.h" + +struct apk_istream { + size_t (*read)(void *stream, void *ptr, size_t size); + size_t (*splice)(void *stream, int fd, size_t size); + void (*close)(void *stream); +}; + +struct apk_bstream { + size_t (*read)(void *stream, void **ptr); + void (*close)(void *stream, csum_t csum); +}; + +struct apk_istream *apk_istream_from_fd(int fd); +struct apk_istream *apk_istream_from_file(const char *file); +size_t apk_istream_skip(struct apk_istream *istream, size_t size); +size_t apk_istream_splice(void *stream, int fd, size_t size); + +struct apk_bstream *apk_bstream_from_istream(struct apk_istream *istream); +struct apk_bstream *apk_bstream_from_fd(int fd); + +apk_blob_t apk_blob_from_istream(struct apk_istream *istream, size_t size); + +#endif diff --git a/src/apk_package.h b/src/apk_package.h index 68e63e1..6f13e94 100644 --- a/src/apk_package.h +++ b/src/apk_package.h @@ -14,6 +14,7 @@ #include "apk_version.h" #include "apk_hash.h" +#include "apk_io.h" struct apk_database; struct apk_name; @@ -75,7 +76,7 @@ struct apk_package *apk_pkg_read(struct apk_database *db, const char *name); void apk_pkg_free(struct apk_package *pkg); int apk_pkg_get_state(struct apk_package *pkg); -int apk_pkg_add_script(struct apk_package *pkg, int fd, +int apk_pkg_add_script(struct apk_package *pkg, struct apk_istream *is, unsigned int type, unsigned int size); int apk_pkg_run_script(struct apk_package *pkg, int root_fd, unsigned int type); diff --git a/src/archive.c b/src/archive.c index 6de998b..33e3428 100644 --- a/src/archive.c +++ b/src/archive.c @@ -47,75 +47,62 @@ struct tar_header { char padding[12]; /* 500-512 */ }; -static int get_dev_null(void) -{ - static int fd_null = 0; +#define GET_OCTAL(s) apk_blob_uint(APK_BLOB_PTR_LEN(s, sizeof(s)), 8) - if (fd_null == 0) { - fd_null = open("/dev/null", O_WRONLY); - if (fd_null < 0) - err(EX_OSFILE, "/dev/null"); - } - return fd_null; -} +struct apk_tar_entry_istream { + struct apk_istream is; + struct apk_istream *tar_is; + size_t bytes_left; +}; -pid_t apk_open_gz(int *fd) +static size_t tar_entry_read(void *stream, void *ptr, size_t size) { - int pipe_fd[2]; - pid_t child_pid; - - if (pipe(pipe_fd) < 0) - err(EX_OSERR, "pipe"); - - child_pid = fork(); - if (child_pid < 0) - err(EX_OSERR, "fork"); - - if (child_pid == 0) { - close(pipe_fd[0]); - dup2(pipe_fd[1], STDOUT_FILENO); - dup2(*fd, STDIN_FILENO); - dup2(get_dev_null(), STDERR_FILENO); - close(pipe_fd[1]); - execl(GUNZIP_BINARY, "gunzip", "-c", NULL); - err(EX_UNAVAILABLE, GUNZIP_BINARY); - } - - close(pipe_fd[1]); - *fd = pipe_fd[0]; - - return child_pid; + struct apk_tar_entry_istream *teis = + container_of(stream, struct apk_tar_entry_istream, is); + + if (size > teis->bytes_left) + size = teis->bytes_left; + size = teis->tar_is->read(teis->tar_is, ptr, size); + if (size >= 0) + teis->bytes_left -= size; + return size; } -#define GET_OCTAL(s) apk_blob_uint(APK_BLOB_PTR_LEN(s, sizeof(s)), 8) - -static int do_splice(int from_fd, int to_fd, int len) +static size_t tar_entry_splice(void *stream, int fd, size_t size) { - int i = 0, r; - - while (i != len) { - r = splice(from_fd, NULL, to_fd, NULL, len - i, SPLICE_F_MOVE); - if (r == -1) - return i; - i += r; - } - - return i; + struct apk_tar_entry_istream *teis = + container_of(stream, struct apk_tar_entry_istream, is); + + if (size > teis->bytes_left) + size = teis->bytes_left; + size = teis->tar_is->splice(teis->tar_is, fd, size); + if (size >= 0) + teis->bytes_left -= size; + return size; } -int apk_parse_tar(int fd, apk_archive_entry_parser parser, void *ctx) +int apk_parse_tar(struct apk_istream *is, apk_archive_entry_parser parser, + void *ctx) { + struct apk_tar_entry_istream teis = { + .is.read = tar_entry_read, + .is.splice = tar_entry_splice, + .tar_is = is, + }; struct apk_archive_entry entry; struct tar_header buf; unsigned long offset = 0; int end = 0, r; + size_t toskip; memset(&entry, 0, sizeof(entry)); - while (read(fd, &buf, 512) == 512) { + while ((r = is->read(is, &buf, 512)) == 512) { offset += 512; if (buf.name[0] == '\0') { - if (end) + if (end) { + r = 0; break; + } end++; continue; } @@ -131,7 +118,6 @@ int apk_parse_tar(int fd, apk_archive_entry_parser parser, void *ctx) .gname = buf.gname, .device = makedev(GET_OCTAL(buf.devmajor), GET_OCTAL(buf.devminor)), - .read_fd = fd, }; switch (buf.typeflag) { @@ -139,7 +125,7 @@ int apk_parse_tar(int fd, apk_archive_entry_parser parser, void *ctx) if (entry.name != NULL) free(entry.name); entry.name = malloc(entry.size+1); - read(fd, entry.name, entry.size); + is->read(is, entry.name, entry.size); offset += entry.size; entry.size = 0; break; @@ -168,74 +154,48 @@ int apk_parse_tar(int fd, apk_archive_entry_parser parser, void *ctx) break; } + teis.bytes_left = entry.size; if (entry.mode & S_IFMT) { if (entry.name == NULL) entry.name = strdup(buf.name); /* callback parser function */ - offset += entry.size; - r = parser(&entry, ctx); + r = parser(ctx, &entry, &teis.is); if (r != 0) return r; - offset -= entry.size; free(entry.name); entry.name = NULL; } - if (entry.size) - offset += do_splice(fd, get_dev_null(), entry.size); + offset += entry.size - teis.bytes_left; + toskip = teis.bytes_left; + if ((offset + toskip) & 511) + toskip += 512 - ((offset + toskip) & 511); + offset += toskip; + if (toskip != 0) + is->read(is, NULL, toskip); + } - /* align to next 512 block */ - if (offset & 511) - offset += do_splice(fd, get_dev_null(), - 512 - (offset & 511)); + if (r != 0) { + apk_error("Bad TAR header (r=%d)", r); + return -1; } return 0; } -int apk_parse_tar_gz(int fd, apk_archive_entry_parser parser, void *ctx) -{ - pid_t pid; - int r, status; - - pid = apk_open_gz(&fd); - if (pid < 0) - return pid; - - r = apk_parse_tar(fd, parser, ctx); - close(fd); - waitpid(pid, &status, 0); - - return r; -} - -apk_blob_t apk_archive_entry_read(struct apk_archive_entry *ae) +int apk_parse_tar_gz(struct apk_bstream *bs, apk_archive_entry_parser parser, + void *ctx) { - char *str; - int pos = 0; - ssize_t r; - - str = malloc(ae->size + 1); - pos = 0; - while (ae->size) { - r = read(ae->read_fd, &str[pos], ae->size); - if (r < 0) { - free(str); - return APK_BLOB_NULL; - } - pos += r; - ae->size -= r; - } - str[pos] = 0; - - return APK_BLOB_PTR_LEN(str, pos+1); + return apk_parse_tar(apk_gunzip_bstream(bs), parser, ctx); } -int apk_archive_entry_extract(struct apk_archive_entry *ae, const char *fn) +int apk_archive_entry_extract(const struct apk_archive_entry *ae, + struct apk_istream *is, + const char *fn) { - int r = -1; + int r = -1, fd; if (fn == NULL) fn = ae->name; @@ -251,12 +211,14 @@ int apk_archive_entry_extract(struct apk_archive_entry *ae, const char *fn) break; case S_IFREG: if (ae->link_target == NULL) { - r = open(fn, O_WRONLY | O_CREAT, ae->mode & 07777); - if (r < 0) + fd = open(fn, O_WRONLY | O_CREAT, ae->mode & 07777); + if (fd < 0) { + r = -1; break; - ae->size -= do_splice(ae->read_fd, r, ae->size); - close(r); - r = ae->size ? -1 : 0; + } + if (is->splice(is, fd, ae->size) == ae->size) + r = 0; + close(fd); } else { r = link(ae->link_target, fn); } @@ -284,74 +246,3 @@ int apk_archive_entry_extract(struct apk_archive_entry *ae, const char *fn) } return r; } - -struct checksum_and_tee { - int in_fd, tee_fd; - void *ptr; -}; - -static void *__apk_checksum_and_tee(void *arg) -{ - struct checksum_and_tee *args = (struct checksum_and_tee *) arg; - char buf[2*1024]; - int r, w, wt; - __off64_t offset; - csum_ctx_t ctx; - int dosplice = 1; - - offset = lseek(args->in_fd, 0, SEEK_CUR); - csum_init(&ctx); - do { - r = read(args->in_fd, buf, sizeof(buf)); - if (r <= 0) - break; - - wt = 0; - do { - if (dosplice) { - w = splice(args->in_fd, &offset, args->tee_fd, NULL, - r - wt, SPLICE_F_MOVE); - if (w < 0) { - dosplice = 0; - continue; - } - } else { - w = write(args->tee_fd, &buf[wt], r - wt); - if (w < 0) - break; - } - wt += w; - } while (wt != r); - - csum_process(&ctx, buf, r); - } while (r == sizeof(buf)); - - csum_finish(&ctx, args->ptr); - close(args->tee_fd); - close(args->in_fd); - free(args); - - return NULL; -} - -pthread_t apk_checksum_and_tee(int *fd, void *ptr) -{ - struct checksum_and_tee *args; - int fds[2]; - pthread_t tid; - - if (pipe(fds) < 0) - return -1; - - fcntl(fds[0], F_SETFD, FD_CLOEXEC); - fcntl(fds[1], F_SETFD, FD_CLOEXEC); - - args = malloc(sizeof(*args)); - *args = (struct checksum_and_tee){ *fd, fds[1], ptr }; - if (pthread_create(&tid, NULL, __apk_checksum_and_tee, args) < 0) - return -1; - - *fd = fds[0]; - return tid; -} - diff --git a/src/database.c b/src/database.c index a2caa53..a1c0300 100644 --- a/src/database.c +++ b/src/database.c @@ -376,14 +376,14 @@ static int apk_db_write_scriptdb(struct apk_database *db, int fd) return 0; } -static int apk_db_read_scriptdb(struct apk_database *db, int fd) +static int apk_db_read_scriptdb(struct apk_database *db, struct apk_istream *is) { struct apk_package *pkg; struct apk_script_header hdr; - while (read(fd, &hdr, sizeof(hdr)) == sizeof(hdr)) { + while (is->read(is, &hdr, sizeof(hdr)) == sizeof(hdr)) { pkg = apk_db_get_pkg(db, hdr.csum); - apk_pkg_add_script(pkg, fd, hdr.type, hdr.size); + apk_pkg_add_script(pkg, is, hdr.type, hdr.size); } return 0; @@ -416,6 +416,7 @@ int apk_db_create(const char *root) static int apk_db_read_config(struct apk_database *db) { + struct apk_istream *is; struct stat st; char *buf; int fd; @@ -452,10 +453,10 @@ static int apk_db_read_config(struct apk_database *db) close(fd); } - fd = open("var/lib/apk/scripts", O_RDONLY); - if (fd >= 0) { - apk_db_read_scriptdb(db, fd); - close(fd); + is = apk_istream_from_file("var/lib/apk/scripts"); + if (is != NULL) { + apk_db_read_scriptdb(db, is); + is->close(is); } return 0; @@ -664,9 +665,11 @@ int apk_db_recalculate_and_commit(struct apk_database *db) return r; } -static int apk_db_install_archive_entry(struct apk_archive_entry *ae, - struct install_ctx *ctx) +static int apk_db_install_archive_entry(void *_ctx, + const struct apk_archive_entry *ae, + struct apk_istream *is) { + struct install_ctx *ctx = (struct install_ctx *) _ctx; struct apk_database *db = ctx->db; struct apk_package *pkg = ctx->pkg; apk_blob_t name = APK_BLOB_STR(ae->name); @@ -698,8 +701,7 @@ static int apk_db_install_archive_entry(struct apk_archive_entry *ae, /* Handle script */ if (type != APK_SCRIPT_INVALID) { - ae->size -= apk_pkg_add_script(pkg, ae->read_fd, - type, ae->size); + apk_pkg_add_script(pkg, is, type, ae->size); if (type == APK_SCRIPT_GENERIC || type == ctx->script) { @@ -736,7 +738,7 @@ static int apk_db_install_archive_entry(struct apk_archive_entry *ae, if (strncmp(file->filename, ".keep_", 6) == 0) return 0; - r = apk_archive_entry_extract(ae, NULL); + r = apk_archive_entry_extract(ae, is, NULL); } else { if (name.ptr[name.len-1] == '/') name.len--; @@ -775,10 +777,10 @@ int apk_db_install_pkg(struct apk_database *db, struct apk_package *oldpkg, struct apk_package *newpkg) { + struct apk_bstream *bs; struct install_ctx ctx; csum_t csum; char file[256]; - pthread_t tid = 0; int fd, r; if (fchdir(db->root_fd) < 0) @@ -813,8 +815,8 @@ int apk_db_install_pkg(struct apk_database *db, fcntl(fd, F_SETFD, FD_CLOEXEC); - tid = apk_checksum_and_tee(&fd, csum); - if (tid < 0) + bs = apk_bstream_from_fd(fd); + if (bs == NULL) goto err_close; ctx = (struct install_ctx) { @@ -823,12 +825,10 @@ int apk_db_install_pkg(struct apk_database *db, .script = (oldpkg == NULL) ? APK_SCRIPT_PRE_INSTALL : APK_SCRIPT_PRE_UPGRADE, }; - if (apk_parse_tar_gz(fd, (apk_archive_entry_parser) - apk_db_install_archive_entry, &ctx) != 0) + if (apk_parse_tar_gz(bs, apk_db_install_archive_entry, &ctx) != 0) goto err_close; - pthread_join(tid, NULL); - close(fd); + bs->close(bs, csum); db->installed.stats.packages++; hlist_add_head(&newpkg->installed_pkgs_list, &db->installed.packages); @@ -850,7 +850,5 @@ int apk_db_install_pkg(struct apk_database *db, err_close: close(fd); - if (tid != 0) - pthread_join(tid, NULL); return -1; } diff --git a/src/gunzip.c b/src/gunzip.c new file mode 100644 index 0000000..f488fa4 --- /dev/null +++ b/src/gunzip.c @@ -0,0 +1,93 @@ +/* gunzip.c - Alpine Package Keeper (APK) + * + * Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. See http://www.gnu.org/ for details. + */ + +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <malloc.h> +#include <zlib.h> + +#include "apk_defines.h" +#include "apk_io.h" + +struct apk_gzip_istream { + struct apk_istream is; + struct apk_bstream *bs; + z_stream zs; + int z_err; +}; + +static size_t gz_read(void *stream, void *ptr, size_t size) +{ + struct apk_gzip_istream *gis = + container_of(stream, struct apk_gzip_istream, is); + + if (gis->z_err == Z_DATA_ERROR || gis->z_err == Z_ERRNO) + return -1; + if (gis->z_err == Z_STREAM_END) + return 0; + + if (ptr == NULL) + return apk_istream_skip(&gis->is, size); + + gis->zs.avail_out = size; + gis->zs.next_out = ptr; + + while (gis->zs.avail_out != 0 && gis->z_err == Z_OK) { + if (gis->zs.avail_in == 0) { + gis->zs.avail_in = gis->bs->read(gis->bs, (void **) &gis->zs.next_in); + if (gis->zs.avail_in < 0) { + gis->z_err = Z_DATA_ERROR; + return size - gis->zs.avail_out; + } + } + + gis->z_err = inflate(&gis->zs, Z_NO_FLUSH); + } + + if (gis->z_err != Z_OK && gis->z_err != Z_STREAM_END) + return -1; + + return size - gis->zs.avail_out; +} + +static void gz_close(void *stream) +{ + struct apk_gzip_istream *gis = + container_of(stream, struct apk_gzip_istream, is); + + inflateEnd(&gis->zs); + free(gis); +} + +struct apk_istream *apk_gunzip_bstream(struct apk_bstream *bs) +{ + struct apk_gzip_istream *gis; + + gis = malloc(sizeof(struct apk_gzip_istream)); + if (gis == NULL) + return NULL; + + *gis = (struct apk_gzip_istream) { + .is.read = gz_read, + .is.splice = apk_istream_splice, + .is.close = gz_close, + .bs = bs, + .z_err = 0, + }; + + if (inflateInit2(&gis->zs, 15+32) != Z_OK) { + free(gis); + return NULL; + } + + return &gis->is; +} + diff --git a/src/io.c b/src/io.c new file mode 100644 index 0000000..38a1b07 --- /dev/null +++ b/src/io.c @@ -0,0 +1,221 @@ +/* io.c - Alpine Package Keeper (APK) + * + * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org> + * Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi> + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. See http://www.gnu.org/ for details. + */ + +#include <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <malloc.h> + +#include "apk_defines.h" +#include "apk_io.h" + +struct apk_fd_istream { + struct apk_istream is; + int fd; +}; + +static size_t fd_read(void *stream, void *ptr, size_t size) +{ + struct apk_fd_istream *fis = + container_of(stream, struct apk_fd_istream, is); + size_t i = 0, r; + + if (ptr == NULL) { + if (lseek(fis->fd, size, SEEK_CUR) < 0) + return -1; + return size; + } + + while (i < size) { + r = read(fis->fd, ptr + i, size - i); + if (r < 0) + return r; + if (r == 0) + return i; + i += r; + } + + return i; +} + +static size_t fd_splice(void *stream, int fd, size_t size) +{ + struct apk_fd_istream *fis = + container_of(stream, struct apk_fd_istream, is); + size_t i = 0, r; + + while (i != size) { + r = splice(fis->fd, NULL, fd, NULL, size - i, SPLICE_F_MOVE); + if (r == -1) + return i; + i += r; + } + + return i; +} + +static void fd_close(void *stream) +{ + struct apk_fd_istream *fis = + container_of(stream, struct apk_fd_istream, is); + + close(fis->fd); + free(fis); +} + +struct apk_istream *apk_istream_from_fd(int fd) +{ + struct apk_fd_istream *fis; + + fis = malloc(sizeof(struct apk_fd_istream)); + if (fis == NULL) + return NULL; + + *fis = (struct apk_fd_istream) { + .is.read = fd_read, + .is.splice = fd_splice, + .is.close = fd_close, + .fd = fd, + }; + + return &fis->is; +} + +struct apk_istream *apk_istream_from_file(const char *file) +{ + int fd; + + fd = open(file, O_RDONLY); + if (fd < 0) + return NULL; + + return apk_istream_from_fd(fd); +} + +size_t apk_istream_skip(struct apk_istream *is, size_t size) +{ + unsigned char buf[2048]; + size_t done = 0, r, togo; + + while (done < size) { + togo = size - done; + if (togo > sizeof(buf)) + togo = sizeof(buf); + r = is->read(is, buf, togo); + if (r < 0) + return r; + done += r; + if (r != togo) + break; + } + return done; +} + +size_t apk_istream_splice(void *stream, int fd, size_t size) +{ + struct apk_istream *is = (struct apk_istream *) stream; + unsigned char buf[2048]; + size_t done = 0, r, togo; + + while (done < size) { + togo = size - done; + if (togo > sizeof(buf)) + togo = sizeof(buf); + r = is->read(is, buf, togo); + if (r < 0) + return r; + if (write(fd, buf, r) != r) + return -1; + done += r; + if (r != togo) + break; + } + return done; +} + +struct apk_istream_bstream { + struct apk_bstream bs; + struct apk_istream *is; + csum_ctx_t csum_ctx; + unsigned char buffer[8*1024]; +}; + +static size_t is_bs_read(void *stream, void **ptr) +{ + struct apk_istream_bstream *isbs = + container_of(stream, struct apk_istream_bstream, bs); + size_t size; + + size = isbs->is->read(isbs->is, isbs->buffer, sizeof(isbs->buffer)); + if (size <= 0) + return size; + + csum_process(&isbs->csum_ctx, isbs->buffer, size); + + *ptr = isbs->buffer; + return size; +} + +static void is_bs_close(void *stream, csum_t csum) +{ + struct apk_istream_bstream *isbs = + container_of(stream, struct apk_istream_bstream, bs); + + if (csum != NULL) + csum_finish(&isbs->csum_ctx, csum); + + isbs->is->close(isbs->is); + free(isbs); +} +struct apk_bstream *apk_bstream_from_istream(struct apk_istream *istream) +{ + struct apk_istream_bstream *isbs; + + isbs = malloc(sizeof(struct apk_istream_bstream)); + if (isbs == NULL) + return NULL; + + isbs->bs = (struct apk_bstream) { + .read = is_bs_read, + .close = is_bs_close, + }; + isbs->is = istream; + csum_init(&isbs->csum_ctx); + + return &isbs->bs; +} + +struct apk_bstream *apk_bstream_from_fd(int fd) +{ + /* FIXME: Write mmap based bstream for files */ + return apk_bstream_from_istream(apk_istream_from_fd(fd)); +} + +apk_blob_t apk_blob_from_istream(struct apk_istream *is, size_t size) +{ + void *ptr; + size_t rsize; + + ptr = malloc(size); + if (ptr == NULL) + return APK_BLOB_NULL; + + rsize = is->read(is, ptr, size); + if (rsize < 0) { + free(ptr); + return APK_BLOB_NULL; + } + if (rsize != size) + ptr = realloc(ptr, rsize); + + return APK_BLOB_PTR_LEN(ptr, rsize); +} + diff --git a/src/package.c b/src/package.c index 1b41444..9132b18 100644 --- a/src/package.c +++ b/src/package.c @@ -254,7 +254,8 @@ static int read_info_line(void *ctx, apk_blob_t line) return 0; } -static int read_info_entry(struct apk_archive_entry *ae, void *ctx) +static int read_info_entry(void *ctx, const struct apk_archive_entry *ae, + struct apk_istream *is) { static struct { const char *str; @@ -277,7 +278,7 @@ static int read_info_entry(struct apk_archive_entry *ae, void *ctx) if (ae->name[0] == '.') { /* APK 2.0 format */ if (strcmp(ae->name, ".PKGINFO") == 0) { - apk_blob_t blob = apk_archive_entry_read(ae); + apk_blob_t blob = apk_blob_from_istream(is, ae->size); apk_blob_for_each_segment(blob, "\n", read_info_line, ctx); free(blob.ptr); /* FIXME: all done after checking if .INSTALL exists */ @@ -306,7 +307,7 @@ static int read_info_entry(struct apk_archive_entry *ae, void *ctx) for (i = 0; i < ARRAY_SIZE(fields); i++) { if (strcmp(fields[i].str, slash+1) == 0) { - apk_blob_t blob = apk_archive_entry_read(ae); + apk_blob_t blob = apk_blob_from_istream(is, ae->size); add_info(ri->db, ri->pkg, fields[i].field, trim(blob)); free(blob.ptr); @@ -326,8 +327,8 @@ static int read_info_entry(struct apk_archive_entry *ae, void *ctx) struct apk_package *apk_pkg_read(struct apk_database *db, const char *file) { struct read_info_ctx ctx; + struct apk_bstream *bs; struct stat st; - pthread_t tid; int fd; ctx.pkg = calloc(1, sizeof(struct apk_package)); @@ -341,27 +342,27 @@ struct apk_package *apk_pkg_read(struct apk_database *db, const char *file) fstat(fd, &st); fcntl(fd, F_SETFD, FD_CLOEXEC); - tid = apk_checksum_and_tee(&fd, ctx.pkg->csum); - if (fd < 0) + bs = apk_bstream_from_fd(fd); + if (bs == NULL) { + close(fd); goto err; + } ctx.db = db; ctx.pkg->size = st.st_size; ctx.has_install = 0; - if (apk_parse_tar_gz(fd, read_info_entry, &ctx) < 0) { + if (apk_parse_tar_gz(bs, read_info_entry, &ctx) < 0) { apk_error("File %s is not an APK archive", file); - pthread_join(tid, NULL); + bs->close(bs, NULL); goto err; } - pthread_join(tid, NULL); + bs->close(bs, ctx.pkg->csum); if (ctx.pkg->name == NULL) { apk_error("File %s is corrupted", file); goto err; } - close(fd); - /* Add implicit busybox dependency if there is scripts */ if (ctx.has_install) { struct apk_dependency dep = { @@ -405,7 +406,7 @@ int apk_pkg_get_state(struct apk_package *pkg) return APK_STATE_NO_INSTALL; } -int apk_pkg_add_script(struct apk_package *pkg, int fd, +int apk_pkg_add_script(struct apk_package *pkg, struct apk_istream *is, unsigned int type, unsigned int size) { struct apk_script *script; @@ -414,7 +415,7 @@ int apk_pkg_add_script(struct apk_package *pkg, int fd, script = malloc(sizeof(struct apk_script) + size); script->type = type; script->size = size; - r = read(fd, script->script, size); + r = is->read(is, script->script, size); if (r < 0) { free(script); return r; |