提交 a6c4adb0 编写于 作者: W WangFengTu

use libarchive to tar container's rootfs

currently we use libtar to tar container's rootfs.
libtar fill non-zero to tar header's size of hardlink,
it's old manner. Modern tar writers should write zero
to tar header's size of hardlink. Docker cann't properly
deal with old manner.
Signed-off-by: NWangFengTu <wangfengtu@huawei.com>
上级 17ebce57
......@@ -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
......
......@@ -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
......
......@@ -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);
......
......@@ -22,7 +22,6 @@
#include <sys/wait.h>
#include <limits.h>
#include <sys/stat.h>
#include <libtar.h>
#include <errno.h>
#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;
}
......@@ -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
......
......@@ -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;
}
......@@ -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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册