From: Egor Chelak Date: Fri, 6 Nov 2020 18:33:46 +0000 (+0200) Subject: ul_copy_file: handle EAGAIN and EINTR X-Git-Tag: v2.37-rc1~393^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=212bde6cf7160422844daba5276c8a0d1412a937;p=thirdparty%2Futil-linux.git ul_copy_file: handle EAGAIN and EINTR I did this by implementing a function called sendfile_all() similar to read_all()/write_all(). The manpage for sendfile doesn't mention EINTR, but I decided to check it anyway, just in case. Suggested-by: Karel Zak Reviewed-by: Sami Kerola Signed-off-by: Egor Chelak --- diff --git a/include/all-io.h b/include/all-io.h index 8ffa9cfb11..98745802c9 100644 --- a/include/all-io.h +++ b/include/all-io.h @@ -12,6 +12,10 @@ #include #include #include +#include +#ifdef HAVE_SENDFILE +#include +#endif #include "c.h" @@ -78,4 +82,32 @@ static inline ssize_t read_all(int fd, char *buf, size_t count) return c; } +static inline ssize_t sendfile_all(int out, int in, off_t *off, size_t count) +{ +#ifdef HAVE_SENDFILE + ssize_t ret; + ssize_t c = 0; + int tries = 0; + while (count) { + ret = sendfile(out, in, off, count); + if (ret < 0) { + if ((errno == EAGAIN || errno == EINTR) && (tries++ < 5)) { + xusleep(250000); + continue; + } + return c ? c : -1; + } + if (ret == 0) + return c; + tries = 0; + count -= ret; + c += ret; + } + return c; +#else + errno = ENOSYS; + return -1; +#endif +} + #endif /* UTIL_LINUX_ALL_IO_H */ diff --git a/lib/fileutils.c b/lib/fileutils.c index 61304c77ea..a9c0e61c0f 100644 --- a/lib/fileutils.c +++ b/lib/fileutils.c @@ -11,9 +11,6 @@ #include #include #include -#ifdef HAVE_SENDFILE -#include -#endif #include #include "c.h" @@ -275,21 +272,15 @@ int ul_copy_file(int from, int to) #ifdef HAVE_SENDFILE struct stat st; ssize_t nw; - off_t left; if (fstat(from, &st) == -1) return -1; if (!S_ISREG(st.st_mode)) return copy_file_simple(from, to); - for (left = st.st_size; left != 0; left -= nw) { - if ((nw = sendfile(to, from, NULL, left)) < 0) - return copy_file_simple(from, to); - if (!nw) - return 0; - } - /* For extra robustness, treat st_size as advisory and ensure that we - * actually get EOF. */ - while ((nw = sendfile(to, from, NULL, 1024*1024)) != 0) + if (sendfile_all(to, from, NULL, st.st_size) < 0) + return copy_file_simple(from, to); + /* ensure we either get an EOF or an error */ + while ((nw = sendfile_all(to, from, NULL, 16*1024*1024)) != 0) if (nw < 0) return copy_file_simple(from, to); return 0;