diff --git a/src/lxc/conf.c b/src/lxc/conf.c index 710625bb0..e822e90d3 100644 --- a/src/lxc/conf.c +++ b/src/lxc/conf.c @@ -3344,9 +3344,8 @@ void remount_all_slave(void) } } -#define __LXC_SENDFILE_MAX 0x7ffff000 /* maximum number of bytes sendfile can handle */ again: - copied = sendfile(memfd, mntinfo_fd, NULL, __LXC_SENDFILE_MAX); + copied = lxc_sendfile_nointr(memfd, mntinfo_fd, NULL, LXC_SENDFILE_MAX); if (copied < 0) { if (errno == EINTR) goto again; diff --git a/src/lxc/file_utils.c b/src/lxc/file_utils.c index 1655e0053..485a7843e 100644 --- a/src/lxc/file_utils.c +++ b/src/lxc/file_utils.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "config.h" @@ -313,3 +314,19 @@ FILE *fopen_cloexec(const char *path, const char *mode) errno = saved_errno; return ret; } + +ssize_t lxc_sendfile_nointr(int out_fd, int in_fd, off_t *offset, size_t count) +{ + ssize_t ret; + +again: + ret = sendfile(out_fd, in_fd, offset, count); + if (ret < 0) { + if (errno == EINTR) + goto again; + + return -1; + } + + return ret; +} diff --git a/src/lxc/file_utils.h b/src/lxc/file_utils.h index 92e29f4e9..6361557a0 100644 --- a/src/lxc/file_utils.h +++ b/src/lxc/file_utils.h @@ -53,5 +53,7 @@ extern bool has_fs_type(const char *path, fs_type_magic magic_val); extern bool fhas_fs_type(int fd, fs_type_magic magic_val); extern bool is_fs_type(const struct statfs *fs, fs_type_magic magic_val); extern FILE *fopen_cloexec(const char *path, const char *mode); +extern ssize_t lxc_sendfile_nointr(int out_fd, int in_fd, off_t *offset, + size_t count); #endif /* __LXC_FILE_UTILS_H */ diff --git a/src/lxc/macro.h b/src/lxc/macro.h index 4ce613c52..30aa2a5a5 100644 --- a/src/lxc/macro.h +++ b/src/lxc/macro.h @@ -389,4 +389,7 @@ enum { #define STRLITERALLEN(x) (sizeof(""x"") - 1) #define STRARRAYLEN(x) (sizeof(x) - 1) +/* Maximum number of bytes sendfile() is able to send in one go. */ +#define LXC_SENDFILE_MAX 0x7ffff000 + #endif /* __LXC_MACRO_H */ diff --git a/src/lxc/parse.c b/src/lxc/parse.c index 2fdb18ec8..ab9aab461 100644 --- a/src/lxc/parse.c +++ b/src/lxc/parse.c @@ -30,10 +30,14 @@ #include #include #include +#include #include "config.h" +#include "file_utils.h" #include "log.h" +#include "macro.h" #include "parse.h" +#include "syscall_wrappers.h" #include "utils.h" lxc_log_define(parse, lxc); @@ -67,35 +71,67 @@ int lxc_strmunmap(void *addr, size_t length) int lxc_file_for_each_line_mmap(const char *file, lxc_file_cb callback, void *data) { - int fd, saved_errno; - char *buf, *line; - struct stat st; - int ret = 0; + int saved_errno; + ssize_t ret, bytes_sent; + char *line; + int fd = -1, memfd = -1; + char *buf = NULL; + + memfd = memfd_create(".lxc_config_file", MFD_CLOEXEC); + if (memfd < 0) { + char template[] = P_tmpdir "/.lxc_config_file_XXXXXX"; + + if (errno != ENOSYS) { + SYSERROR("Failed to create memory file"); + goto on_error; + } + + TRACE("Failed to create in-memory file. Falling back to " + "temporary file"); + memfd = lxc_make_tmpfile(template, true); + if (memfd < 0) { + SYSERROR("Failed to create temporary file \"%s\"", template); + goto on_error; + } + } fd = open(file, O_RDONLY | O_CLOEXEC); if (fd < 0) { - SYSERROR("Failed to open config file \"%s\"", file); + SYSERROR("Failed to open file \"%s\"", file); return -1; } - ret = fstat(fd, &st); - if (ret < 0) { - SYSERROR("Failed to stat config file \"%s\"", file); - goto on_error_fstat; - } - ret = 0; - if (st.st_size == 0) - goto on_error_fstat; - - ret = -1; - buf = lxc_strmmap(NULL, st.st_size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_POPULATE, fd, 0); - if (buf == MAP_FAILED) { - SYSERROR("Failed to map config file \"%s\"", file); + /* sendfile() handles up to 2GB. No config file should be that big. */ + bytes_sent = lxc_sendfile_nointr(memfd, fd, NULL, LXC_SENDFILE_MAX); + if (bytes_sent < 0) { + SYSERROR("Failed to sendfile \"%s\"", file); goto on_error; } + ret = lxc_write_nointr(memfd, "\0", 1); + if (ret < 0) { + SYSERROR("Failed to append zero byte"); + goto on_error; + } + bytes_sent++; + + ret = lseek(memfd, 0, SEEK_SET); + if (ret < 0) { + SYSERROR("Failed to lseek"); + goto on_error; + } + + ret = -1; + buf = mmap(NULL, bytes_sent, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_POPULATE, memfd, 0); + if (buf == MAP_FAILED) { + buf = NULL; + SYSERROR("Failed to mmap"); + goto on_error; + } + + ret = 0; lxc_iterate_parts(line, buf, "\n\0") { ret = callback(line, data); if (ret) { @@ -104,22 +140,22 @@ int lxc_file_for_each_line_mmap(const char *file, lxc_file_cb callback, void *da */ if (ret < 0) ERROR("Failed to parse config file \"%s\" at " - "line \"%s\"", - file, line); + "line \"%s\"", file, line); break; } } on_error: - if (lxc_strmunmap(buf, st.st_size) < 0) { - SYSERROR("Failed to unmap config file \"%s\"", file); + saved_errno = errno; + if (fd >= 0) + close(fd); + if (memfd >= 0) + close(memfd); + if (buf && munmap(buf, bytes_sent)) { + SYSERROR("Failed to unmap"); if (ret == 0) ret = -1; } - -on_error_fstat: - saved_errno = errno; - close(fd); errno = saved_errno; return ret;