From 2d4a37b427167907ef2402586a8e8e2931a22490 Mon Sep 17 00:00:00 2001 From: Aleksa Sarai Date: Fri, 1 Mar 2019 18:23:54 +1100 Subject: [PATCH] nsenter: cloned_binary: userspace copy fallback if sendfile fails There are some circumstances where sendfile(2) can fail (one example is that AppArmor appears to block writing to deleted files with sendfile(2) under some circumstances) and so we need to have a userspace fallback. It's fairly trivial (and handles short-writes). Signed-off-by: Aleksa Sarai --- libcontainer/nsenter/cloned_binary.c | 37 +++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/libcontainer/nsenter/cloned_binary.c b/libcontainer/nsenter/cloned_binary.c index 1381324b..b410e295 100644 --- a/libcontainer/nsenter/cloned_binary.c +++ b/libcontainer/nsenter/cloned_binary.c @@ -160,7 +160,7 @@ static char *read_file(char *path, size_t *length) *length = 0; for (;;) { - int n; + ssize_t n; n = read(fd, buf, sizeof(buf)); if (n < 0) @@ -403,6 +403,33 @@ out: return ret; } +static ssize_t fd_to_fd(int outfd, int infd) +{ + ssize_t total = 0; + char buffer[4096]; + + for (;;) { + ssize_t nread, nwritten = 0; + + nread = read(infd, buffer, sizeof(buffer)); + if (nread < 0) + return -1; + if (!nread) + break; + + do { + ssize_t n = write(outfd, buffer + nwritten, nread - nwritten); + if (n < 0) + return -1; + nwritten += n; + } while(nwritten < nread); + + total += nwritten; + } + + return total; +} + static int clone_binary(void) { int binfd, execfd; @@ -435,8 +462,12 @@ static int clone_binary(void) while (sent < statbuf.st_size) { int n = sendfile(execfd, binfd, NULL, statbuf.st_size - sent); - if (n < 0) - goto error_binfd; + if (n < 0) { + /* sendfile can fail so we fallback to a dumb user-space copy. */ + n = fd_to_fd(execfd, binfd); + if (n < 0) + goto error_binfd; + } sent += n; } close(binfd);