diff --git a/cmake/checker.cmake b/cmake/checker.cmake index 56683c7ab5f074d8daad88f3473000acfa122a2e..4aaec1b3cf7fa92c20da4e1286d40dfec6f21aab 100644 --- a/cmake/checker.cmake +++ b/cmake/checker.cmake @@ -65,14 +65,6 @@ find_library(LIBARCHIVE_LIBRARY archive HINTS ${PC_LIBARCHIVE_LIBDIR} ${PC_LIBARCHIVE_LIBRARY_DIRS}) _CHECK(LIBARCHIVE_LIBRARY "LIBARCHIVE_LIBRARY-NOTFOUND" "libarchive.so") -# check libtar -find_path(LIBTAR_INCLUDE_DIR libtar.h - HINTS ${PC_LIBTAR_INCLUDEDIR} ${PC_LIBTAR_INCLUDE_DIRS}) -_CHECK(LIBTAR_INCLUDE_DIR "LIBTAR_INCLUDE_DIR-NOTFOUND" "libtar.h") -find_library(LIBTAR_LIBRARY tar - HINTS ${PC_LIBTAR_LIBDIR} ${PC_LIBTAR_LIBRARY_DIRS}) -_CHECK(LIBTAR_LIBRARY "LIBTAR_LIBRARY-NOTFOUND" "libtar.so") - # check libcrypto pkg_check_modules(PC_CRYPTO REQUIRED "libcrypto") find_library(CRYPTO_LIBRARY crypto diff --git a/iSulad.spec b/iSulad.spec index 06a05721f48a21837ff12ad4ca0d5aa59bcebcb1..282b0c5a7df34c0909d06483761bd385027e96bd 100644 --- a/iSulad.spec +++ b/iSulad.spec @@ -31,7 +31,7 @@ Requires(preun): initscripts BuildRequires: cmake gcc-c++ lxc lxc-devel lcr-devel yajl-devel clibcni-devel BuildRequires: grpc grpc-plugins grpc-devel protobuf-devel -BuildRequires: libcurl libcurl-devel sqlite-devel libarchive-devel libtar-devel device-mapper-devel +BuildRequires: libcurl libcurl-devel sqlite-devel libarchive-devel device-mapper-devel BuildRequires: http-parser-devel BuildRequires: libseccomp-devel libcap-devel libselinux-devel libwebsockets libwebsockets-devel BuildRequires: systemd-devel git @@ -40,7 +40,7 @@ Requires: lcr lxc clibcni Requires: grpc protobuf Requires: libcurl Requires: sqlite http-parser libseccomp -Requires: libcap libselinux libwebsockets libarchive libtar device-mapper +Requires: libcap libselinux libwebsockets libarchive device-mapper Requires: systemd %description diff --git a/src/daemon/modules/image/oci/oci_export.c b/src/daemon/modules/image/oci/oci_export.c index 003d71b34a767148eb2e2bb1a2ddc65a0b13cf62..ca4b8276b00d7e474183584909de4742a45f1354 100644 --- a/src/daemon/modules/image/oci/oci_export.c +++ b/src/daemon/modules/image/oci/oci_export.c @@ -19,7 +19,7 @@ #include "oci_export.h" #include "isula_libutils/log.h" #include "err_msg.h" -#include "isulad_tar.h" +#include "util_archive.h" int oci_do_export(char *id, char *file) { @@ -40,7 +40,7 @@ int oci_do_export(char *id, char *file) return -1; } - ret = chroot_tar(mount_point, file, &errmsg); + ret = archive_chroot_tar(mount_point, file, &errmsg); if (ret != 0) { ERROR("failed to export container %s to file %s: %s", id, file, errmsg); isulad_set_error_message("Failed to export rootfs with error: %s", errmsg); diff --git a/src/utils/tar/isulad_tar.c b/src/utils/tar/isulad_tar.c index 6baf637879384af52ceafbd03c19a993f5c50730..7d0088e6699b6b631431b84462fe06a14a1cdafb 100644 --- a/src/utils/tar/isulad_tar.c +++ b/src/utils/tar/isulad_tar.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include "stdbool.h" @@ -43,8 +42,6 @@ #define TAR_EXACT_OPT "-x" #define TAR_CHDIR_OPT "-C" #define TAR_GZIP_OPT "-z" -#define TAR_DEFAULT_MODE 0600 -#define TAR_DEFAULT_FLAG (O_WRONLY | O_CREAT) static void set_char_to_separator(char *p) { @@ -892,125 +889,3 @@ int tar_resource(const struct archive_copy_info *info, struct io_read_wrapper *a { return tar_resource_rebase(info->path, info->rebase_name, archive_reader, err); } - -static int tar_all(char *path, int fd) -{ - TAR *tar = NULL; - int ret = 0; - - ret = tar_fdopen(&tar, fd, NULL, NULL, TAR_DEFAULT_FLAG, TAR_DEFAULT_MODE, TAR_GNU); - if (ret != 0) { - ERROR("open file for exporting container rootfs failed: %s", strerror(errno)); - fprintf(stderr, "open file for exporting container rootfs failed: %s", strerror(errno)); - return -1; - } - - ret = tar_append_tree(tar, path, "."); - if (ret != 0) { - ERROR("append files tree for exporting container rootfs failed: %s", strerror(errno)); - fprintf(stderr, "append files tree for exporting container rootfs failed: %s", strerror(errno)); - goto out; - } - -out: - - tar_close(tar); - tar = NULL; - - return ret; -} - -int chroot_tar(char *path, char *file, char **errmsg) -{ - int ret = 0; - pid_t pid; - int pipe_for_read[2] = { -1, -1 }; - int keepfds[] = { -1, -1 }; - char errbuf[BUFSIZ] = {0}; - int fd = 0; - - if (pipe2(pipe_for_read, O_CLOEXEC) != 0) { - ERROR("Failed to create pipe"); - ret = -1; - goto cleanup; - } - - pid = fork(); - if (pid == (pid_t) -1) { - ERROR("Failed to fork()"); - ret = -1; - close(pipe_for_read[0]); - close(pipe_for_read[1]); - goto cleanup; - } - - if (pid == (pid_t)0) { - keepfds[0] = isula_libutils_get_log_fd(); - keepfds[1] = pipe_for_read[1]; - ret = util_check_inherited_exclude_fds(true, keepfds, 2); - if (ret != 0) { - ERROR("Failed to close fds."); - ret = -1; - goto child_out; - } - - // child process, dup2 pipe_for_read[1] to stderr, - if (dup2(pipe_for_read[1], 2) < 0) { - ERROR("Dup fd error: %s", strerror(errno)); - ret = -1; - goto child_out; - } - - fd = open(file, TAR_DEFAULT_FLAG, TAR_DEFAULT_MODE); - if (fd < 0) { - ERROR("Failed to open file %s for export: %s", file, strerror(errno)); - fprintf(stderr, "Failed to open file %s for export: %s", file, strerror(errno)); - ret = -1; - goto child_out; - } - - if (chroot(path) != 0) { - ERROR("Failed to chroot to %s", path); - fprintf(stderr, "Failed to chroot to %s", path); - ret = -1; - goto child_out; - } - - if (chdir("/") != 0) { - ERROR("Failed to chroot to /"); - fprintf(stderr, "Failed to chroot to /"); - ret = -1; - goto child_out; - } - - ret = tar_all("/", fd); - -child_out: - - if (ret != 0) { - exit(EXIT_FAILURE); - } else { - exit(EXIT_SUCCESS); - } - } - - ret = wait_for_pid(pid); - if (ret != 0) { - ERROR("tar failed"); - 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: - if (errmsg != NULL && strlen(errbuf) != 0) { - *errmsg = util_strdup_s(errbuf); - } - - return ret; -} diff --git a/src/utils/tar/isulad_tar.h b/src/utils/tar/isulad_tar.h index de14f158dec653edb6b9649dca4df059a852ccbe..e2b78463c89afcaf66b616613e13d0120d8bdaaa 100644 --- a/src/utils/tar/isulad_tar.h +++ b/src/utils/tar/isulad_tar.h @@ -70,8 +70,6 @@ int archive_copy_to(const struct io_read_wrapper *content, bool compression, con int archive_path(const char *srcdir, const char *srcbase, const char *rebase_name, bool compression, struct io_read_wrapper *archive_reader); -int chroot_tar(char *path, char *file, char **errmsg); - #ifdef __cplusplus } #endif diff --git a/src/utils/tar/util_archive.c b/src/utils/tar/util_archive.c index 8887577a5c8f434d7ddaed92d5b7ddf7d26206e7..8d398f8146577df23c2948eb7945618bcb9779e5 100644 --- a/src/utils/tar/util_archive.c +++ b/src/utils/tar/util_archive.c @@ -32,11 +32,14 @@ #include "isula_libutils/log.h" #include "io_wrapper.h" #include "utils_file.h" +#include "map.h" struct archive; struct archive_entry; #define ARCHIVE_READ_BUFFER_SIZE (10 * 1024) +#define TAR_DEFAULT_MODE 0600 +#define TAR_DEFAULT_FLAG (O_WRONLY | O_CREAT | O_TRUNC) #define WHITEOUT_PREFIX ".wh." #define WHITEOUT_META_PREFIX ".wh..wh." @@ -352,3 +355,272 @@ out: return (ret == ARCHIVE_OK); } + +static int copy_data_between_archives(struct archive *ar, struct archive *aw) +{ + int ret = ARCHIVE_FAILED; + const void *buff = NULL; + size_t size; + int64_t offset; + + for (;;) { + ret = archive_read_data_block(ar, &buff, &size, &offset); + if (ret == ARCHIVE_EOF) { + return ARCHIVE_OK; + } + if (ret < ARCHIVE_OK) { + ERROR("tar archive read result %d, error: %s", ret, archive_error_string(ar)); + fprintf(stderr, "tar archive read result %d, error: %s", ret, archive_error_string(ar)); + return ret; + } + ret = archive_write_data(aw, buff, size); + if (ret < ARCHIVE_OK) { + ERROR("tar archive write result %d, error: %s", ret, archive_error_string(aw)); + fprintf(stderr, "tar archive write result %d, error: %s", ret, archive_error_string(aw)); + return ret; + } + } +} + +int update_entry_for_hardlink(map_t *map_link, struct archive_entry *entry) +{ + const char *path = archive_entry_pathname(entry); + char *linkname = NULL; + unsigned int nlink = archive_entry_nlink(entry); + int ino = archive_entry_ino(entry); + + // hardlink is regular file, not type AE_IFLNK + if (archive_entry_filetype(entry) != AE_IFREG) { + return 0; + } + + // no hardlink + if (nlink <= 1) { + return 0; + } + + linkname = map_search(map_link, (void*)&ino); + if (linkname == NULL) { + linkname = (char*)path; + if (!map_insert(map_link, (void*)&ino, linkname)) { + ERROR("insert to map failed"); + fprintf(stderr, "insert to map failed"); + return -1; + } + return 0; + } + + archive_entry_set_size(entry, 0); + archive_entry_set_hardlink(entry, linkname); + + return 0; +} + +static void link_kvfree(void *key, void *value) +{ + free(key); + free(value); + return; +} + +int tar_handler(struct archive *r, struct archive *w) +{ + int ret = ARCHIVE_OK; + struct archive_entry *entry = NULL; + map_t *map_link = NULL; + + map_link = map_new(MAP_INT_STR, MAP_DEFAULT_CMP_FUNC, link_kvfree); + if (map_link == NULL) { + ERROR("out of memory"); + fprintf(stderr, "out of memory\n"); + return -1; + } + + for (;;) { + ret = archive_read_next_header(r, &entry); + if (ret == ARCHIVE_EOF) { + ret = ARCHIVE_OK; + break; + } + + if (ret != ARCHIVE_OK) { + ERROR("read from disk failed: %s", archive_error_string(r)); + fprintf(stderr, "read from disk failed: %s\n", archive_error_string(r)); + break; + } + + if (update_entry_for_hardlink(map_link, entry) != 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)); + fprintf(stderr, "Fail to write tar header: %s\nlink:%s target:%s", + archive_error_string(w), archive_entry_pathname(entry), + archive_entry_hardlink(entry)); + break; + } + + if (archive_entry_size(entry) > 0) { + ret = copy_data_between_archives(r, w); + if (ret != ARCHIVE_OK) { + ERROR("Failed to do copy data: %s", archive_error_string(w)); + fprintf(stderr, "Failed to do copy data: %s\n", archive_error_string(w)); + break; + } + } + + ret = archive_write_finish_entry(w); + if (ret != ARCHIVE_OK) { + ERROR("Failed to freeing archive entry: %s\n", archive_error_string(w)); + fprintf(stderr, "Failed to freeing archive entry: %s\n", archive_error_string(w)); + break; + } + + if (archive_entry_filetype(entry) == AE_IFDIR) { + ret = archive_read_disk_descend(r); + if (ret != ARCHIVE_OK) { + ERROR("read disk descend failed: %s\n", archive_error_string(w)); + fprintf(stderr, "read disk descend failed: %s\n", archive_error_string(w)); + break; + } + } + } + + map_free(map_link); + + return ret; +} + +static int tar_all(int fd) +{ + struct archive *r = NULL; + struct archive *w = NULL; + int ret = ARCHIVE_OK; + + r = archive_read_disk_new(); + 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, "."); + 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)); + goto out; + } + + w = archive_write_new(); + archive_write_set_format_pax(w); + ret = archive_write_open_fd(w, fd); + 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); + +out: + archive_free(r); + archive_free(w); + + return (ret == ARCHIVE_OK) ? 0 : -1; +} + +int archive_chroot_tar(char *path, char *file, char **errmsg) +{ + int ret = 0; + pid_t pid; + int pipe_for_read[2] = { -1, -1 }; + int keepfds[] = { -1, -1 }; + char errbuf[BUFSIZ] = {0}; + int fd = 0; + + if (pipe2(pipe_for_read, O_CLOEXEC) != 0) { + ERROR("Failed to create pipe"); + ret = -1; + goto cleanup; + } + + pid = fork(); + if (pid == (pid_t) -1) { + ERROR("Failed to fork()"); + ret = -1; + close(pipe_for_read[0]); + close(pipe_for_read[1]); + goto cleanup; + } + + if (pid == (pid_t)0) { + keepfds[0] = isula_libutils_get_log_fd(); + keepfds[1] = pipe_for_read[1]; + ret = util_check_inherited_exclude_fds(true, keepfds, 2); + if (ret != 0) { + ERROR("Failed to close fds."); + ret = -1; + goto child_out; + } + + // child process, dup2 pipe_for_read[1] to stderr, + if (dup2(pipe_for_read[1], 2) < 0) { + ERROR("Dup fd error: %s", strerror(errno)); + ret = -1; + goto child_out; + } + + fd = open(file, TAR_DEFAULT_FLAG, TAR_DEFAULT_MODE); + if (fd < 0) { + ERROR("Failed to open file %s for export: %s", file, strerror(errno)); + fprintf(stderr, "Failed to open file %s for export: %s\n", file, strerror(errno)); + ret = -1; + goto child_out; + } + + if (chroot(path) != 0) { + ERROR("Failed to chroot to %s", path); + fprintf(stderr, "Failed to chroot to %s\n", path); + ret = -1; + goto child_out; + } + + if (chdir("/") != 0) { + ERROR("Failed to chroot to /"); + fprintf(stderr, "Failed to chroot to /\n"); + ret = -1; + goto child_out; + } + + ret = tar_all(fd); + +child_out: + + if (ret != 0) { + exit(EXIT_FAILURE); + } else { + exit(EXIT_SUCCESS); + } + } + + ret = wait_for_pid(pid); + if (ret != 0) { + ERROR("tar failed"); + fcntl(pipe_for_read[0], F_SETFL, O_NONBLOCK); + 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: + if (errmsg != NULL && strlen(errbuf) != 0) { + *errmsg = util_strdup_s(errbuf); + } + + return ret; +} diff --git a/src/utils/tar/util_archive.h b/src/utils/tar/util_archive.h index 0bfa385292d2e0291b1dfb7364ea9fe798738cef..4c4e4a1e3deeed7c51ea291b29da1bc6ecb509ed 100644 --- a/src/utils/tar/util_archive.h +++ b/src/utils/tar/util_archive.h @@ -42,6 +42,8 @@ int archive_unpack(const struct io_read_wrapper *content, const char *dstdir, co bool valid_archive_format(const char *file); +int archive_chroot_tar(char *path, char *file, char **errmsg); + #ifdef __cplusplus } #endif