From 043760719a015f6379a27745c4b81b364469d88d Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 18 Jul 2018 13:32:49 -0700 Subject: [PATCH 1/5] s3: smbd: Fix Linux sendfile() for SMB2. Ensure we don't spin on EAGAIN. For SMB2 the socket is set non-blocking. Ensure sendfile() calls complete if they return EAGAIN by saving the socket state, setting it blocking, doing the sendfile until completion and then restoring the socket state. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13537 Signed-off-by: Jeremy Allison Reviewed-by: Volker Lendecke (cherry picked from commit 809967b3eab7a1b48c186517701538ca93536246) --- source3/lib/sendfile.c | 84 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 7 deletions(-) diff --git a/source3/lib/sendfile.c b/source3/lib/sendfile.c index 3d457bd6f13..f7db57acee1 100644 --- a/source3/lib/sendfile.c +++ b/source3/lib/sendfile.c @@ -24,6 +24,7 @@ */ #include "includes.h" +#include "system/filesys.h" #if defined(LINUX_SENDFILE_API) @@ -36,8 +37,10 @@ ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count) { size_t total=0; - ssize_t ret; + ssize_t ret = -1; size_t hdr_len = 0; + int old_flags = 0; + bool socket_flags_changed = false; /* * Send the header first. @@ -48,8 +51,25 @@ ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset hdr_len = header->length; while (total < hdr_len) { ret = sys_send(tofd, header->data + total,hdr_len - total, MSG_MORE); - if (ret == -1) - return -1; + if (ret == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* + * send() must complete before we can + * send any other outgoing data on the + * socket. Ensure socket is in blocking + * mode. For SMB2 by default the socket + * is in non-blocking mode. + */ + old_flags = fcntl(tofd, F_GETFL, 0); + ret = set_blocking(tofd, true); + if (ret == -1) { + goto out; + } + socket_flags_changed = true; + continue; + } + goto out; + } total += ret; } } @@ -59,8 +79,34 @@ ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset ssize_t nwritten; do { nwritten = sendfile(tofd, fromfd, &offset, total); - } while (nwritten == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); + } while (nwritten == -1 && errno == EINTR); if (nwritten == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + if (socket_flags_changed) { + /* + * We're already in blocking + * mode. This is an error. + */ + ret = -1; + goto out; + } + + /* + * Sendfile must complete before we can + * send any other outgoing data on the socket. + * Ensure socket is in blocking mode. + * For SMB2 by default the socket is in + * non-blocking mode. + */ + old_flags = fcntl(tofd, F_GETFL, 0); + ret = set_blocking(tofd, true); + if (ret == -1) { + goto out; + } + socket_flags_changed = true; + continue; + } + if (errno == ENOSYS || errno == EINVAL) { /* Ok - we're in a world of pain here. We just sent * the header, but the sendfile failed. We have to @@ -72,17 +118,41 @@ ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset */ errno = EINTR; /* Normally we can never return this. */ } - return -1; + ret = -1; + goto out; } if (nwritten == 0) { /* * EOF, return a short read */ - return hdr_len + (count - total); + ret = hdr_len + (count - total); + goto out; } total -= nwritten; } - return count + hdr_len; + + ret = count + hdr_len; + + out: + + if (socket_flags_changed) { + int saved_errno; + int err; + + if (ret == -1) { + saved_errno = errno; + } + /* Restore the old state of the socket. */ + err = fcntl(tofd, F_SETFL, old_flags); + if (err == -1) { + return -1; + } + if (ret == -1) { + errno = saved_errno; + } + } + + return ret; } #elif defined(SOLARIS_SENDFILE_API) -- 2.18.0.233.g985f88cf7e-goog From 2312ed7ffe0242b80e1e534a759db4f5aa1de85d Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 18 Jul 2018 15:29:37 -0700 Subject: [PATCH 2/5] s3: smbd: Fix Solaris sendfile() for SMB2. Ensure we don't spin on EAGAIN. For SMB2 the socket is set non-blocking. Ensure sendfile() calls complete if they return EAGAIN by saving the socket state, setting it blocking, doing the sendfile until completion and then restoring the socket state. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13537 Signed-off-by: Jeremy Allison Reviewed-by: Volker Lendecke (cherry picked from commit 16a587075c8c62c1160869358ca56a133e90247a) --- source3/lib/sendfile.c | 56 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/source3/lib/sendfile.c b/source3/lib/sendfile.c index f7db57acee1..db66bf72d16 100644 --- a/source3/lib/sendfile.c +++ b/source3/lib/sendfile.c @@ -169,6 +169,9 @@ ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset size_t total, xferred; struct sendfilevec vec[2]; ssize_t hdr_len = 0; + int old_flags = 0; + ssize_t ret = -1; + bool socket_flags_changed = false; if (header) { sfvcnt = 2; @@ -205,17 +208,37 @@ ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset xferred = 0; nwritten = sendfilev(tofd, vec, sfvcnt, &xferred); - if (nwritten == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)) { + if (nwritten == -1 && errno == EINTR) { if (xferred == 0) continue; /* Nothing written yet. */ else nwritten = xferred; } - if (nwritten == -1) - return -1; - if (nwritten == 0) - return -1; /* I think we're at EOF here... */ + if (nwritten == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* + * Sendfile must complete before we can + * send any other outgoing data on the socket. + * Ensure socket is in blocking mode. + * For SMB2 by default the socket is in + * non-blocking mode. + */ + old_flags = fcntl(tofd, F_GETFL, 0); + ret = set_blocking(tofd, true); + if (ret == -1) { + goto out; + } + socket_flags_changed = true; + continue; + } + ret = -1; + goto out; + } + if (nwritten == 0) { + ret = -1; + goto out; /* I think we're at EOF here... */ + } /* * If this was a short (signal interrupted) write we may need @@ -237,7 +260,28 @@ ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset } total -= nwritten; } - return count + hdr_len; + ret = count + hdr_len; + + out: + + if (socket_flags_changed) { + int saved_errno; + int err; + + if (ret == -1) { + saved_errno = errno; + } + /* Restore the old state of the socket. */ + err = fcntl(tofd, F_SETFL, old_flags); + if (err == -1) { + return -1; + } + if (ret == -1) { + errno = saved_errno; + } + } + + return ret; } #elif defined(HPUX_SENDFILE_API) -- 2.18.0.233.g985f88cf7e-goog From 0fd9deecca0b1d18ad8a799be4ad5eb8733f4610 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 18 Jul 2018 15:36:47 -0700 Subject: [PATCH 3/5] s3: smbd: Fix HPUX sendfile() for SMB2. Ensure we don't spin on EAGAIN. For SMB2 the socket is set non-blocking. Ensure sendfile() calls complete if they return EAGAIN by saving the socket state, setting it blocking, doing the sendfile until completion and then restoring the socket state. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13537 Signed-off-by: Jeremy Allison Reviewed-by: Volker Lendecke (cherry picked from commit d222caa449d9c00bb2dd9da6c79ea509960d47c6) --- source3/lib/sendfile.c | 56 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/source3/lib/sendfile.c b/source3/lib/sendfile.c index db66bf72d16..05e9a9b7cbd 100644 --- a/source3/lib/sendfile.c +++ b/source3/lib/sendfile.c @@ -294,6 +294,9 @@ ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset size_t total=0; struct iovec hdtrl[2]; size_t hdr_len = 0; + int old_flags = 0; + ssize_t ret = -1; + bool socket_flags_changed = false; if (header) { /* Set up the header/trailer iovec. */ @@ -319,11 +322,31 @@ ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset do { nwritten = sendfile(tofd, fromfd, offset, total, &hdtrl[0], 0); - } while (nwritten == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)); - if (nwritten == -1) - return -1; - if (nwritten == 0) - return -1; /* I think we're at EOF here... */ + } while (nwritten == -1 && errno == EINTR); + if (nwritten == -1) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* + * Sendfile must complete before we can + * send any other outgoing data on the socket. + * Ensure socket is in blocking mode. + * For SMB2 by default the socket is in + * non-blocking mode. + */ + old_flags = fcntl(tofd, F_GETFL, 0); + ret = set_blocking(tofd, true); + if (ret == -1) { + goto out; + } + socket_flags_changed = true; + continue; + } + ret = -1; + goto out; + } + if (nwritten == 0) { + ret = -1; /* I think we're at EOF here... */ + goto out; + } /* * If this was a short (signal interrupted) write we may need @@ -347,7 +370,28 @@ ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset total -= nwritten; offset += nwritten; } - return count + hdr_len; + ret = count + hdr_len; + + out: + + if (socket_flags_changed) { + int saved_errno; + int err; + + if (ret == -1) { + saved_errno = errno; + } + /* Restore the old state of the socket. */ + err = fcntl(tofd, F_SETFL, old_flags); + if (err == -1) { + return -1; + } + if (ret == -1) { + errno = saved_errno; + } + } + + return ret; } #elif defined(FREEBSD_SENDFILE_API) || defined(DARWIN_SENDFILE_API) -- 2.18.0.233.g985f88cf7e-goog From b4f89fffed13c40355681b2077f2993c1154cce3 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 18 Jul 2018 15:44:34 -0700 Subject: [PATCH 4/5] s3: smbd: Fix FreeBSD sendfile() for SMB2. Ensure we don't spin on EAGAIN. For SMB2 the socket is set non-blocking. Ensure sendfile() calls complete if they return EAGAIN by saving the socket state, setting it blocking, doing the sendfile until completion and then restoring the socket state. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13537 Signed-off-by: Jeremy Allison Reviewed-by: Volker Lendecke (cherry picked from commit 456e520a3be7e4b54f1f144324c3671b8f6e35ea) --- source3/lib/sendfile.c | 48 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/source3/lib/sendfile.c b/source3/lib/sendfile.c index 05e9a9b7cbd..aa115948501 100644 --- a/source3/lib/sendfile.c +++ b/source3/lib/sendfile.c @@ -405,9 +405,11 @@ ssize_t sys_sendfile(int tofd, int fromfd, { struct sf_hdtr sf_header = {0}; struct iovec io_header = {0}; + int old_flags = 0; off_t nwritten; - int ret; + ssize_t ret = -1; + bool socket_flags_changed = false; if (header) { sf_header.headers = &io_header; @@ -428,9 +430,26 @@ ssize_t sys_sendfile(int tofd, int fromfd, #else ret = sendfile(fromfd, tofd, offset, count, &sf_header, &nwritten, 0); #endif - if (ret == -1 && errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK) { + if (ret == -1 && errno != EINTR) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* + * Sendfile must complete before we can + * send any other outgoing data on the socket. + * Ensure socket is in blocking mode. + * For SMB2 by default the socket is in + * non-blocking mode. + */ + old_flags = fcntl(tofd, F_GETFL, 0); + ret = set_blocking(tofd, true); + if (ret == -1) { + goto out; + } + socket_flags_changed = true; + continue; + } /* Send failed, we are toast. */ - return -1; + ret = -1; + goto out; } if (nwritten == 0) { @@ -457,7 +476,28 @@ ssize_t sys_sendfile(int tofd, int fromfd, count -= nwritten; } - return nwritten; + ret = nwritten; + + out: + + if (socket_flags_changed) { + int saved_errno; + int err; + + if (ret == -1) { + saved_errno = errno; + } + /* Restore the old state of the socket. */ + err = fcntl(tofd, F_SETFL, old_flags); + if (err == -1) { + return -1; + } + if (ret == -1) { + errno = saved_errno; + } + } + + return ret; } #elif defined(AIX_SENDFILE_API) -- 2.18.0.233.g985f88cf7e-goog From 770e53822f5b8b30ca3e25d5957a3bbc20a8b05a Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 18 Jul 2018 15:49:29 -0700 Subject: [PATCH 5/5] s3: smbd: Fix AIX sendfile() for SMB2. Ensure we don't spin on EAGAIN. For SMB2 the socket is set non-blocking. Ensure sendfile() calls complete if they return EAGAIN by saving the socket state, setting it blocking, doing the sendfile until completion and then restoring the socket state. BUG: https://bugzilla.samba.org/show_bug.cgi?id=13537 Signed-off-by: Jeremy Allison Reviewed-by: Volker Lendecke Autobuild-User(master): Volker Lendecke Autobuild-Date(master): Fri Jul 20 15:14:24 CEST 2018 on sn-devel-144 (cherry picked from commit 582ce5d6b599516d6d8d619529a2aa809139a175) --- source3/lib/sendfile.c | 49 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/source3/lib/sendfile.c b/source3/lib/sendfile.c index aa115948501..d3effaf30aa 100644 --- a/source3/lib/sendfile.c +++ b/source3/lib/sendfile.c @@ -510,6 +510,9 @@ ssize_t sys_sendfile(int tofd, int fromfd, ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset, size_t count) { struct sf_parms hdtrl; + int old_flags = 0; + ssize_t ret = -1; + bool socket_flags_changed = false; /* Set up the header/trailer struct params. */ if (header) { @@ -527,8 +530,6 @@ ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset hdtrl.file_bytes = count; while ( hdtrl.file_bytes + hdtrl.header_length ) { - ssize_t ret; - /* Return Value @@ -546,12 +547,50 @@ ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, off_t offset */ do { ret = send_file(&tofd, &hdtrl, 0); - } while ((ret == 1) || (ret == -1 && (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK))); - if ( ret == -1 ) + } while ((ret == 1) || (ret == -1 && errno == EINTR)); + if ( ret == -1 ) { + if (errno == EAGAIN || errno == EWOULDBLOCK) { + /* + * Sendfile must complete before we can + * send any other outgoing data on the socket. + * Ensure socket is in blocking mode. + * For SMB2 by default the socket is in + * non-blocking mode. + */ + old_flags = fcntl(tofd, F_GETFL, 0); + ret = set_blocking(tofd, true); + if (ret == -1) { + goto out; + } + socket_flags_changed = true; + continue; + } + goto out; + } + } + + ret = count + header->length; + + out: + + if (socket_flags_changed) { + int saved_errno; + int err; + + if (ret == -1) { + saved_errno = errno; + } + /* Restore the old state of the socket. */ + err = fcntl(tofd, F_SETFL, old_flags); + if (err == -1) { return -1; + } + if (ret == -1) { + errno = saved_errno; + } } - return count + header->length; + return ret; } /* END AIX SEND_FILE */ -- 2.18.0.233.g985f88cf7e-goog