From fb3d3c5e4a19724c903276c96267e90bef546546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Ter=C3=A4s?= Date: Thu, 10 Jun 2021 11:41:23 +0300 Subject: [PATCH] extract: add experimental support for uvol volumes --- src/apk_context.h | 2 + src/app_extract.c | 104 ++++++++++++++++++++++++++++++++++++++++++++-- src/context.c | 1 + 3 files changed, 104 insertions(+), 3 deletions(-) diff --git a/src/apk_context.h b/src/apk_context.h index 2b4b889..3a6f2f2 100644 --- a/src/apk_context.h +++ b/src/apk_context.h @@ -64,6 +64,7 @@ struct apk_ctx { const char *keys_dir; const char *cache_dir; const char *repositories_file; + const char *uvol; struct apk_string_array *repository_list; struct apk_string_array *private_keys; @@ -84,5 +85,6 @@ static inline int apk_ctx_fd_root(struct apk_ctx *ac) { return ac->root_fd; } static inline time_t apk_ctx_since(struct apk_ctx *ac, time_t since) { return (ac->force & APK_FORCE_REFRESH) ? APK_ISTREAM_FORCE_REFRESH : since; } +static inline const char *apk_ctx_get_uvol(struct apk_ctx *ac) { return ac->uvol; } #endif diff --git a/src/app_extract.c b/src/app_extract.c index acee37b..89663b5 100644 --- a/src/app_extract.c +++ b/src/app_extract.c @@ -9,8 +9,10 @@ #include #include #include +#include #include #include +#include #include "apk_applet.h" #include "apk_print.h" @@ -29,6 +31,7 @@ struct extract_ctx { unsigned int cur_path, cur_file; struct apk_pathbuilder pb; + unsigned int is_uvol : 1; }; @@ -67,6 +70,93 @@ static void apk_extract_acl(struct apk_file_info *fi, struct adb_obj *o, struct fi->gid = apk_id_cache_resolve_gid(idc, adb_ro_blob(o, ADBI_ACL_GROUP), 65534); } +static int uvol_detect(struct apk_ctx *ac, struct apk_pathbuilder *pb) +{ + apk_blob_t b = apk_pathbuilder_get(pb); + if (!apk_ctx_get_uvol(ac)) return 0; + return apk_blob_starts_with(b, APK_BLOB_STRLIT("uvol")) && + (b.len == 4 || b.ptr[4] == '/'); +} + +static int uvol_run(struct apk_ctx *ac, char *action, 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, 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; + } + waitpid(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + apk_err(out, "%s: uvol exited with error %d", volname, WEXITSTATUS(status)); + return -EIO; + } + return 0; +} + +static int uvol_extract(struct apk_ctx *ac, char *action, char *volname, char *arg1, off_t sz, struct apk_istream *is, struct apk_digest_ctx *dctx) +{ + struct apk_out *out = &ac->out; + pid_t pid; + int r, status, pipefds[2]; + char *argv[] = { (char*)apk_ctx_get_uvol(ac), action, 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]); + r = apk_istream_splice(is, pipefds[1], sz, 0, 0, dctx); + close(pipefds[1]); + if (r != sz) { + if (r >= 0) r = -EIO; + apk_err(out, "%s: uvol write error: %s", volname, apk_error_str(r)); + return r; + } + + waitpid(pid, &status, 0); + if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { + apk_err(out, "%s: uvol exited with error %d", volname, WEXITSTATUS(status)); + return -EIO; + } + + return 0; +} + +static int apk_extract_volume(struct apk_ctx *ac, struct apk_file_info *fi, struct apk_istream *is, struct apk_digest_ctx *dctx) +{ + char *volname = (char*) fi->name, size[64]; + int r; + + snprintf(size, sizeof size, "%ju", fi->size); + + r = uvol_run(ac, "create", volname, (fi->mode & S_IWUSR) ? "rw" : "ro", size); + if (r != 0) return r; + r = uvol_extract(ac, "write", volname, size, fi->size, is, dctx); + if (r != 0) goto err; + r = uvol_run(ac, "up", volname, 0, 0); + if (r != 0) goto err; + return 0; +err: + uvol_run(ac, "remove", volname, 0, 0); + return r; +} + static int apk_extract_file(struct extract_ctx *ctx, off_t sz, struct apk_istream *is) { struct apk_ctx *ac = ctx->ac; @@ -87,9 +177,13 @@ static int apk_extract_file(struct extract_ctx *ctx, off_t sz, struct apk_istrea fi.mode |= S_IFREG; apk_digest_ctx_init(&dctx, fi.digest.alg); - r = apk_archive_entry_extract( - ctx->root_fd, &fi, 0, 0, is, 0, 0, &dctx, - ctx->extract_flags, out); + if (ctx->is_uvol) { + r = apk_extract_volume(ac, &fi, is, &dctx); + } else { + r = apk_archive_entry_extract( + ctx->root_fd, &fi, 0, 0, is, 0, 0, &dctx, + ctx->extract_flags, out); + } apk_digest_ctx_final(&dctx, &d); apk_digest_ctx_free(&dctx); if (r != 0) return r; @@ -106,6 +200,8 @@ static int apk_extract_directory(struct extract_ctx *ctx) }; struct adb_obj acl; + if (ctx->is_uvol) return 0; + apk_extract_acl(&fi, adb_ro_obj(&ctx->path, ADBI_DI_ACL, &acl), apk_ctx_get_id_cache(ctx->ac)); fi.mode |= S_IFDIR; @@ -116,6 +212,7 @@ static int apk_extract_directory(struct extract_ctx *ctx) static int apk_extract_next_file(struct extract_ctx *ctx) { + struct apk_ctx *ac = ctx->ac; apk_blob_t target; int r; @@ -137,6 +234,7 @@ static int apk_extract_next_file(struct extract_ctx *ctx) if (ctx->cur_path > adb_ra_num(&ctx->paths)) return 1; adb_ro_obj(&ctx->paths, ctx->cur_path, &ctx->path); apk_pathbuilder_setb(&ctx->pb, adb_ro_blob(&ctx->path, ADBI_DI_NAME)); + ctx->is_uvol = uvol_detect(ac, &ctx->pb); adb_ro_obj(&ctx->path, ADBI_DI_FILES, &ctx->files); r = apk_extract_directory(ctx); if (r != 0) return r; diff --git a/src/context.c b/src/context.c index ac586a8..7020b25 100644 --- a/src/context.c +++ b/src/context.c @@ -44,6 +44,7 @@ int apk_ctx_prepare(struct apk_ctx *ac) if (!ac->root) ac->root = "/"; if (!ac->cache_max_age) ac->cache_max_age = 4*60*60; /* 4 hours default */ if (!strcmp(ac->root, "/")) ac->flags |= APK_NO_CHROOT; /* skip chroot if root is default */ + ac->uvol = getenv("APK_UVOL"); ac->root_fd = openat(AT_FDCWD, ac->root, O_RDONLY | O_CLOEXEC); if (ac->root_fd < 0 && (ac->open_flags & APK_OPENF_CREATE)) {