diff options
author | Jeremy Allison <jra@samba.org> | 2009-01-13 13:02:44 -0800 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2009-01-13 13:02:44 -0800 |
commit | 0f450623c5e73b941ced136ba7160bf6554a21ce (patch) | |
tree | 5232d14f1f9dcf737a12a8d91e7d7de0a677ce93 /source3 | |
parent | 83c3b2809636cab704a54c2d699a907c6d07d199 (diff) | |
download | samba-0f450623c5e73b941ced136ba7160bf6554a21ce.tar.gz samba-0f450623c5e73b941ced136ba7160bf6554a21ce.tar.bz2 samba-0f450623c5e73b941ced136ba7160bf6554a21ce.zip |
Fix bug noticed by Volker - if sendfile returns zero then
we might have to handle a short send by filling with zeros.
Jeremy.
Diffstat (limited to 'source3')
-rw-r--r-- | source3/lib/sendfile.c | 16 | ||||
-rw-r--r-- | source3/smbd/reply.c | 68 |
2 files changed, 78 insertions, 6 deletions
diff --git a/source3/lib/sendfile.c b/source3/lib/sendfile.c index fb10cae2aa..59a18ce6e1 100644 --- a/source3/lib/sendfile.c +++ b/source3/lib/sendfile.c @@ -78,8 +78,12 @@ ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T of } return -1; } - if (nwritten == 0) - return -1; /* I think we're at EOF here... */ + if (nwritten == 0) { + /* + * EOF, return a short read + */ + return hdr_len + (count - total); + } total -= nwritten; } return count + hdr_len; @@ -156,8 +160,12 @@ ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T of } return -1; } - if (nwritten == 0) - return -1; /* I think we're at EOF here... */ + if (nwritten == 0) { + /* + * EOF, return a short read + */ + return hdr_len + (((uint32)count) - small_total); + } small_total -= nwritten; } return count + hdr_len; diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 96fb939e05..c8b8ec50e5 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -2654,6 +2654,63 @@ static ssize_t fake_sendfile(files_struct *fsp, SMB_OFF_T startpos, } /**************************************************************************** + Deal with the case of sendfile reading less bytes from the file than + requested. Fill with zeros (all we can do). +****************************************************************************/ + +static void sendfile_short_send(files_struct *fsp, + ssize_t nread, + size_t headersize, + size_t smb_maxcnt) +{ + if (nread < headersize) { + DEBUG(0,("sendfile_short_send: sendfile failed to send " + "header for file %s (%s). Terminating\n", + fsp->fsp_name, strerror(errno) )); + exit_server_cleanly("sendfile_short_send failed"); + } + + nread -= headersize; + + if (nread < smb_maxcnt) { + char *buf = SMB_CALLOC_ARRAY(char, 1024); + if (!buf) { + exit_server_cleanly("sendfile_short_send: " + "malloc failed"); + } + + DEBUG(0,("sendfile_short_send: filling truncated file %s " + "with zeros !\n", fsp->fsp_name)); + + while (nread < smb_maxcnt) { + /* + * We asked for the real file size and told sendfile + * to not go beyond the end of the file. But it can + * happen that in between our fstat call and the + * sendfile call the file was truncated. This is very + * bad because we have already announced the larger + * number of bytes to the client. + * + * The best we can do now is to send 0-bytes, just as + * a read from a hole in a sparse file would do. + * + * This should happen rarely enough that I don't care + * about efficiency here :-) + */ + size_t to_write; + + to_write = MIN(sizeof(buf), smb_maxcnt - nread); + if (write_data(smbd_server_fd(), buf, to_write) != to_write) { + exit_server_cleanly("sendfile_short_send: " + "write_data failed"); + } + nread += to_write; + } + SAFE_FREE(buf); + } +} + +/**************************************************************************** Return a readbraw error (4 bytes of zero). ****************************************************************************/ @@ -2689,14 +2746,15 @@ void send_file_readbraw(connection_struct *conn, if ( (chain_size == 0) && (nread > 0) && (fsp->base_fsp == NULL) && (fsp->wcp == NULL) && lp_use_sendfile(SNUM(conn)) ) { + ssize_t sendfile_read = -1; char header[4]; DATA_BLOB header_blob; _smb_setlen(header,nread); header_blob = data_blob_const(header, 4); - if (SMB_VFS_SENDFILE(smbd_server_fd(), fsp, - &header_blob, startpos, nread) == -1) { + if ((sendfile_read = SMB_VFS_SENDFILE(smbd_server_fd(), fsp, + &header_blob, startpos, nread)) == -1) { /* Returning ENOSYS means no data at all was sent. * Do this as a normal read. */ if (errno == ENOSYS) { @@ -2726,6 +2784,8 @@ void send_file_readbraw(connection_struct *conn, exit_server_cleanly("send_file_readbraw sendfile failed"); } + /* Deal with possible short send. */ + sendfile_short_send(fsp, sendfile_read, 4, nread); return; } #endif @@ -3216,6 +3276,10 @@ static void send_file_readX(connection_struct *conn, struct smb_request *req, DEBUG( 3, ( "send_file_readX: sendfile fnum=%d max=%d nread=%d\n", fsp->fnum, (int)smb_maxcnt, (int)nread ) ); + + /* Deal with possible short send. */ + sendfile_short_send(fsp, nread, sizeof(headerbuf), smb_maxcnt); + /* No outbuf here means successful sendfile. */ TALLOC_FREE(req->outbuf); return; |