iSulad/0006-do-not-pause-container-when-copy.patch

1919 lines
62 KiB
Diff
Raw Normal View History

From b69da83db290057dde5dbe34e153fb0895e456e2 Mon Sep 17 00:00:00 2001
From: WangFengTu <wangfengtu@huawei.com>
Date: Tue, 29 Dec 2020 10:16:13 +0800
Subject: [PATCH 6/9] do not pause container when copy
and use libarchive to do unpack/tar instead of
execute tar command. Once not pause container,
we need to chroot to container's rootfs first
to avoid symlink attrack when copy.
Signed-off-by: WangFengTu <wangfengtu@huawei.com>
---
src/cmd/isula/main.c | 20 +
src/cmd/isula/stream/cp.c | 3 +-
.../executor/container_cb/execution_stream.c | 133 ++--
src/daemon/modules/image/oci/oci_load.c | 8 +-
.../graphdriver/devmapper/driver_devmapper.c | 6 +-
.../graphdriver/overlay2/driver_overlay2.c | 6 +-
src/utils/tar/isulad_tar.c | 405 +-----------
src/utils/tar/isulad_tar.h | 10 +-
src/utils/tar/util_archive.c | 611 ++++++++++++++++--
src/utils/tar/util_archive.h | 15 +-
10 files changed, 679 insertions(+), 538 deletions(-)
diff --git a/src/cmd/isula/main.c b/src/cmd/isula/main.c
index d4a66695..a69df5d5 100644
--- a/src/cmd/isula/main.c
+++ b/src/cmd/isula/main.c
@@ -14,6 +14,7 @@
******************************************************************************/
#include <stdio.h>
+#include <locale.h>
#include "isula_commands.h"
#include "create.h"
@@ -202,8 +203,27 @@ struct command g_commands[] = {
{ NULL, false, NULL, NULL, NULL, NULL } // End of the list
};
+static int set_locale()
+{
+ int ret = 0;
+
+ /* Change from the standard (C) to en_US.UTF-8 locale, so libarchive can handle filename conversions.*/
+ if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) {
+ fprintf(stderr, "Could not set locale to en_US.UTF-8:%s", strerror(errno));
+ ret = -1;
+ goto out;
+ }
+
+out:
+ return ret;
+}
+
int main(int argc, char **argv)
{
+ if (set_locale() != 0) {
+ exit(ECOMMON);
+ }
+
if (connect_client_ops_init()) {
return ECOMMON;
}
diff --git a/src/cmd/isula/stream/cp.c b/src/cmd/isula/stream/cp.c
index 4ebca2b3..e954ed3d 100644
--- a/src/cmd/isula/stream/cp.c
+++ b/src/cmd/isula/stream/cp.c
@@ -27,6 +27,7 @@
#include "path.h"
#include "isula_connect.h"
#include "isulad_tar.h"
+#include "util_archive.h"
#include "command_parser.h"
#include "connect.h"
#include "io_wrapper.h"
@@ -124,7 +125,7 @@ static int client_copy_from_container(const struct client_arguments *args, const
srcinfo->path = util_strdup_s(srcpath);
srcinfo->isdir = S_ISDIR(response->stat->mode);
- nret = archive_copy_to(&response->reader, false, srcinfo, resolved, &archive_err);
+ nret = archive_copy_to(&response->reader, srcinfo, resolved, &archive_err);
if (nret != 0) {
ret = nret;
}
diff --git a/src/daemon/executor/container_cb/execution_stream.c b/src/daemon/executor/container_cb/execution_stream.c
index fde5d41d..7d165fb9 100644
--- a/src/daemon/executor/container_cb/execution_stream.c
+++ b/src/daemon/executor/container_cb/execution_stream.c
@@ -46,6 +46,7 @@
#include "image_api.h"
#include "path.h"
#include "isulad_tar.h"
+#include "util_archive.h"
#include "container_api.h"
#include "error.h"
#include "isula_libutils/logger_json_file.h"
@@ -384,9 +385,18 @@ out:
return ret;
}
+static char *get_tar_path(const char *srcdir, const char *srcbase, const char *container_fs)
+{
+ if (!util_has_prefix(srcdir, container_fs)) {
+ ERROR("srcdir %s does not contain %s", srcdir, container_fs);
+ return NULL;
+ }
+ return util_path_join(srcdir + strlen(container_fs), srcbase);
+}
+
static int archive_and_send_copy_data(const stream_func_wrapper *stream,
struct isulad_copy_from_container_response *response, const char *resolvedpath,
- const char *abspath)
+ const char *abspath, const char *container_fs)
{
int ret = -1;
int nret;
@@ -399,6 +409,7 @@ static int archive_and_send_copy_data(const stream_func_wrapper *stream,
char *buf = NULL;
char cleaned[PATH_MAX + 2] = { 0 };
struct io_read_wrapper reader = { 0 };
+ char *tar_path = NULL;
buf = util_common_calloc_s(buf_len);
if (buf == NULL) {
@@ -422,7 +433,15 @@ static int archive_and_send_copy_data(const stream_func_wrapper *stream,
ERROR("split %s failed", abspath);
goto cleanup;
}
- nret = archive_path(srcdir, srcbase, absbase, false, &reader);
+
+ tar_path = get_tar_path(srcdir, srcbase, container_fs);
+ if (tar_path == NULL) {
+ goto cleanup;
+ }
+
+ DEBUG("archive chroot tar stream container_fs(%s) srcdir(%s) relative(%s) srcbase(%s) absbase(%s)",
+ container_fs, srcdir, tar_path, srcbase, absbase);
+ nret = archive_chroot_tar_stream(container_fs, tar_path, srcbase, absbase, &reader);
if (nret != 0) {
ERROR("Archive %s failed", resolvedpath);
goto cleanup;
@@ -445,6 +464,7 @@ static int archive_and_send_copy_data(const stream_func_wrapper *stream,
ret = 0;
cleanup:
+ free(tar_path);
free(buf);
free(srcdir);
free(srcbase);
@@ -583,58 +603,6 @@ static container_path_stat *resolve_and_stat_path(const char *rootpath, const ch
return stat;
}
-static int pause_container(const container_t *cont)
-{
- int ret = 0;
- rt_pause_params_t params = { 0 };
- const char *id = cont->common_config->id;
-
- params.rootpath = cont->root_path;
- params.state = cont->state_path;
- if (runtime_pause(id, cont->runtime, &params)) {
- ERROR("Failed to pause container:%s", id);
- ret = -1;
- goto out;
- }
-
- container_state_set_paused(cont->state);
-
- if (container_state_to_disk(cont)) {
- ERROR("Failed to save container \"%s\" to disk", id);
- ret = -1;
- goto out;
- }
-
-out:
- return ret;
-}
-
-static int resume_container(const container_t *cont)
-{
- int ret = 0;
- rt_resume_params_t params = { 0 };
- const char *id = cont->common_config->id;
-
- params.rootpath = cont->root_path;
- params.state = cont->state_path;
- if (runtime_resume(id, cont->runtime, &params)) {
- ERROR("Failed to resume container:%s", id);
- ret = -1;
- goto out;
- }
-
- container_state_reset_paused(cont->state);
-
- if (container_state_to_disk(cont)) {
- ERROR("Failed to save container \"%s\" to disk", id);
- ret = -1;
- goto out;
- }
-
-out:
- return ret;
-}
-
static int copy_from_container_cb(const struct isulad_copy_from_container_request *request,
const stream_func_wrapper *stream, char **err)
{
@@ -645,7 +613,6 @@ static int copy_from_container_cb(const struct isulad_copy_from_container_reques
container_path_stat *stat = NULL;
container_t *cont = NULL;
struct isulad_copy_from_container_response *response = NULL;
- bool need_pause = false;
DAEMON_CLEAR_ERRMSG();
if (request == NULL || stream == NULL || err == NULL) {
@@ -665,19 +632,10 @@ static int copy_from_container_cb(const struct isulad_copy_from_container_reques
goto unlock_container;
}
- need_pause = container_is_running(cont->state) && !container_is_paused(cont->state);
- if (need_pause) {
- if (pause_container(cont) != 0) {
- ERROR("can't copy to a container which is cannot be paused");
- isulad_set_error_message("can't copy to a container which is cannot be paused");
- goto unlock_container;
- }
- }
-
nret = im_mount_container_rootfs(cont->common_config->image_type, cont->common_config->image,
cont->common_config->id);
if (nret != 0) {
- goto unpause_container;
+ goto unlock_container;
}
stat = resolve_and_stat_path(cont->common_config->base_fs, request->srcpath, &resolvedpath, &abspath);
@@ -692,7 +650,7 @@ static int copy_from_container_cb(const struct isulad_copy_from_container_reques
goto cleanup_rootfs;
}
- nret = archive_and_send_copy_data(stream, response, resolvedpath, abspath);
+ nret = archive_and_send_copy_data(stream, response, resolvedpath, abspath, cont->common_config->base_fs);
if (nret < 0) {
ERROR("Failed to send archive data");
goto cleanup_rootfs;
@@ -705,10 +663,6 @@ cleanup_rootfs:
cont->common_config->id) != 0) {
WARN("Can not umount rootfs of container: %s", cont->common_config->id);
}
-unpause_container:
- if (need_pause && resume_container(cont) != 0) {
- ERROR("can't resume container which has been paused before copy");
- }
unlock_container:
container_unlock(cont);
container_unref(cont);
@@ -777,15 +731,16 @@ static ssize_t extract_stream_to_io_read(void *content, void *buf, size_t buf_le
return (ssize_t)(copy.data_len);
}
-int read_and_extract_archive(stream_func_wrapper *stream, const char *resolved_path, const char *transform)
+static int read_and_extract_archive(stream_func_wrapper *stream, const char *container_fs,
+ const char *dstdir_in_container, const char *src_rebase,
+ const char *dst_rebase)
{
int ret = -1;
char *err = NULL;
struct io_read_wrapper content = { 0 };
-
content.context = stream;
content.read = extract_stream_to_io_read;
- ret = archive_untar(&content, false, resolved_path, transform, &err);
+ ret = archive_chroot_untar_stream(&content, container_fs, dstdir_in_container, src_rebase, dst_rebase, &err);
if (ret != 0) {
ERROR("Can not untar to container: %s", (err != NULL) ? err : "unknown");
isulad_set_error_message("Can not untar to container: %s", (err != NULL) ? err : "unknown");
@@ -795,7 +750,7 @@ int read_and_extract_archive(stream_func_wrapper *stream, const char *resolved_p
}
static char *copy_to_container_get_dstdir(const container_t *cont, const container_copy_to_request *request,
- char **transform)
+ char **src_base, char **dst_base)
{
char *dstdir = NULL;
char *error = NULL;
@@ -836,7 +791,7 @@ static char *copy_to_container_get_dstdir(const container_t *cont, const contain
srcinfo.path = request->src_path;
srcinfo.rebase_name = request->src_rebase_name;
- dstdir = prepare_archive_copy(&srcinfo, dstinfo, transform, &error);
+ dstdir = prepare_archive_copy(&srcinfo, dstinfo, src_base, dst_base, &error);
if (dstdir == NULL) {
if (error == NULL) {
ERROR("Can not prepare archive copy");
@@ -930,9 +885,9 @@ static int copy_to_container_cb(const container_copy_to_request *request, stream
char *resolvedpath = NULL;
char *abspath = NULL;
char *dstdir = NULL;
- char *transform = NULL;
+ char *src_base = NULL;
+ char *dst_base = NULL;
container_t *cont = NULL;
- bool need_pause = false;
DAEMON_CLEAR_ERRMSG();
if (request == NULL || stream == NULL || err == NULL) {
@@ -952,22 +907,13 @@ static int copy_to_container_cb(const container_copy_to_request *request, stream
goto unlock_container;
}
- need_pause = container_is_running(cont->state) && !container_is_paused(cont->state);
- if (need_pause) {
- if (pause_container(cont) != 0) {
- ERROR("can't copy to a container which is cannot be paused");
- isulad_set_error_message("can't copy to a container which is cannot be paused");
- goto unlock_container;
- }
- }
-
nret = im_mount_container_rootfs(cont->common_config->image_type, cont->common_config->image,
cont->common_config->id);
if (nret != 0) {
- goto unpause_container;
+ goto unlock_container;
}
- dstdir = copy_to_container_get_dstdir(cont, request, &transform);
+ dstdir = copy_to_container_get_dstdir(cont, request, &src_base, &dst_base);
if (dstdir == NULL) {
goto cleanup_rootfs;
}
@@ -982,7 +928,8 @@ static int copy_to_container_cb(const container_copy_to_request *request, stream
goto cleanup_rootfs;
}
- nret = read_and_extract_archive(stream, resolvedpath, transform);
+ nret = read_and_extract_archive(stream, cont->common_config->base_fs,
+ dstdir, src_base, dst_base);
if (nret < 0) {
ERROR("Failed to send archive data");
goto cleanup_rootfs;
@@ -997,11 +944,6 @@ cleanup_rootfs:
WARN("Can not umount rootfs of container: %s", cont->common_config->id);
}
-unpause_container:
- if (need_pause && resume_container(cont) != 0) {
- ERROR("can't resume container which has been paused before copy");
- }
-
unlock_container:
container_unlock(cont);
container_unref(cont);
@@ -1013,7 +955,8 @@ pack_response:
free(resolvedpath);
free(abspath);
free(dstdir);
- free(transform);
+ free(src_base);
+ free(dst_base);
return ret;
}
diff --git a/src/daemon/modules/image/oci/oci_load.c b/src/daemon/modules/image/oci/oci_load.c
index 80647253..a8eecfe9 100644
--- a/src/daemon/modules/image/oci/oci_load.c
+++ b/src/daemon/modules/image/oci/oci_load.c
@@ -1061,6 +1061,7 @@ int oci_do_load(const im_load_request *request)
load_image_t *im = NULL;
char *digest = NULL;
char *dstdir = NULL;
+ char *err = NULL;
if (request == NULL || request->file == NULL) {
ERROR("Invalid input arguments, cannot load image");
@@ -1082,9 +1083,9 @@ int oci_do_load(const im_load_request *request)
}
options.whiteout_format = NONE_WHITEOUT_FORMATE;
- if (archive_unpack(&reader, dstdir, &options) != 0) {
- ERROR("Failed to unpack to :%s", dstdir);
- isulad_try_set_error_message("Failed to unpack to :%s", dstdir);
+ if (archive_unpack(&reader, dstdir, &options, &err) != 0) {
+ ERROR("Failed to unpack to %s: %s", dstdir, err);
+ isulad_try_set_error_message("Failed to unpack to %s: %s", dstdir, err);
ret = -1;
goto out;
}
@@ -1167,5 +1168,6 @@ out:
WARN("failed to remove directory %s", dstdir);
}
free(dstdir);
+ free(err);
return ret;
}
diff --git a/src/daemon/modules/image/oci/storage/layer_store/graphdriver/devmapper/driver_devmapper.c b/src/daemon/modules/image/oci/storage/layer_store/graphdriver/devmapper/driver_devmapper.c
index f2586f0d..e91ffe05 100644
--- a/src/daemon/modules/image/oci/storage/layer_store/graphdriver/devmapper/driver_devmapper.c
+++ b/src/daemon/modules/image/oci/storage/layer_store/graphdriver/devmapper/driver_devmapper.c
@@ -319,6 +319,7 @@ int devmapper_apply_diff(const char *id, const struct graphdriver *driver, const
char *layer_fs = NULL;
int ret = 0;
struct archive_options options = { 0 };
+ char *err = NULL;
if (!util_valid_str(id) || driver == NULL || content == NULL) {
ERROR("invalid argument to apply diff with id(%s)", id);
@@ -340,8 +341,8 @@ int devmapper_apply_diff(const char *id, const struct graphdriver *driver, const
}
options.whiteout_format = REMOVE_WHITEOUT_FORMATE;
- if (archive_unpack(content, layer_fs, &options) != 0) {
- ERROR("devmapper: failed to unpack to :%s", layer_fs);
+ if (archive_unpack(content, layer_fs, &options, &err) != 0) {
+ ERROR("devmapper: failed to unpack to %s: %s", layer_fs, err);
ret = -1;
goto out;
}
@@ -355,6 +356,7 @@ int devmapper_apply_diff(const char *id, const struct graphdriver *driver, const
out:
free_driver_mount_opts(mount_opts);
free(layer_fs);
+ free(err);
return ret;
}
diff --git a/src/daemon/modules/image/oci/storage/layer_store/graphdriver/overlay2/driver_overlay2.c b/src/daemon/modules/image/oci/storage/layer_store/graphdriver/overlay2/driver_overlay2.c
index 6cdabe54..659d9d52 100644
--- a/src/daemon/modules/image/oci/storage/layer_store/graphdriver/overlay2/driver_overlay2.c
+++ b/src/daemon/modules/image/oci/storage/layer_store/graphdriver/overlay2/driver_overlay2.c
@@ -1657,6 +1657,7 @@ int overlay2_apply_diff(const char *id, const struct graphdriver *driver, const
char *layer_dir = NULL;
char *layer_diff = NULL;
struct archive_options options = { 0 };
+ char *err = NULL;
if (id == NULL || driver == NULL || content == NULL) {
ERROR("invalid argument");
@@ -1680,14 +1681,15 @@ int overlay2_apply_diff(const char *id, const struct graphdriver *driver, const
options.whiteout_format = OVERLAY_WHITEOUT_FORMATE;
- ret = archive_unpack(content, layer_diff, &options);
+ ret = archive_unpack(content, layer_diff, &options, &err);
if (ret != 0) {
- ERROR("Failed to unpack to :%s", layer_diff);
+ ERROR("Failed to unpack to %s: %s", layer_diff, err);
ret = -1;
goto out;
}
out:
+ free(err);
free(layer_dir);
free(layer_diff);
return ret;
diff --git a/src/utils/tar/isulad_tar.c b/src/utils/tar/isulad_tar.c
index 5edf2ac3..03277373 100644
--- a/src/utils/tar/isulad_tar.c
+++ b/src/utils/tar/isulad_tar.c
@@ -31,17 +31,7 @@
#include "isula_libutils/log.h"
#include "error.h"
#include "isula_libutils/json_common.h"
-#include "io_wrapper.h"
-#include "utils_file.h"
-#include "utils_verify.h"
-
-#define TAR_MAX_OPTS 50
-#define TAR_CMD "tar"
-#define TAR_TRANSFORM_OPT "--transform"
-#define TAR_CREATE_OPT "-c"
-#define TAR_EXACT_OPT "-x"
-#define TAR_CHDIR_OPT "-C"
-#define TAR_GZIP_OPT "-z"
+#include "util_archive.h"
static void set_char_to_separator(char *p)
{
@@ -126,110 +116,6 @@ int gzip(const char *filename, size_t len)
return status;
}
-struct archive_context {
- int stdin_fd;
- int stdout_fd;
- int stderr_fd;
- pid_t pid;
-};
-
-static ssize_t archive_context_read(void *context, void *buf, size_t len)
-{
- struct archive_context *ctx = (struct archive_context *)context;
- if (ctx == NULL) {
- return -1;
- }
- if (ctx->stdout_fd >= 0) {
- return util_read_nointr(ctx->stdout_fd, buf, len);
- }
- return 0;
-}
-
-static ssize_t archive_context_write(const void *context, const void *buf, size_t len)
-{
- struct archive_context *ctx = (struct archive_context *)context;
- if (ctx == NULL) {
- return -1;
- }
- if (ctx->stdin_fd >= 0) {
- return util_write_nointr(ctx->stdin_fd, buf, len);
- }
- return 0;
-}
-
-static int close_wait_pid(struct archive_context *ctx, int *status)
-{
- int ret = 0;
-
- // close stdin and stdout first, this will make sure the process of tar exit.
- if (ctx->stdin_fd >= 0) {
- close(ctx->stdin_fd);
- }
-
- if (ctx->stdout_fd >= 0) {
- close(ctx->stdout_fd);
- }
-
- if (ctx->pid > 0) {
- if (waitpid(ctx->pid, status, 0) != ctx->pid) {
- ERROR("Failed to wait pid %u", ctx->pid);
- ret = -1;
- }
- }
-
- return ret;
-}
-
-static int archive_context_close(void *context, char **err)
-{
- int ret = 0;
- int status = 0;
- char *reason = NULL;
- ssize_t size_read = 0;
- char buffer[BUFSIZ + 1] = { 0 };
- struct archive_context *ctx = (struct archive_context *)context;
- char *marshaled = NULL;
-
- if (ctx == NULL) {
- return 0;
- }
-
- ret = close_wait_pid(ctx, &status);
-
- if (WIFSIGNALED((unsigned int)status)) {
- status = WTERMSIG(status);
- reason = "signaled";
- } else if (WIFEXITED(status)) {
- status = WEXITSTATUS(status);
- reason = "exited";
- } else {
- reason = "unknown";
- }
-
- if (ctx->stderr_fd >= 0) {
- size_read = util_read_nointr(ctx->stderr_fd, buffer, BUFSIZ);
- if (size_read > 0) {
- reason = buffer;
- marshaled = util_marshal_string(buffer);
- if (marshaled == NULL) {
- ERROR("Can not marshal json buffer: %s", buffer);
- } else {
- reason = marshaled;
- }
- }
- close(ctx->stderr_fd);
- }
-
- if (size_read > 0 || status != 0) {
- format_errorf(err, "tar exited with status %d: %s", status, reason);
- ret = -1;
- }
-
- free(marshaled);
- free(ctx);
- return ret;
-}
-
static int get_rebase_name(const char *path, const char *real_path, char **resolved_path, char **rebase_name)
{
int nret;
@@ -502,50 +388,8 @@ static bool asserts_directory(const char *path)
return util_has_trailing_path_separator(path) || util_specify_current_dir(path);
}
-static char *format_transform_of_tar(const char *srcbase, const char *dstbase)
-{
- char *transform = NULL;
- const char *src_escaped = srcbase;
- const char *dst_escaped = dstbase;
- int nret;
- size_t len;
-
- if (srcbase == NULL || dstbase == NULL) {
- return NULL;
- }
-
- // escape "/" by "." to avoid generating leading / in tar archive which is dangerous to host when untar.
- // this means tar or untar with leading / is forbidden and may got error, take care of this when coding.
- if (strcmp(srcbase, "/") == 0) {
- src_escaped = ".";
- }
-
- if (strcmp(dstbase, "/") == 0) {
- dst_escaped = ".";
- }
-
- len = strlen(src_escaped) + strlen(dst_escaped) + 5;
- if (len > PATH_MAX) {
- ERROR("Invalid path length");
- return NULL;
- }
-
- transform = util_common_calloc_s(len);
- if (transform == NULL) {
- ERROR("Out of memory");
- return NULL;
- }
- nret = snprintf(transform, len, "s/%s/%s/", src_escaped, dst_escaped);
- if (nret < 0 || (size_t)nret >= len) {
- ERROR("Failed to print string");
- free(transform);
- return NULL;
- }
- return transform;
-}
-
char *prepare_archive_copy(const struct archive_copy_info *srcinfo, const struct archive_copy_info *dstinfo,
- char **transform, char **err)
+ char **src_base, char **dst_base, char **err)
{
char *dstdir = NULL;
char *srcbase = NULL;
@@ -573,7 +417,8 @@ char *prepare_archive_copy(const struct archive_copy_info *srcinfo, const struct
free(srcbase);
srcbase = util_strdup_s(srcinfo->rebase_name);
}
- *transform = format_transform_of_tar(srcbase, dstbase);
+ *src_base = util_strdup_s(srcbase);
+ *dst_base = util_strdup_s(dstbase);
} else if (srcinfo->isdir) {
// dst does not exist and src is a directory, untar the content to parent of dest,
// and rename basename of src name to dest's basename.
@@ -581,7 +426,8 @@ char *prepare_archive_copy(const struct archive_copy_info *srcinfo, const struct
free(srcbase);
srcbase = util_strdup_s(srcinfo->rebase_name);
}
- *transform = format_transform_of_tar(srcbase, dstbase);
+ *src_base = util_strdup_s(srcbase);
+ *dst_base = util_strdup_s(dstbase);
} else if (asserts_directory(dstinfo->path)) {
// dst does not exist and is want to be created as a directory, but src is not a directory, report error.
format_errorf(err, "no such directory, can not copy file");
@@ -594,7 +440,8 @@ char *prepare_archive_copy(const struct archive_copy_info *srcinfo, const struct
free(srcbase);
srcbase = util_strdup_s(srcinfo->rebase_name);
}
- *transform = format_transform_of_tar(srcbase, dstbase);
+ *src_base = util_strdup_s(srcbase);
+ *dst_base = util_strdup_s(dstbase);
}
cleanup:
@@ -603,125 +450,14 @@ cleanup:
return dstdir;
}
-static void close_pipe_fd(int pipe_fd[])
-{
- if (pipe_fd[0] != -1) {
- close(pipe_fd[0]);
- pipe_fd[0] = -1;
- }
- if (pipe_fd[1] != -1) {
- close(pipe_fd[1]);
- pipe_fd[1] = -1;
- }
-}
-
-int archive_untar(const struct io_read_wrapper *content, bool compression, const char *dstdir, const char *transform,
- char **err)
-{
- int stdin_pipe[2] = { -1, -1 };
- int stderr_pipe[2] = { -1, -1 };
- int ret = -1;
- int cret = 0;
- pid_t pid;
- struct archive_context *ctx = NULL;
- char *buf = NULL;
- size_t buf_len = ARCHIVE_BLOCK_SIZE;
- ssize_t read_len;
- const char *params[TAR_MAX_OPTS] = { NULL };
-
- buf = util_common_calloc_s(buf_len);
- if (buf == NULL) {
- ERROR("Out of memory");
- return -1;
- }
-
- if (pipe(stderr_pipe) != 0) {
- ERROR("Failed to create pipe: %s", strerror(errno));
- goto cleanup;
- }
- if (pipe(stdin_pipe) != 0) {
- ERROR("Failed to create pipe: %s", strerror(errno));
- goto cleanup;
- }
-
- pid = fork();
- if (pid == (pid_t) -1) {
- ERROR("Failed to fork: %s", strerror(errno));
- goto cleanup;
- }
-
- if (pid == (pid_t)0) {
- int i = 0;
- // child process, dup2 stderr[1] to stderr, stdout[0] to stdin.
- close(stderr_pipe[0]);
- dup2(stderr_pipe[1], 2);
- close(stdin_pipe[1]);
- dup2(stdin_pipe[0], 0);
-
- params[i++] = TAR_CMD;
- params[i++] = TAR_EXACT_OPT;
- if (compression) {
- params[i++] = TAR_GZIP_OPT;
- }
- params[i++] = TAR_CHDIR_OPT;
- params[i++] = dstdir;
- if (transform != NULL) {
- params[i++] = TAR_TRANSFORM_OPT;
- params[i++] = transform;
- }
-
- execvp(TAR_CMD, (char * const *)params);
-
- fprintf(stderr, "Failed to exec tar: %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- close(stderr_pipe[1]);
- stderr_pipe[1] = -1;
- close(stdin_pipe[0]);
- stdin_pipe[0] = -1;
-
- ctx = util_common_calloc_s(sizeof(struct archive_context));
- if (ctx == NULL) {
- goto cleanup;
- }
-
- ctx->pid = pid;
- ctx->stdin_fd = stdin_pipe[1];
- stdin_pipe[1] = -1;
- ctx->stdout_fd = -1;
- ctx->stderr_fd = stderr_pipe[0];
- stderr_pipe[0] = -1;
-
- read_len = content->read(content->context, buf, buf_len);
- while (read_len > 0) {
- ssize_t writed_len = archive_context_write(ctx, buf, (size_t)read_len);
- if (writed_len < 0) {
- DEBUG("Tar may exited: %s", strerror(errno));
- break;
- }
- read_len = content->read(content->context, buf, buf_len);
- }
-
- ret = 0;
-
-cleanup:
- free(buf);
- cret = archive_context_close(ctx, err);
- ret = (cret != 0) ? cret : ret;
- close_pipe_fd(stderr_pipe);
- close_pipe_fd(stdin_pipe);
-
- return ret;
-}
-
-int archive_copy_to(const struct io_read_wrapper *content, bool compression, const struct archive_copy_info *srcinfo,
+int archive_copy_to(const struct io_read_wrapper *content, const struct archive_copy_info *srcinfo,
const char *dstpath, char **err)
{
int ret = -1;
struct archive_copy_info *dstinfo = NULL;
char *dstdir = NULL;
- char *transform = NULL;
+ char *src_base = NULL;
+ char *dst_base = NULL;
dstinfo = copy_info_destination_path(dstpath, err);
if (dstinfo == NULL) {
@@ -729,128 +465,23 @@ int archive_copy_to(const struct io_read_wrapper *content, bool compression, con
return -1;
}
- dstdir = prepare_archive_copy(srcinfo, dstinfo, &transform, err);
+ dstdir = prepare_archive_copy(srcinfo, dstinfo, &src_base, &dst_base, err);
if (dstdir == NULL) {
ERROR("Can not prepare archive copy");
goto cleanup;
}
- ret = archive_untar(content, compression, dstdir, transform, err);
+ ret = archive_chroot_untar_stream(content, dstdir, ".", src_base, dst_base, err);
cleanup:
free_archive_copy_info(dstinfo);
free(dstdir);
- free(transform);
- return ret;
-}
-
-static void close_archive_pipes_fd(int *pipes, size_t pipe_size)
-{
- size_t i = 0;
-
- for (i = 0; i < pipe_size; i++) {
- if (pipes[i] >= 0) {
- close(pipes[i]);
- pipes[i] = -1;
- }
- }
-}
-
-/*
- * Archive file or directory.
- * param src : file or directory to compression.
- * param compression : using gzip compression or not
- * param exclude_base : exclude source basename in the archived file or not
- * return : zero if archive success, non-zero if not.
- */
-int archive_path(const char *srcdir, const char *srcbase, const char *rebase_name, bool compression,
- struct io_read_wrapper *archive_reader)
-{
- int stderr_pipe[2] = { -1, -1 };
- int stdout_pipe[2] = { -1, -1 };
- int ret = -1;
- pid_t pid;
- struct archive_context *ctx = NULL;
- char *transform = NULL;
- const char *params[TAR_MAX_OPTS] = { NULL };
-
- transform = format_transform_of_tar(srcbase, rebase_name);
-
- if (pipe(stderr_pipe) != 0) {
- ERROR("Failed to create pipe: %s", strerror(errno));
- goto free_out;
- }
- if (pipe(stdout_pipe) != 0) {
- ERROR("Failed to create pipe: %s", strerror(errno));
- goto free_out;
- }
-
- pid = fork();
- if (pid == (pid_t) -1) {
- ERROR("Failed to fork: %s", strerror(errno));
- goto free_out;
- }
-
- if (pid == (pid_t)0) {
- int i = 0;
- // child process, dup2 stderr[1] to stderr, stdout[1] to stdout.
- close(stderr_pipe[0]);
- close(stdout_pipe[0]);
- dup2(stderr_pipe[1], 2);
- dup2(stdout_pipe[1], 1);
-
- params[i++] = TAR_CMD;
- params[i++] = TAR_CREATE_OPT;
- if (compression) {
- params[i++] = TAR_GZIP_OPT;
- }
- params[i++] = TAR_CHDIR_OPT;
- params[i++] = srcdir;
- if (transform != NULL) {
- params[i++] = TAR_TRANSFORM_OPT;
- params[i++] = transform;
- }
- params[i++] = srcbase;
-
- execvp(TAR_CMD, (char * const *)params);
-
- fprintf(stderr, "Failed to exec tar: %s", strerror(errno));
- exit(EXIT_FAILURE);
- }
-
- close(stderr_pipe[1]);
- stderr_pipe[1] = -1;
- close(stdout_pipe[1]);
- stdout_pipe[1] = -1;
-
- ctx = util_common_calloc_s(sizeof(struct archive_context));
- if (ctx == NULL) {
- goto free_out;
- }
-
- ctx->stdin_fd = -1;
- ctx->stdout_fd = stdout_pipe[0];
- stdout_pipe[0] = -1;
- ctx->stderr_fd = stderr_pipe[0];
- stderr_pipe[0] = -1;
- ctx->pid = pid;
-
- archive_reader->close = archive_context_close;
- archive_reader->context = ctx;
- ctx = NULL;
- archive_reader->read = archive_context_read;
-
- ret = 0;
-free_out:
- free(transform);
- close_archive_pipes_fd(stderr_pipe, 2);
- close_archive_pipes_fd(stdout_pipe, 2);
- free(ctx);
-
+ free(src_base);
+ free(dst_base);
return ret;
}
-int tar_resource_rebase(const char *path, const char *rebase, struct io_read_wrapper *archive_reader, char **err)
+static int tar_resource_rebase(const char *path, const char *rebase, struct io_read_wrapper *archive_reader, char **err)
{
int ret = -1;
int nret;
@@ -868,8 +499,8 @@ int tar_resource_rebase(const char *path, const char *rebase, struct io_read_wra
goto cleanup;
}
- DEBUG("Copying %s from %s", srcbase, srcdir);
- nret = archive_path(srcdir, srcbase, rebase, false, archive_reader);
+ DEBUG("chroot tar stream srcdir(%s) srcbase(%s) rebase(%s)", srcdir, srcbase, rebase);
+ nret = archive_chroot_tar_stream(srcdir, srcbase, srcbase, rebase, archive_reader);
if (nret < 0) {
ERROR("Can not archive path: %s", path);
goto cleanup;
diff --git a/src/utils/tar/isulad_tar.h b/src/utils/tar/isulad_tar.h
index e2b78463..c773fe9b 100644
--- a/src/utils/tar/isulad_tar.h
+++ b/src/utils/tar/isulad_tar.h
@@ -57,19 +57,13 @@ int gzip(const char *filename, size_t len);
struct archive_copy_info *copy_info_source_path(const char *path, bool follow_link, char **err);
char *prepare_archive_copy(const struct archive_copy_info *srcinfo, const struct archive_copy_info *dstinfo,
- char **transform, char **err);
+ char **src_base, char **dst_base, char **err);
int tar_resource(const struct archive_copy_info *info, struct io_read_wrapper *archive_reader, char **err);
-int archive_untar(const struct io_read_wrapper *content, bool compression, const char *dstdir, const char *transform,
- char **err);
-
-int archive_copy_to(const struct io_read_wrapper *content, bool compression, const struct archive_copy_info *srcinfo,
+int archive_copy_to(const struct io_read_wrapper *content, const struct archive_copy_info *srcinfo,
const char *dstpath, char **err);
-int archive_path(const char *srcdir, const char *srcbase, const char *rebase_name, bool compression,
- struct io_read_wrapper *archive_reader);
-
#ifdef __cplusplus
}
#endif
diff --git a/src/utils/tar/util_archive.c b/src/utils/tar/util_archive.c
index 234e661e..7a28286a 100644
--- a/src/utils/tar/util_archive.c
+++ b/src/utils/tar/util_archive.c
@@ -26,6 +26,7 @@
#include <errno.h>
#include <stdarg.h>
#include <stdint.h>
+#include <libgen.h>
#include "stdbool.h"
#include "utils.h"
@@ -33,11 +34,14 @@
#include "io_wrapper.h"
#include "utils_file.h"
#include "map.h"
+#include "path.h"
+#include "error.h"
struct archive;
struct archive_entry;
#define ARCHIVE_READ_BUFFER_SIZE (10 * 1024)
+#define ARCHIVE_WRITE_BUFFER_SIZE (10 * 1024)
#define TAR_DEFAULT_MODE 0600
#define TAR_DEFAULT_FLAG (O_WRONLY | O_CREAT | O_TRUNC)
@@ -45,6 +49,13 @@ struct archive_entry;
#define WHITEOUT_META_PREFIX ".wh..wh."
#define WHITEOUT_OPAQUEDIR ".wh..wh..opq"
+struct archive_context {
+ int stdin_fd;
+ int stdout_fd;
+ int stderr_fd;
+ pid_t pid;
+};
+
struct archive_content_data {
const struct io_read_wrapper *content;
char buff[ARCHIVE_READ_BUFFER_SIZE];
@@ -286,8 +297,104 @@ static whiteout_convert_call_back_t get_whiteout_convert_cb(whiteout_format_type
return NULL;
}
-int archive_unpack_handler(const struct io_read_wrapper *content, const char *dstdir,
- const struct archive_options *options)
+static char *to_relative_path(const char *path)
+{
+ char *dst_path = NULL;
+
+ if (path != NULL && path[0] == '/') {
+ if (strcmp(path, "/") == 0) {
+ dst_path = util_strdup_s(".");
+ } else {
+ dst_path = util_strdup_s(path + 1);
+ }
+ } else {
+ dst_path = util_strdup_s(path);
+ }
+
+ return dst_path;
+}
+
+static int rebase_pathname(struct archive_entry *entry, const char *src_base, const char *dst_base)
+{
+ int nret = 0;
+ const char *pathname = archive_entry_pathname(entry);
+ char path[PATH_MAX] = { 0 };
+
+ if (src_base == NULL || dst_base == NULL || !util_has_prefix(pathname, src_base)) {
+ return 0;
+ }
+
+ nret = snprintf(path, sizeof(path), "%s%s", dst_base, pathname + strlen(src_base));
+ if (nret < 0 || (size_t)nret >= sizeof(path)) {
+ ERROR("snprintf %s%s failed", dst_base, pathname + strlen(src_base));
+ fprintf(stderr, "snprintf %s%s failed", dst_base, pathname + strlen(src_base));
+ return -1;
+ }
+
+ archive_entry_set_pathname(entry, path);
+
+ return 0;
+}
+
+static char *update_entry_for_pathname(struct archive_entry *entry, const char *src_base, const char *dst_base)
+{
+ char *dst_path = NULL;
+ const char *pathname = NULL;
+
+ if (rebase_pathname(entry, src_base, dst_base) != 0) {
+ return NULL;
+ }
+
+ pathname = archive_entry_pathname(entry);
+ if (pathname == NULL) {
+ ERROR("Failed to get archive entry path name");
+ fprintf(stderr, "Failed to get archive entry path name");
+ return NULL;
+ }
+
+ // if path in archive is absolute, we need to translate it to relative because
+ // libarchive can not support absolute path when unpack
+ dst_path = to_relative_path(pathname);
+ if (dst_path == NULL) {
+ ERROR("translate %s to relative path failed", pathname);
+ fprintf(stderr, "translate %s to relative path failed", pathname);
+ goto out;
+ }
+
+ archive_entry_set_pathname(entry, dst_path);
+out:
+
+ return dst_path;
+}
+
+static int rebase_hardlink(struct archive_entry *entry, const char *src_base, const char *dst_base)
+{
+ int nret = 0;
+ const char *linkname = NULL;
+ char path[PATH_MAX] = { 0 };
+
+ linkname = archive_entry_hardlink(entry);
+ if (linkname == NULL) {
+ return 0;
+ }
+
+ if (src_base == NULL || dst_base == NULL || !util_has_prefix(linkname, src_base)) {
+ return 0;
+ }
+
+ nret = snprintf(path, sizeof(path), "%s%s", dst_base, linkname + strlen(src_base));
+ if (nret < 0 || (size_t)nret >= sizeof(path)) {
+ ERROR("snprintf %s%s failed", dst_base, linkname + strlen(src_base));
+ fprintf(stderr, "snprintf %s%s failed", dst_base, linkname + strlen(src_base));
+ return -1;
+ }
+
+ archive_entry_set_hardlink(entry, path);
+
+ return 0;
+}
+
+int archive_unpack_handler(const struct io_read_wrapper *content, const struct archive_options *options)
{
int ret = 0;
struct archive *a = NULL;
@@ -302,6 +409,7 @@ int archive_unpack_handler(const struct io_read_wrapper *content, const char *ds
unpacked_path_map = map_new(MAP_STR_BOOL, MAP_DEFAULT_CMP_FUNC, MAP_DEFAULT_FREE_FUNC);
if (unpacked_path_map == NULL) {
ERROR("Out of memory");
+ fprintf(stderr, "Out of memory");
ret = -1;
goto out;
}
@@ -309,6 +417,7 @@ int archive_unpack_handler(const struct io_read_wrapper *content, const char *ds
mydata = util_common_calloc_s(sizeof(struct archive_content_data));
if (mydata == NULL) {
ERROR("Memory out");
+ fprintf(stderr, "Memory out");
ret = -1;
goto out;
}
@@ -327,6 +436,7 @@ int archive_unpack_handler(const struct io_read_wrapper *content, const char *ds
a = archive_read_new();
if (a == NULL) {
ERROR("archive read new failed");
+ fprintf(stderr, "archive read new failed");
ret = -1;
goto out;
}
@@ -336,6 +446,7 @@ int archive_unpack_handler(const struct io_read_wrapper *content, const char *ds
ext = archive_write_disk_new();
if (ext == NULL) {
ERROR("archive write disk new failed");
+ fprintf(stderr, "archive write disk new failed");
ret = -1;
goto out;
}
@@ -345,6 +456,7 @@ int archive_unpack_handler(const struct io_read_wrapper *content, const char *ds
ret = archive_read_open(a, mydata, NULL, read_content, NULL);
if (ret != 0) {
SYSERROR("Failed to open archive");
+ fprintf(stderr, "Failed to open archive: %s", strerror(errno));
ret = -1;
goto out;
}
@@ -354,7 +466,6 @@ int archive_unpack_handler(const struct io_read_wrapper *content, const char *ds
for (;;) {
free(dst_path);
dst_path = NULL;
-
ret = archive_read_next_header(a, &entry);
if (ret == ARCHIVE_EOF) {
@@ -363,20 +474,23 @@ int archive_unpack_handler(const struct io_read_wrapper *content, const char *ds
if (ret != ARCHIVE_OK) {
ERROR("Warning reading tar header: %s", archive_error_string(a));
+ fprintf(stderr, "Warning reading tar header: %s", archive_error_string(a));
ret = -1;
goto out;
}
- const char *pathname = archive_entry_pathname(entry);
- if (pathname == NULL) {
- ERROR("Failed to get archive entry path name");
+ dst_path = update_entry_for_pathname(entry, options->src_base, options->dst_base);
+ if (dst_path == NULL) {
+ ERROR("Failed to update pathname");
+ fprintf(stderr, "Failed to update pathname");
ret = -1;
goto out;
}
- dst_path = util_path_join(dstdir, pathname);
- if (dst_path == NULL) {
- ERROR("Failed to get archive entry dst path %s/%s", dstdir, pathname);
+ ret = rebase_hardlink(entry, options->src_base, options->dst_base);
+ if (ret != 0) {
+ ERROR("Failed to rebase hardlink");
+ fprintf(stderr, "Failed to rebase hardlink");
ret = -1;
goto out;
}
@@ -385,22 +499,17 @@ int archive_unpack_handler(const struct io_read_wrapper *content, const char *ds
continue;
}
- // if path in archive is absolute, we need to translate it to relative because
- // libarchive can not support absolute path when unpack
- pathname = archive_entry_pathname(entry);
- if (pathname != NULL && pathname[0] == '/') {
- archive_entry_set_pathname(entry, pathname + 1);
- }
-
ret = archive_write_header(ext, entry);
if (ret != ARCHIVE_OK) {
ERROR("Fail to handle tar header: %s", archive_error_string(ext));
+ fprintf(stderr, "Fail to handle tar header: %s", archive_error_string(ext));
ret = -1;
goto out;
} else if (archive_entry_size(entry) > 0) {
ret = copy_data(a, ext);
if (ret != ARCHIVE_OK) {
ERROR("Failed to do copy tar data: %s", archive_error_string(ext));
+ fprintf(stderr, "Failed to do copy tar data: %s", archive_error_string(ext));
ret = -1;
goto out;
}
@@ -408,6 +517,7 @@ int archive_unpack_handler(const struct io_read_wrapper *content, const char *ds
ret = archive_write_finish_entry(ext);
if (ret != ARCHIVE_OK) {
ERROR("Failed to freeing archive entry: %s\n", archive_error_string(ext));
+ fprintf(stderr, "Failed to freeing archive entry: %s\n", archive_error_string(ext));
ret = -1;
goto out;
}
@@ -415,6 +525,7 @@ int archive_unpack_handler(const struct io_read_wrapper *content, const char *ds
bool b = true;
if (!map_replace(unpacked_path_map, (void *)dst_path, (void *)(&b))) {
ERROR("Failed to replace unpacked path map element");
+ fprintf(stderr, "Failed to replace unpacked path map element");
ret = -1;
goto out;
}
@@ -433,11 +544,32 @@ out:
return ret;
}
-int archive_unpack(const struct io_read_wrapper *content, const char *dstdir, const struct archive_options *options)
+static void close_archive_pipes_fd(int *pipes, size_t pipe_size)
+{
+ size_t i = 0;
+
+ for (i = 0; i < pipe_size; i++) {
+ if (pipes[i] >= 0) {
+ close(pipes[i]);
+ pipes[i] = -1;
+ }
+ }
+}
+
+int archive_unpack(const struct io_read_wrapper *content, const char *dstdir, const struct archive_options *options,
+ char **errmsg)
{
int ret = 0;
pid_t pid = -1;
- int keepfds[] = { -1, -1 };
+ int keepfds[] = { -1, -1, -1 };
+ int pipe_stderr[2] = { -1, -1 };
+ char errbuf[BUFSIZ] = { 0 };
+
+ if (pipe2(pipe_stderr, O_CLOEXEC) != 0) {
+ ERROR("Failed to create pipe");
+ ret = -1;
+ goto cleanup;
+ }
pid = fork();
if (pid == (pid_t) -1) {
@@ -448,26 +580,37 @@ int archive_unpack(const struct io_read_wrapper *content, const char *dstdir, co
if (pid == (pid_t)0) {
keepfds[0] = isula_libutils_get_log_fd();
keepfds[1] = *(int *)(content->context);
- ret = util_check_inherited_exclude_fds(true, keepfds, 2);
+ keepfds[2] = pipe_stderr[1];
+ ret = util_check_inherited_exclude_fds(true, keepfds, 3);
if (ret != 0) {
ERROR("Failed to close fds.");
+ fprintf(stderr, "Failed to close fds.");
+ ret = -1;
+ goto child_out;
+ }
+
+ // child process, dup2 pipe_for_read[1] to stderr,
+ if (dup2(pipe_stderr[1], 2) < 0) {
+ ERROR("Dup fd error: %s", strerror(errno));
ret = -1;
goto child_out;
}
if (chroot(dstdir) != 0) {
SYSERROR("Failed to chroot to %s", dstdir);
+ fprintf(stderr, "Failed to chroot to %s: %s", dstdir, strerror(errno));
ret = -1;
goto child_out;
}
if (chdir("/") != 0) {
SYSERROR("Failed to chroot to /");
+ fprintf(stderr, "Failed to chroot to /: %s", strerror(errno));
ret = -1;
goto child_out;
}
- ret = archive_unpack_handler(content, "/", options);
+ ret = archive_unpack_handler(content, options);
child_out:
if (ret != 0) {
@@ -476,13 +619,23 @@ child_out:
exit(EXIT_SUCCESS);
}
}
+ close(pipe_stderr[1]);
+ pipe_stderr[1] = -1;
ret = util_wait_for_pid(pid);
if (ret != 0) {
ERROR("Wait archive_untar_handler failed");
+ fcntl(pipe_stderr[0], F_SETFL, O_NONBLOCK);
+ if (read(pipe_stderr[0], errbuf, BUFSIZ) < 0) {
+ ERROR("read error message from child failed");
+ }
}
cleanup:
+ close_archive_pipes_fd(pipe_stderr, 2);
+ if (errmsg != NULL && strlen(errbuf) != 0) {
+ *errmsg = util_strdup_s(errbuf);
+ }
return ret;
}
@@ -569,19 +722,19 @@ static int copy_data_between_archives(struct archive *ar, struct archive *aw)
}
}
-int update_entry_for_hardlink(map_t *map_link, struct archive_entry *entry)
+int update_entry_for_hardlink(map_t *map_link, struct archive_entry *entry, const char *src_base, const char *dst_base)
{
const char *path = archive_entry_pathname(entry);
char *linkname = NULL;
unsigned int nlink = archive_entry_nlink(entry);
int ino = archive_entry_ino(entry);
+ const char *hardlink = archive_entry_hardlink(entry);
- // hardlink is regular file, not type AE_IFLNK
- if (archive_entry_filetype(entry) != AE_IFREG) {
- return 0;
+ if (hardlink != NULL && rebase_hardlink(entry, src_base, dst_base) != 0) {
+ return -1;
}
- // no hardlink
+ // try to use hardlink to reduce tar size
if (nlink <= 1) {
return 0;
}
@@ -610,11 +763,12 @@ static void link_kvfree(void *key, void *value)
return;
}
-int tar_handler(struct archive *r, struct archive *w)
+int tar_handler(struct archive *r, struct archive *w, const char *src_base, const char *dst_base)
{
int ret = ARCHIVE_OK;
struct archive_entry *entry = NULL;
map_t *map_link = NULL;
+ char *pathname = NULL;
map_link = map_new(MAP_INT_STR, MAP_DEFAULT_CMP_FUNC, link_kvfree);
if (map_link == NULL) {
@@ -636,11 +790,18 @@ int tar_handler(struct archive *r, struct archive *w)
break;
}
- if (update_entry_for_hardlink(map_link, entry) != 0) {
+ pathname = update_entry_for_pathname(entry, src_base, dst_base);
+ if (pathname == NULL) {
ret = ARCHIVE_FAILED;
break;
}
+ free(pathname);
+ pathname = NULL;
+ if (update_entry_for_hardlink(map_link, entry, src_base, dst_base) != 0) {
+ ret = ARCHIVE_FAILED;
+ break;
+ }
ret = archive_write_header(w, entry);
if (ret != ARCHIVE_OK) {
ERROR("Fail to write tar header: %s", archive_error_string(w));
@@ -680,7 +841,29 @@ int tar_handler(struct archive *r, struct archive *w)
return ret;
}
-static int tar_all(int fd)
+static ssize_t stream_write_data(struct archive *a, void *client_data, const void *buffer, size_t length)
+{
+ struct io_write_wrapper *writer = (struct io_write_wrapper *)client_data;
+ size_t written_length = 0;
+ size_t size = 0;
+ while (length > written_length) {
+ if (length - written_length > ARCHIVE_WRITE_BUFFER_SIZE) {
+ size = ARCHIVE_WRITE_BUFFER_SIZE;
+ } else {
+ size = length - written_length;
+ }
+ if (!writer->write_func(writer->context, (const char *)buffer + written_length, size)) {
+ ERROR("write stream failed");
+ return -1;
+ }
+ written_length += size;
+ }
+
+ return size;
+}
+
+static int tar_all(const struct io_write_wrapper *writer, const char *tar_dir,
+ const char *src_base, const char *dst_base)
{
struct archive *r = NULL;
struct archive *w = NULL;
@@ -689,12 +872,13 @@ static int tar_all(int fd)
r = archive_read_disk_new();
if (r == NULL) {
ERROR("archive read disk new failed");
+ fprintf(stderr, "archive read disk new failed");
return -1;
}
archive_read_disk_set_standard_lookup(r);
archive_read_disk_set_symlink_physical(r);
archive_read_disk_set_behavior(r, ARCHIVE_READDISK_NO_TRAVERSE_MOUNTS);
- ret = archive_read_disk_open(r, ".");
+ ret = archive_read_disk_open(r, tar_dir);
if (ret != ARCHIVE_OK) {
ERROR("open archive read failed: %s", archive_error_string(r));
fprintf(stderr, "open archive read failed: %s\n", archive_error_string(r));
@@ -704,19 +888,20 @@ static int tar_all(int fd)
w = archive_write_new();
if (w == NULL) {
ERROR("archive write new failed");
+ fprintf(stderr, "archive write new failed");
ret = ARCHIVE_FAILED;
goto out;
}
archive_write_set_format_pax(w);
archive_write_set_options(w, "xattrheader=SCHILY");
- ret = archive_write_open_fd(w, fd);
+ ret = archive_write_open(w, (void*)writer, NULL, stream_write_data, NULL);
if (ret != ARCHIVE_OK) {
ERROR("open archive write failed: %s", archive_error_string(w));
fprintf(stderr, "open archive write failed: %s\n", archive_error_string(w));
goto out;
}
- ret = tar_handler(r, w);
+ ret = tar_handler(r, w, src_base, dst_base);
out:
archive_free(r);
@@ -725,8 +910,14 @@ out:
return (ret == ARCHIVE_OK) ? 0 : -1;
}
+static ssize_t fd_write(void *context, const void *data, size_t len)
+{
+ return util_write_nointr(*(int*)context, data, len);
+}
+
int archive_chroot_tar(char *path, char *file, char **errmsg)
{
+ struct io_write_wrapper pipe_context = { 0 };
int ret = 0;
pid_t pid;
int pipe_for_read[2] = { -1, -1 };
@@ -744,8 +935,6 @@ int archive_chroot_tar(char *path, char *file, char **errmsg)
if (pid == (pid_t) -1) {
ERROR("Failed to fork()");
ret = -1;
- close(pipe_for_read[0]);
- close(pipe_for_read[1]);
goto cleanup;
}
@@ -788,7 +977,9 @@ int archive_chroot_tar(char *path, char *file, char **errmsg)
goto child_out;
}
- ret = tar_all(fd);
+ pipe_context.context = (void*)&fd;
+ pipe_context.write_func = fd_write;
+ ret = tar_all(&pipe_context, ".", ".", NULL);
child_out:
@@ -798,6 +989,8 @@ child_out:
exit(EXIT_SUCCESS);
}
}
+ close(pipe_for_read[1]);
+ pipe_for_read[1] = -1;
ret = util_wait_for_pid(pid);
if (ret != 0) {
@@ -806,17 +999,357 @@ child_out:
if (read(pipe_for_read[0], errbuf, BUFSIZ) < 0) {
ERROR("read error message from child failed");
}
- close(pipe_for_read[0]);
- pipe_for_read[0] = -1;
}
- close(pipe_for_read[1]);
- pipe_for_read[1] = -1;
-
cleanup:
+ close_archive_pipes_fd(pipe_for_read, 2);
if (errmsg != NULL && strlen(errbuf) != 0) {
*errmsg = util_strdup_s(errbuf);
}
return ret;
}
+
+static ssize_t pipe_read(void *context, void *buf, size_t len)
+{
+ return util_read_nointr(*(int*)context, buf, len);
+}
+
+static ssize_t archive_context_write(const void *context, const void *buf, size_t len)
+{
+ struct archive_context *ctx = (struct archive_context *)context;
+ if (ctx == NULL) {
+ return -1;
+ }
+ if (ctx->stdin_fd >= 0) {
+ return util_write_nointr(ctx->stdin_fd, buf, len);
+ }
+ return 0;
+}
+
+static ssize_t pipe_write(void *context, const void *data, size_t len)
+{
+ return util_write_nointr(*(int*)context, data, len);
+}
+
+static ssize_t archive_context_read(void *context, void *buf, size_t len)
+{
+ struct archive_context *ctx = (struct archive_context *)context;
+ if (ctx == NULL) {
+ return -1;
+ }
+ if (ctx->stdout_fd >= 0) {
+ return util_read_nointr(ctx->stdout_fd, buf, len);
+ }
+ return 0;
+}
+
+static int close_wait_pid(struct archive_context *ctx, int *status)
+{
+ int ret = 0;
+
+ // close stdin and stdout first, this will make sure the process of tar exit.
+ if (ctx->stdin_fd >= 0) {
+ close(ctx->stdin_fd);
+ }
+
+ if (ctx->stdout_fd >= 0) {
+ close(ctx->stdout_fd);
+ }
+
+ if (ctx->pid > 0) {
+ if (waitpid(ctx->pid, status, 0) != ctx->pid) {
+ ERROR("Failed to wait pid %u", ctx->pid);
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+static int archive_context_close(void *context, char **err)
+{
+ int ret = 0;
+ int status = 0;
+ char *reason = NULL;
+ ssize_t size_read = 0;
+ char buffer[BUFSIZ + 1] = { 0 };
+ struct archive_context *ctx = (struct archive_context *)context;
+ char *marshaled = NULL;
+
+ if (ctx == NULL) {
+ return 0;
+ }
+
+ ret = close_wait_pid(ctx, &status);
+
+ if (WIFSIGNALED((unsigned int)status)) {
+ status = WTERMSIG(status);
+ reason = "signaled";
+ } else if (WIFEXITED(status)) {
+ status = WEXITSTATUS(status);
+ reason = "exited";
+ } else {
+ reason = "unknown";
+ }
+ if (ctx->stderr_fd >= 0) {
+ size_read = util_read_nointr(ctx->stderr_fd, buffer, BUFSIZ);
+ if (size_read > 0) {
+ reason = buffer;
+ marshaled = util_marshal_string(buffer);
+ if (marshaled == NULL) {
+ ERROR("Can not marshal json buffer: %s", buffer);
+ } else {
+ reason = marshaled;
+ }
+ }
+ close(ctx->stderr_fd);
+ }
+
+ if (size_read > 0 || status != 0) {
+ format_errorf(err, "tar exited with status %d: %s", status, reason);
+ ret = -1;
+ }
+
+ free(marshaled);
+ free(ctx);
+ return ret;
+}
+
+int archive_chroot_untar_stream(const struct io_read_wrapper *context, const char *chroot_dir,
+ const char *untar_dir, const char *src_base, const char *dst_base,
+ char **errmsg)
+{
+ struct io_read_wrapper pipe_context = { 0 };
+ int pipe_stream[2] = { -1, -1 };
+ int pipe_stderr[2] = { -1, -1 };
+ int keepfds[] = { -1, -1, -1 };
+ int ret = -1;
+ int cret = 0;
+ pid_t pid;
+ struct archive_context *ctx = NULL;
+ char *buf = NULL;
+ size_t buf_len = ARCHIVE_BLOCK_SIZE;
+ ssize_t read_len;
+ struct archive_options options = {
+ .whiteout_format = NONE_WHITEOUT_FORMATE,
+ .src_base = src_base,
+ .dst_base = dst_base
+ };
+
+ buf = util_common_calloc_s(buf_len);
+ if (buf == NULL) {
+ ERROR("Out of memory");
+ return -1;
+ }
+
+ if (pipe(pipe_stderr) != 0) {
+ ERROR("Failed to create pipe: %s", strerror(errno));
+ goto cleanup;
+ }
+ if (pipe(pipe_stream) != 0) {
+ ERROR("Failed to create pipe: %s", strerror(errno));
+ goto cleanup;
+ }
+
+ pid = fork();
+ if (pid == (pid_t) -1) {
+ ERROR("Failed to fork: %s", strerror(errno));
+ goto cleanup;
+ }
+
+ if (pid == (pid_t)0) {
+ keepfds[0] = isula_libutils_get_log_fd();
+ keepfds[1] = pipe_stderr[1];
+ keepfds[2] = pipe_stream[0];
+ ret = util_check_inherited_exclude_fds(true, keepfds, 3);
+ if (ret != 0) {
+ ERROR("Failed to close fds.");
+ ret = -1;
+ goto child_out;
+ }
+
+ // child process, dup2 pipe_stderr[1] to stderr,
+ if (dup2(pipe_stderr[1], 2) < 0) {
+ ERROR("Dup fd error: %s", strerror(errno));
+ ret = -1;
+ goto child_out;
+ }
+
+ if (chroot(chroot_dir) != 0) {
+ SYSERROR("Failed to chroot to %s", chroot_dir);
+ ret = -1;
+ goto child_out;
+ }
+
+ if (chdir("/") != 0 || chdir(untar_dir) != 0) {
+ SYSERROR("Failed to chdir to %s", untar_dir);
+ fprintf(stderr, "Failed to chdir to %s", untar_dir);
+ ret = -1;
+ goto child_out;
+ }
+
+ pipe_context.context = (void*)&pipe_stream[0];
+ pipe_context.read = pipe_read;
+ ret = archive_unpack_handler(&pipe_context, &options);
+
+child_out:
+ if (ret != 0) {
+ exit(EXIT_FAILURE);
+ } else {
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ close(pipe_stderr[1]);
+ pipe_stderr[1] = -1;
+ close(pipe_stream[0]);
+ pipe_stream[0] = -1;
+
+ ctx = util_common_calloc_s(sizeof(struct archive_context));
+ if (ctx == NULL) {
+ goto cleanup;
+ }
+
+ ctx->pid = pid;
+ ctx->stdin_fd = pipe_stream[1];
+ pipe_stream[1] = -1;
+ ctx->stdout_fd = -1;
+ ctx->stderr_fd = pipe_stderr[0];
+ pipe_stderr[0] = -1;
+
+ read_len = context->read(context->context, buf, buf_len);
+ while (read_len > 0) {
+ ssize_t writed_len = archive_context_write(ctx, buf, (size_t)read_len);
+ if (writed_len < 0) {
+ DEBUG("Tar may exited: %s", strerror(errno));
+ break;
+ }
+ read_len = context->read(context->context, buf, buf_len);
+ }
+
+ ret = 0;
+
+cleanup:
+ free(buf);
+ cret = archive_context_close(ctx, errmsg);
+ ret = (cret != 0) ? cret : ret;
+ close_archive_pipes_fd(pipe_stderr, 2);
+ close_archive_pipes_fd(pipe_stream, 2);
+
+ return ret;
+}
+
+int archive_chroot_tar_stream(const char *chroot_dir, const char *tar_path, const char *src_base,
+ const char *dst_base, struct io_read_wrapper *reader)
+{
+ struct io_write_wrapper pipe_context = { 0 };
+ int keepfds[] = { -1, -1, -1 };
+ int pipe_stderr[2] = { -1, -1 };
+ int pipe_stream[2] = { -1, -1 };
+ int ret = -1;
+ pid_t pid;
+ struct archive_context *ctx = NULL;
+
+ if (pipe(pipe_stderr) != 0) {
+ ERROR("Failed to create pipe: %s", strerror(errno));
+ goto free_out;
+ }
+ if (pipe(pipe_stream) != 0) {
+ ERROR("Failed to create pipe: %s", strerror(errno));
+ goto free_out;
+ }
+
+ pid = fork();
+ if (pid == (pid_t) - 1) {
+ ERROR("Failed to fork: %s", strerror(errno));
+ goto free_out;
+ }
+
+ if (pid == (pid_t)0) {
+ char *tar_dir_name = NULL;
+ char *tar_base_name = NULL;
+
+ keepfds[0] = isula_libutils_get_log_fd();
+ keepfds[1] = pipe_stderr[1];
+ keepfds[2] = pipe_stream[1];
+ ret = util_check_inherited_exclude_fds(true, keepfds, 3);
+ if (ret != 0) {
+ ERROR("Failed to close fds.");
+ ret = -1;
+ goto child_out;
+ }
+
+ // child process, dup2 pipe_stderr[1] to stderr,
+ if (dup2(pipe_stderr[1], 2) < 0) {
+ ERROR("Dup fd error: %s", strerror(errno));
+ ret = -1;
+ goto child_out;
+ }
+
+ if (chroot(chroot_dir) != 0) {
+ ERROR("Failed to chroot to %s", chroot_dir);
+ fprintf(stderr, "Failed to chroot to %s\n", chroot_dir);
+ ret = -1;
+ goto child_out;
+ }
+
+ if (util_split_dir_and_base_name(tar_path, &tar_dir_name, &tar_base_name) != 0) {
+ ERROR("Failed to split %s", tar_path);
+ fprintf(stderr, "Failed to split %s\n", tar_path);
+ ret = -1;
+ goto child_out;
+ }
+
+ if (chdir("/") != 0 || chdir(tar_dir_name) != 0) {
+ ERROR("Failed to chdir to %s", tar_dir_name);
+ fprintf(stderr, "Failed to chdir to %s\n", tar_dir_name);
+ ret = -1;
+ goto child_out;
+ }
+
+ pipe_context.context = (void*)&pipe_stream[1];
+ pipe_context.write_func = pipe_write;
+ ret = tar_all(&pipe_context, tar_base_name, src_base, dst_base);
+
+child_out:
+ free(tar_dir_name);
+ free(tar_base_name);
+
+ if (ret != 0) {
+ exit(EXIT_FAILURE);
+ } else {
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ close(pipe_stderr[1]);
+ pipe_stderr[1] = -1;
+ close(pipe_stream[1]);
+ pipe_stream[1] = -1;
+
+ ctx = util_common_calloc_s(sizeof(struct archive_context));
+ if (ctx == NULL) {
+ goto free_out;
+ }
+
+ ctx->stdin_fd = -1;
+ ctx->stdout_fd = pipe_stream[0];
+ pipe_stream[0] = -1;
+ ctx->stderr_fd = pipe_stderr[0];
+ pipe_stderr[0] = -1;
+ ctx->pid = pid;
+
+ reader->close = archive_context_close;
+ reader->context = ctx;
+ ctx = NULL;
+ reader->read = archive_context_read;
+
+ ret = 0;
+free_out:
+ close_archive_pipes_fd(pipe_stderr, 2);
+ close_archive_pipes_fd(pipe_stream, 2);
+ free(ctx);
+
+ return ret;
+}
diff --git a/src/utils/tar/util_archive.h b/src/utils/tar/util_archive.h
index 0e05a363..55fd7683 100644
--- a/src/utils/tar/util_archive.h
+++ b/src/utils/tar/util_archive.h
@@ -24,6 +24,8 @@
#include "io_wrapper.h"
+#define ARCHIVE_BLOCK_SIZE (32 * 1024)
+
struct io_read_wrapper;
#ifdef __cplusplus
@@ -38,14 +40,25 @@ typedef enum {
struct archive_options {
whiteout_format_type whiteout_format;
+
+ // rename archive entry's name from src_base to dst_base
+ const char *src_base;
+ const char *dst_base;
};
-int archive_unpack(const struct io_read_wrapper *content, const char *dstdir, const struct archive_options *options);
+int archive_unpack(const struct io_read_wrapper *content, const char *dstdir, const struct archive_options *options,
+ char **errmsg);
bool valid_archive_format(const char *file);
int archive_chroot_tar(char *path, char *file, char **errmsg);
+int archive_chroot_tar_stream(const char *chroot_dir, const char *tar_path, const char *src_base,
+ const char *dst_base, struct io_read_wrapper *content);
+int archive_chroot_untar_stream(const struct io_read_wrapper *content, const char *chroot_dir,
+ const char *untar_dir, const char *src_base, const char *dst_base,
+ char **errmsg);
+
#ifdef __cplusplus
}
#endif
--
2.25.1