mirror of
https://github.com/samba-team/samba.git
synced 2024-12-23 17:34:34 +03:00
r3944: Fix the problem we get on Linux where sendfile fails, but we've already sent the
header using send(). As our implementation of sendfile can't return EINTR (it restarts in that case) use an errno of EINTR to signal the linux sendfile fail after header case. When that happens send the rest of the data and then turn off sendfile. Sendfile should be safe to enable on all systems now (even though it may not help in all performance cases). Jeremy.
This commit is contained in:
parent
412ff4a129
commit
78236382f7
@ -65,8 +65,20 @@ ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T of
|
|||||||
nwritten = sendfile(tofd, fromfd, &offset, total);
|
nwritten = sendfile(tofd, fromfd, &offset, total);
|
||||||
#endif
|
#endif
|
||||||
} while (nwritten == -1 && errno == EINTR);
|
} while (nwritten == -1 && errno == EINTR);
|
||||||
if (nwritten == -1)
|
if (nwritten == -1) {
|
||||||
|
if (errno == ENOSYS) {
|
||||||
|
/* Ok - we're in a world of pain here. We just sent
|
||||||
|
* the header, but the sendfile failed. We have to
|
||||||
|
* emulate the sendfile at an upper layer before we
|
||||||
|
* disable it's use. So we do something really ugly.
|
||||||
|
* We set the errno to a strange value so we can detect
|
||||||
|
* this at the upper level and take care of it without
|
||||||
|
* layer violation. JRA.
|
||||||
|
*/
|
||||||
|
errno = EINTR; /* Normally we can never return this. */
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
if (nwritten == 0)
|
if (nwritten == 0)
|
||||||
return -1; /* I think we're at EOF here... */
|
return -1; /* I think we're at EOF here... */
|
||||||
total -= nwritten;
|
total -= nwritten;
|
||||||
@ -131,8 +143,20 @@ ssize_t sys_sendfile(int tofd, int fromfd, const DATA_BLOB *header, SMB_OFF_T of
|
|||||||
do {
|
do {
|
||||||
nwritten = sendfile(tofd, fromfd, &small_offset, small_total);
|
nwritten = sendfile(tofd, fromfd, &small_offset, small_total);
|
||||||
} while (nwritten == -1 && errno == EINTR);
|
} while (nwritten == -1 && errno == EINTR);
|
||||||
if (nwritten == -1)
|
if (nwritten == -1) {
|
||||||
|
if (errno == ENOSYS) {
|
||||||
|
/* Ok - we're in a world of pain here. We just sent
|
||||||
|
* the header, but the sendfile failed. We have to
|
||||||
|
* emulate the sendfile at an upper layer before we
|
||||||
|
* disable it's use. So we do something really ugly.
|
||||||
|
* We set the errno to a strange value so we can detect
|
||||||
|
* this at the upper level and take care of it without
|
||||||
|
* layer violation. JRA.
|
||||||
|
*/
|
||||||
|
errno = EINTR; /* Normally we can never return this. */
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
if (nwritten == 0)
|
if (nwritten == 0)
|
||||||
return -1; /* I think we're at EOF here... */
|
return -1; /* I think we're at EOF here... */
|
||||||
small_total -= nwritten;
|
small_total -= nwritten;
|
||||||
|
@ -1717,7 +1717,7 @@ int reply_unlink(connection_struct *conn, char *inbuf,char *outbuf, int dum_size
|
|||||||
Fail for readbraw.
|
Fail for readbraw.
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
void fail_readraw(void)
|
static void fail_readraw(void)
|
||||||
{
|
{
|
||||||
pstring errstr;
|
pstring errstr;
|
||||||
slprintf(errstr, sizeof(errstr)-1, "FAIL ! reply_readbraw: socket write fail (%s)",
|
slprintf(errstr, sizeof(errstr)-1, "FAIL ! reply_readbraw: socket write fail (%s)",
|
||||||
@ -1725,12 +1725,46 @@ void fail_readraw(void)
|
|||||||
exit_server(errstr);
|
exit_server(errstr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(WITH_SENDFILE)
|
||||||
|
/****************************************************************************
|
||||||
|
Fake (read/write) sendfile. Returns -1 on read or write fail.
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
static ssize_t fake_sendfile(files_struct *fsp, SMB_OFF_T startpos, size_t nread, char *buf, int bufsize)
|
||||||
|
{
|
||||||
|
ssize_t ret=0;
|
||||||
|
|
||||||
|
/* Paranioa check... */
|
||||||
|
if (nread > bufsize) {
|
||||||
|
fail_readraw();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nread > 0) {
|
||||||
|
ret = read_file(fsp,buf,startpos,nread);
|
||||||
|
if (ret == -1) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we had a short read, fill with zeros. */
|
||||||
|
if (ret < nread) {
|
||||||
|
memset(buf, '\0', nread - ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (write_data(smbd_server_fd(),buf,nread) != nread) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (ssize_t)nread;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
Use sendfile in readbraw.
|
Use sendfile in readbraw.
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
void send_file_readbraw(connection_struct *conn, files_struct *fsp, SMB_OFF_T startpos, size_t nread,
|
void send_file_readbraw(connection_struct *conn, files_struct *fsp, SMB_OFF_T startpos, size_t nread,
|
||||||
ssize_t mincount, char *outbuf)
|
ssize_t mincount, char *outbuf, int out_buffsize)
|
||||||
{
|
{
|
||||||
ssize_t ret=0;
|
ssize_t ret=0;
|
||||||
|
|
||||||
@ -1752,12 +1786,21 @@ void send_file_readbraw(connection_struct *conn, files_struct *fsp, SMB_OFF_T st
|
|||||||
|
|
||||||
if ( SMB_VFS_SENDFILE( smbd_server_fd(), fsp, fsp->fd, &header, startpos, nread) == -1) {
|
if ( SMB_VFS_SENDFILE( smbd_server_fd(), fsp, fsp->fd, &header, startpos, nread) == -1) {
|
||||||
/*
|
/*
|
||||||
* Special hack for broken Linux with no 64 bit clean sendfile. If we
|
* Special hack for broken Linux with no working sendfile. If we
|
||||||
* return ENOSYS then pretend we just got a normal read.
|
* return EINTR we sent the header but not the rest of the data.
|
||||||
|
* Fake this up by doing read/write calls.
|
||||||
*/
|
*/
|
||||||
if (errno == ENOSYS) {
|
if (errno == EINTR) {
|
||||||
|
/* Ensure we don't do this again. */
|
||||||
set_use_sendfile(SNUM(conn), False);
|
set_use_sendfile(SNUM(conn), False);
|
||||||
goto normal_read;
|
DEBUG(0,("send_file_readbraw: sendfile not available. Faking..\n"));
|
||||||
|
|
||||||
|
if (fake_sendfile(fsp, startpos, nread, outbuf + 4, out_buffsize - 4) == -1) {
|
||||||
|
DEBUG(0,("send_file_readbraw: fake_sendfile failed for file %s (%s).\n",
|
||||||
|
fsp->fsp_name, strerror(errno) ));
|
||||||
|
exit_server("send_file_readbraw fake_sendfile failed");
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG(0,("send_file_readbraw: sendfile failed for file %s (%s). Terminating\n",
|
DEBUG(0,("send_file_readbraw: sendfile failed for file %s (%s). Terminating\n",
|
||||||
@ -1767,7 +1810,6 @@ void send_file_readbraw(connection_struct *conn, files_struct *fsp, SMB_OFF_T st
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
normal_read:
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (nread > 0) {
|
if (nread > 0) {
|
||||||
@ -1790,7 +1832,7 @@ void send_file_readbraw(connection_struct *conn, files_struct *fsp, SMB_OFF_T st
|
|||||||
Reply to a readbraw (core+ protocol).
|
Reply to a readbraw (core+ protocol).
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_size, int dum_buffsize)
|
int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_size, int out_buffsize)
|
||||||
{
|
{
|
||||||
extern struct current_user current_user;
|
extern struct current_user current_user;
|
||||||
ssize_t maxcount,mincount;
|
ssize_t maxcount,mincount;
|
||||||
@ -1905,7 +1947,7 @@ int reply_readbraw(connection_struct *conn, char *inbuf, char *outbuf, int dum_s
|
|||||||
DEBUG( 3, ( "readbraw fnum=%d start=%.0f max=%d min=%d nread=%d\n", fsp->fnum, (double)startpos,
|
DEBUG( 3, ( "readbraw fnum=%d start=%.0f max=%d min=%d nread=%d\n", fsp->fnum, (double)startpos,
|
||||||
(int)maxcount, (int)mincount, (int)nread ) );
|
(int)maxcount, (int)mincount, (int)nread ) );
|
||||||
|
|
||||||
send_file_readbraw(conn, fsp, startpos, nread, mincount, outbuf);
|
send_file_readbraw(conn, fsp, startpos, nread, mincount, outbuf, out_buffsize);
|
||||||
|
|
||||||
DEBUG(5,("readbraw finished\n"));
|
DEBUG(5,("readbraw finished\n"));
|
||||||
END_PROFILE(SMBreadbraw);
|
END_PROFILE(SMBreadbraw);
|
||||||
@ -2069,7 +2111,7 @@ Returning short read of maximum allowed for compatibility with Windows 2000.\n",
|
|||||||
Reply to a read and X - possibly using sendfile.
|
Reply to a read and X - possibly using sendfile.
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
int send_file_readX(connection_struct *conn, char *inbuf,char *outbuf,int length,
|
int send_file_readX(connection_struct *conn, char *inbuf,char *outbuf,int length, int len_outbuf,
|
||||||
files_struct *fsp, SMB_OFF_T startpos, size_t smb_maxcnt)
|
files_struct *fsp, SMB_OFF_T startpos, size_t smb_maxcnt)
|
||||||
{
|
{
|
||||||
ssize_t nread = -1;
|
ssize_t nread = -1;
|
||||||
@ -2117,12 +2159,22 @@ int send_file_readX(connection_struct *conn, char *inbuf,char *outbuf,int length
|
|||||||
|
|
||||||
if ( SMB_VFS_SENDFILE( smbd_server_fd(), fsp, fsp->fd, &header, startpos, smb_maxcnt) == -1) {
|
if ( SMB_VFS_SENDFILE( smbd_server_fd(), fsp, fsp->fd, &header, startpos, smb_maxcnt) == -1) {
|
||||||
/*
|
/*
|
||||||
* Special hack for broken Linux with no 64 bit clean sendfile. If we
|
* Special hack for broken Linux with no working sendfile. If we
|
||||||
* return ENOSYS then pretend we just got a normal read.
|
* return EINTR we sent the header but not the rest of the data.
|
||||||
|
* Fake this up by doing read/write calls.
|
||||||
*/
|
*/
|
||||||
if (errno == ENOSYS) {
|
if (errno == EINTR) {
|
||||||
|
/* Ensure we don't do this again. */
|
||||||
set_use_sendfile(SNUM(conn), False);
|
set_use_sendfile(SNUM(conn), False);
|
||||||
goto normal_read;
|
DEBUG(0,("send_file_readX: sendfile not available. Faking..\n"));
|
||||||
|
|
||||||
|
if ((nread = fake_sendfile(fsp, startpos, smb_maxcnt, data,
|
||||||
|
len_outbuf - (data-outbuf))) == -1) {
|
||||||
|
DEBUG(0,("send_file_readX: fake_sendfile failed for file %s (%s).\n",
|
||||||
|
fsp->fsp_name, strerror(errno) ));
|
||||||
|
exit_server("send_file_readX: fake_sendfile failed");
|
||||||
|
}
|
||||||
|
return nread;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEBUG(0,("send_file_readX: sendfile failed for file %s (%s). Terminating\n",
|
DEBUG(0,("send_file_readX: sendfile failed for file %s (%s). Terminating\n",
|
||||||
@ -2223,7 +2275,7 @@ int reply_read_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt
|
|||||||
return ERROR_DOS(ERRDOS,ERRlock);
|
return ERROR_DOS(ERRDOS,ERRlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
nread = send_file_readX(conn, inbuf, outbuf, length, fsp, startpos, smb_maxcnt);
|
nread = send_file_readX(conn, inbuf, outbuf, length, bufsize, fsp, startpos, smb_maxcnt);
|
||||||
if (nread != -1)
|
if (nread != -1)
|
||||||
nread = chain_reply(inbuf,outbuf,length,bufsize);
|
nread = chain_reply(inbuf,outbuf,length,bufsize);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user