diff options
Diffstat (limited to 'src/archive.c')
-rw-r--r-- | src/archive.c | 241 |
1 files changed, 66 insertions, 175 deletions
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; -} - |