summaryrefslogtreecommitdiff
path: root/source3
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2009-01-13 13:02:44 -0800
committerJeremy Allison <jra@samba.org>2009-01-13 13:02:44 -0800
commit0f450623c5e73b941ced136ba7160bf6554a21ce (patch)
tree5232d14f1f9dcf737a12a8d91e7d7de0a677ce93 /source3
parent83c3b2809636cab704a54c2d699a907c6d07d199 (diff)
downloadsamba-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.c16
-rw-r--r--source3/smbd/reply.c68
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;