From 11aca9c703b2d4003657939de59628beb956bfa4 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Sat, 24 Dec 2011 21:12:09 -0800 Subject: [PATCH 1/4] Fix bug #8679 - recvfile code path using splice() on Linux leaves data in the pipe on short write Bug found and fix suggested by Andrew Bartlett. Autobuild-User: Jeremy Allison Autobuild-Date: Sun Dec 25 07:46:38 CET 2011 on sn-devel-104 (cherry picked from commit eb617374a673bb1189dd9b6bccbf3f1d9fb91010) --- source3/lib/recvfile.c | 5 ++--- 1 files changed, 2 insertions(+), 3 deletions(-) diff --git a/source3/lib/recvfile.c b/source3/lib/recvfile.c index 7cbb8a0..5d1c0b2 100644 --- a/source3/lib/recvfile.c +++ b/source3/lib/recvfile.c @@ -214,10 +214,9 @@ ssize_t sys_recvfile(int fromfd, } done: - if (total_written < count) { + if (count) { int saved_errno = errno; - if (drain_socket(fromfd, count-total_written) != - count-total_written) { + if (drain_socket(fromfd, count) != count) { /* socket is dead. */ return -1; } -- 1.7.3.1 From b030f76f49557a940d32b9d23739f0c7038f3c51 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 30 Dec 2011 20:23:00 -0800 Subject: [PATCH 2/4] Second part of fix for bug #8679 - recvfile code path using splice() on Linux leaves data in the pipe on short write. Split out the functionality of drain_socket() into a separate function from default_sys_recvfile(). (cherry picked from commit a5715420e37b98038fe8f2c3028e4c6938400eed) --- source3/lib/recvfile.c | 31 ++++++++++++++++++++++++++++++- 1 files changed, 30 insertions(+), 1 deletions(-) diff --git a/source3/lib/recvfile.c b/source3/lib/recvfile.c index 5d1c0b2..31d9311 100644 --- a/source3/lib/recvfile.c +++ b/source3/lib/recvfile.c @@ -242,9 +242,38 @@ ssize_t sys_recvfile(int fromfd, /***************************************************************** Throw away "count" bytes from the client socket. + Returns count or -1 on error. *****************************************************************/ ssize_t drain_socket(int sockfd, size_t count) { - return default_sys_recvfile(sockfd, -1, (SMB_OFF_T)-1, count); + size_t total = 0; + size_t bufsize = MIN(TRANSFER_BUF_SIZE,count); + char *buffer = NULL; + + if (count == 0) { + return 0; + } + + buffer = SMB_MALLOC_ARRAY(char, bufsize); + if (buffer == NULL) { + return -1; + } + + while (total < count) { + ssize_t read_ret; + size_t toread = MIN(bufsize,count - total); + + /* Read from socket - ignore EINTR. */ + read_ret = sys_read(sockfd, buffer, toread); + if (read_ret <= 0) { + /* EOF or socket error. */ + free(buffer); + return -1; + } + total += read_ret; + } + + free(buffer); + return count; } -- 1.7.3.1 From eacd3588d0e70cef7a2cd025b9efc5c59d0d9066 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 30 Dec 2011 20:45:10 -0800 Subject: [PATCH 3/4] Third part of fix for bug #8679 - recvfile code path using splice() on Linux leaves data in the pipe on short write. Fix default_sys_recvfile() to correctly cope with short writes. Return the amount written. Return -1 and set errno if no data could be written. (cherry picked from commit 5e6263960aaf1a5f9993cb7bb5646d36ff92b9cc) --- source3/lib/recvfile.c | 39 ++++++++++++++++++--------------------- 1 files changed, 18 insertions(+), 21 deletions(-) diff --git a/source3/lib/recvfile.c b/source3/lib/recvfile.c index 31d9311..c74cdd5 100644 --- a/source3/lib/recvfile.c +++ b/source3/lib/recvfile.c @@ -30,16 +30,10 @@ * It's safe to make direct syscalls to lseek/write here * as we're below the Samba vfs layer. * - * If tofd is -1 we just drain the incoming socket of count - * bytes without writing to the outgoing fd. - * If a write fails we do the same (to cope with disk full) - * errors. - * * Returns -1 on short reads from fromfd (read error) * and sets errno. * * Returns number of bytes written to 'tofd' - * or thrown away if 'tofd == -1'. * return != count then sets errno. * Returns count if complete success. */ @@ -96,23 +90,26 @@ static ssize_t default_sys_recvfile(int fromfd, num_written = 0; - while (num_written < read_ret) { + /* Don't write any more after a write error. */ + while (tofd != -1 && (num_written < read_ret)) { ssize_t write_ret; - if (tofd == -1) { - write_ret = read_ret; - } else { - /* Write to file - ignore EINTR. */ - write_ret = sys_write(tofd, - buffer + num_written, - read_ret - num_written); - - if (write_ret <= 0) { - /* write error - stop writing. */ - tofd = -1; - saved_errno = errno; - continue; - } + /* Write to file - ignore EINTR. */ + write_ret = sys_write(tofd, + buffer + num_written, + read_ret - num_written); + + if (write_ret <= 0) { + /* write error - stop writing. */ + tofd = -1; + if (total_written == 0) { + /* Ensure we return + -1 if the first + write failed. */ + total_written = -1; + } + saved_errno = errno; + break; } num_written += (size_t)write_ret; -- 1.7.3.1 From ec8b56ee6aee62b13e7f1f911a2a1b4c5269c691 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Fri, 30 Dec 2011 21:19:08 -0800 Subject: [PATCH 4/4] Final part of fix for bug #8679 - recvfile code path using splice() on Linux leaves data in the pipe on short write. The code to set a DOS error on short writeX return is amazingly legacy code, and also breaks the reply as fixup_chain_error_packet() enforces a 2-byte wct on any reply where smb_rcls != 0. Found in testing by Andrew Bartlett. Thanks Andrew ! Autobuild-User: Jeremy Allison Autobuild-Date: Sat Dec 31 08:05:35 CET 2011 on sn-devel-104 (cherry picked from commit e39df67669f61056692736db9c8dc16fbf2c3624) --- source3/smbd/reply.c | 5 ----- 1 files changed, 0 insertions(+), 5 deletions(-) diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 6d3ea1d..c0e8a98 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -4630,11 +4630,6 @@ void reply_write_and_X(struct smb_request *req) SSVAL(req->outbuf,smb_vwv2,nwritten); SSVAL(req->outbuf,smb_vwv4,nwritten>>16); - if (nwritten < (ssize_t)numtowrite) { - SCVAL(req->outbuf,smb_rcls,ERRHRD); - SSVAL(req->outbuf,smb_err,ERRdiskfull); - } - DEBUG(3,("writeX fnum=%d num=%d wrote=%d\n", fsp->fnum, (int)numtowrite, (int)nwritten)); -- 1.7.3.1