summaryrefslogtreecommitdiff
path: root/src/extract.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/extract.c')
-rw-r--r--src/extract.c96
1 files changed, 92 insertions, 4 deletions
diff --git a/src/extract.c b/src/extract.c
index 1e092c5..00d871f 100644
--- a/src/extract.c
+++ b/src/extract.c
@@ -7,22 +7,110 @@
* SPDX-License-Identifier: GPL-2.0-only
*/
+#include <spawn.h>
+#include <unistd.h>
#include <sys/stat.h>
+#include <sys/wait.h>
#include <sys/xattr.h>
-#include <unistd.h>
+#include "apk_context.h"
#include "apk_extract.h"
+static int uvol_run(struct apk_ctx *ac, char *action, const char *volname, char *arg1, char *arg2)
+{
+ struct apk_out *out = &ac->out;
+ pid_t pid;
+ int r, status;
+ char *argv[] = { (char*)apk_ctx_get_uvol(ac), action, (char*) volname, arg1, arg2, 0 };
+ posix_spawn_file_actions_t act;
+
+ posix_spawn_file_actions_init(&act);
+ posix_spawn_file_actions_addclose(&act, STDIN_FILENO);
+ r = posix_spawn(&pid, apk_ctx_get_uvol(ac), &act, 0, argv, environ);
+ posix_spawn_file_actions_destroy(&act);
+ if (r != 0) {
+ apk_err(out, "%s: uvol exec error: %s", volname, apk_error_str(r));
+ return r;
+ }
+ while (waitpid(pid, &status, 0) < 0 && errno == EINTR);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ apk_err(out, "%s: uvol exited with error %d", volname, WEXITSTATUS(status));
+ return -APKE_UVOL;
+ }
+ return 0;
+}
+
+static int uvol_extract(struct apk_ctx *ac, const char *volname, char *arg1, off_t sz,
+ struct apk_istream *is, apk_progress_cb cb, void *cb_ctx)
+{
+ struct apk_out *out = &ac->out;
+ struct apk_ostream *os;
+ pid_t pid;
+ int r, status, pipefds[2];
+ char *argv[] = { (char*)apk_ctx_get_uvol(ac), "write", (char*) volname, arg1, 0 };
+ posix_spawn_file_actions_t act;
+
+ if (pipe2(pipefds, O_CLOEXEC) != 0) return -errno;
+
+ posix_spawn_file_actions_init(&act);
+ posix_spawn_file_actions_adddup2(&act, pipefds[0], STDIN_FILENO);
+ r = posix_spawn(&pid, apk_ctx_get_uvol(ac), &act, 0, argv, environ);
+ posix_spawn_file_actions_destroy(&act);
+ if (r != 0) {
+ apk_err(out, "%s: uvol exec error: %s", volname, apk_error_str(r));
+ return r;
+ }
+ close(pipefds[0]);
+ os = apk_ostream_to_fd(pipefds[1]);
+ apk_stream_copy(is, os, sz, cb, cb_ctx, 0);
+ r = apk_ostream_close(os);
+ if (r != 0) {
+ if (r >= 0) r = -APKE_UVOL;
+ apk_err(out, "%s: uvol write error: %s", volname, apk_error_str(r));
+ return r;
+ }
+
+ while (waitpid(pid, &status, 0) < 0 && errno == EINTR);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ apk_err(out, "%s: uvol exited with error %d", volname, WEXITSTATUS(status));
+ return -APKE_UVOL;
+ }
+
+ return 0;
+}
+
+static int apk_extract_volume(struct apk_ctx *ac, const struct apk_file_info *fi,
+ struct apk_istream *is, apk_progress_cb cb, void *cb_ctx)
+{
+ char size[64];
+ int r;
+
+ snprintf(size, sizeof size, "%ju", fi->size);
+ r = uvol_run(ac, "create", fi->uvol_name, size, "ro");
+ if (r != 0) return r;
+
+ r = uvol_extract(ac, fi->uvol_name, size, fi->size, is, cb, cb_ctx);
+ if (r == 0) r = uvol_run(ac, "up", fi->uvol_name, 0, 0);
+ if (r != 0) uvol_run(ac, "remove", fi->uvol_name, 0, 0);
+ return r;
+}
+
int apk_extract_file(int atfd, const struct apk_file_info *ae,
const char *extract_name, const char *link_target,
struct apk_istream *is,
- apk_progress_cb cb, void *cb_ctx, struct apk_digest_ctx *dctx,
- unsigned int extract_flags, struct apk_out *out)
+ apk_progress_cb cb, void *cb_ctx,
+ unsigned int extract_flags, struct apk_ctx *ac)
{
+ struct apk_out *out = &ac->out;
struct apk_xattr *xattr;
const char *fn = extract_name ?: ae->name;
int fd, r = -1, atflags = 0, ret = 0;
+ if (ae->uvol_name && is) {
+ if (extract_name || link_target) return -APKE_UVOL;
+ return apk_extract_volume(ac, ae, is, cb, cb_ctx);
+ }
+
if (!S_ISDIR(ae->mode) && !(extract_flags & APK_EXTRACTF_NO_OVERWRITE)) {
if (unlinkat(atfd, fn, 0) != 0 && errno != ENOENT) return -errno;
}
@@ -46,7 +134,7 @@ int apk_extract_file(int atfd, const struct apk_file_info *ae,
ret = PTR_ERR(os);
break;
}
- apk_stream_copy(is, os, ae->size, cb, cb_ctx, dctx);
+ apk_stream_copy(is, os, ae->size, cb, cb_ctx, 0);
r = apk_ostream_close(os);
if (r < 0) {
unlinkat(atfd, fn, 0);