summaryrefslogtreecommitdiff
path: root/src/archive.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/archive.c')
-rw-r--r--src/archive.c241
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;
-}
-