diff options
author | Jeremy Allison <jra@samba.org> | 2002-09-12 02:12:52 +0000 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2002-09-12 02:12:52 +0000 |
commit | 4f613ac61e9bc442715141188d915b537e15f4c6 (patch) | |
tree | 1988e7728bb0828831d953da13db6895c7cc9e13 /source3 | |
parent | b060593287f6344a147da95e5179170335ad78d1 (diff) | |
download | samba-4f613ac61e9bc442715141188d915b537e15f4c6.tar.gz samba-4f613ac61e9bc442715141188d915b537e15f4c6.tar.bz2 samba-4f613ac61e9bc442715141188d915b537e15f4c6.zip |
First cut at portable sendfile code. Only used in readX at the moment
and doesn't actually call sendfile. Needs to be vectored through the
VFS and tested on all supported platforms (Solaris/HPUX/FreeBSD/Linux).
Linux doesn't actually work (2.4.19 kernel) at the moment because it
doesn't have a 64-bit clean sendfile.
Jeremy.
(This used to be commit fd772ca7b16cd86e0d50c7ed8d537c202976a6d2)
Diffstat (limited to 'source3')
-rw-r--r-- | source3/Makefile.in | 2 | ||||
-rw-r--r-- | source3/include/smbprofile.h | 3 | ||||
-rw-r--r-- | source3/lib/sendfile.c | 158 | ||||
-rw-r--r-- | source3/smbd/reply.c | 103 | ||||
-rw-r--r-- | source3/smbd/vfs-wrap.c | 12 |
5 files changed, 261 insertions, 17 deletions
diff --git a/source3/Makefile.in b/source3/Makefile.in index 0a28937fc9..87ee4ef540 100644 --- a/source3/Makefile.in +++ b/source3/Makefile.in @@ -126,7 +126,7 @@ TDB_OBJ = $(TDBBASE_OBJ) tdb/tdbutil.o LIB_OBJ = lib/charcnv.o lib/debug.o lib/fault.o \ lib/getsmbpass.o lib/interface.o lib/md4.o \ lib/interfaces.o lib/pidfile.o lib/replace.o \ - lib/signal.o lib/system.o lib/time.o \ + lib/signal.o lib/system.o lib/sendfile.o lib/time.o \ lib/ufc.o lib/genrand.o lib/username.o \ lib/util_getent.o lib/util_pw.o lib/access.o lib/smbrun.o \ lib/bitmap.o lib/crc32.o lib/snprintf.o lib/dprintf.o \ diff --git a/source3/include/smbprofile.h b/source3/include/smbprofile.h index 82017cee85..870fef71e3 100644 --- a/source3/include/smbprofile.h +++ b/source3/include/smbprofile.h @@ -65,6 +65,9 @@ struct profile_stats { unsigned syscall_write_bytes; /* bytes written with write syscall */ unsigned syscall_lseek_count; unsigned syscall_lseek_time; + unsigned syscall_sendfile_count; + unsigned syscall_sendfile_time; + unsigned syscall_sendfile_bytes; /* bytes read with sendfile syscall */ unsigned syscall_rename_count; unsigned syscall_rename_time; unsigned syscall_fsync_count; diff --git a/source3/lib/sendfile.c b/source3/lib/sendfile.c new file mode 100644 index 0000000000..33282e704a --- /dev/null +++ b/source3/lib/sendfile.c @@ -0,0 +1,158 @@ +/* + Unix SMB/Netbios implementation. + Version 2.2.x / 3.0.x + sendfile implementations. + Copyright (C) Jeremy Allison 2002. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + * This file handles the OS dependent sendfile implementations. + * The API is such that it returns -1 on error, else returns the + * number of bytes written. + */ + +#include "includes.h" + +#if defined(LINUX_SENDFILE_API) + +#include <sys/sendfile.h> + +#ifndef MSG_MORE +#define MSG_MORE 0x8000 +#endif + +ssize_t sys_sendfile(int outfd, int infd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count) +{ + size_t total=0; + ssize_t ret; + + /* + * Send the header first. + * Use MSG_MORE to cork the TCP output until sendfile is called. + */ + + while (total < header->length) { + ret = sys_send(outfd, header->data + total,header->length - total, MSG_MORE); + if (ret == -1) + return -1; + total += ret; + } + + total = count; + while (total) { + ssize_t nwritten; + do { +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(SENDFILE64) + nwritten = sendfile64(outfd, infd, &offset, total); +#else + nwritten = sendfile(outfd, infd, &offset, total); +#endif + } while (nwritten == -1 && errno == EINTR); + if (nwritten == -1) + return -1; + if (nwritten == 0) + return -1; /* I think we're at EOF here... */ + total -= nwritten; + } + return count + header->length; +} + +#elif defined(SOLARIS_SENDFILE_API) + +ssize_t sys_sendfile(int outfd, int infd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count) +{ +} + +#elif defined(HPUX_SENDFILE_API) + +#include <sys/socket.h> +#include <sys/uio.h> + +ssize_t sys_sendfile(int outfd, int infd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count) +{ + size_t total=0; + struct iovec hdtrl[2]; + + /* Set up the header/trailer iovec. */ + hdtrl[0].iov_base = header->data; + hdtrl[0].iov_len = header->length; + hdtrl[1].iov_base = NULL; + hdtrl[1].iov_base = 0; + + total = count; + while (total) { + ssize_t nwritten; + do { +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(SENDFILE64) + nwritten = sendfile64(outfd, infd, &offset, total, &hdtrl, 0); +#else + nwritten = sendfile(outfd, infd, &offset, total, &hdtrl, 0); +#endif + } while (nwritten == -1 && errno == EINTR); + if (nwritten == -1) + return -1; + total -= nwritten; + } + return count + header->length; +} + +#elif defined(FREEBSD_SENDFILE_API) + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/uio.h> + +ssize_t sys_sendfile(int outfd, int infd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count) +{ + size_t total=0; + struct sf_hdtr hdr; + struct iovec hdtrl; + + hdr->headers = &hdtrl; + hdr->hdr_cnt = 1; + hdr->trailers = NULL; + hdr->trl_cnt = 0; + + /* Set up the header iovec. */ + hdtrl.iov_base = header->data; + hdtrl.iov_len = header->length; + + total = count; + while (total) { + ssize_t nwritten; + do { +#if defined(HAVE_EXPLICIT_LARGEFILE_SUPPORT) && defined(HAVE_OFF64_T) && defined(SENDFILE64) + nwritten = sendfile64(outfd, infd, &offset, total, &hdr, NULL, 0); +#else + nwritten = sendfile(outfd, infd, &offset, total, &hdr, NULL, 0); +#endif + } while (nwritten == -1 && errno == EINTR); + if (nwritten == -1) + return -1; + total -= nwritten; + } + return count + header->length; +} + +#else /* No sendfile implementation. Return error. */ + +ssize_t sys_sendfile(int outfd, int infd, const DATA_BLOB *header, SMB_OFF_T offset, size_t count) +{ + /* No sendfile syscall. */ + errno = ENOSYS; + return -1; +} +#endif diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index c3a4c5a860..02aa53807c 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -1683,6 +1683,85 @@ int reply_read(connection_struct *conn, char *inbuf,char *outbuf, int size, int } /**************************************************************************** + Reply to a read and X - possibly using sendfile. +****************************************************************************/ + +int send_file_readX(connection_struct *conn, char *inbuf,char *outbuf,int length, + files_struct *fsp, SMB_OFF_T startpos, size_t smb_maxcnt) +{ + ssize_t nread = -1; + char *data = smb_buf(outbuf); + +#if defined(WITH_SENDFILE) && defined(HAVE_SENDFILE) + /* + * We can only use sendfile on a non-chained packet and on a file + * that is exclusively oplocked. + */ + + if ((CVAL(inbuf,smb_vwv0) == 0xFF) && EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type)) { + SMB_STRUCT_STAT sbuf; + DATA_BLOB header; + + if(vfs_fstat(fsp,fsp->fd, &sbuf) == -1) + return(UNIXERROR(ERRDOS,ERRnoaccess)); + + if (startpos > sbuf.st_size) + goto normal_read; + + if (smb_maxcnt > (sbuf.st_size - startpos)) + smb_maxcnt = (sbuf.st_size - startpos); + + if (smb_maxcnt == 0) + goto normal_read; + + /* + * Set up the packet header before send. We + * assume here the sendfile will work (get the + * correct amount of data). + */ + + SSVAL(outbuf,smb_vwv5,smb_maxcnt); + SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf)); + SSVAL(smb_buf(outbuf),-2,smb_maxcnt); + CVAL(outbuf,smb_vwv0) = 0xFF; + set_message(outbuf,12,smb_maxcnt,False); + header.data = outbuf; + header.length = data - outbuf; + header.free = NULL; + + if ( conn->vfs_ops.sendfile( smbd_server_fd(), fsp, fsp->fd, &header, startpos, smb_maxcnt) == -1) { + DEBUG(0,("send_file_readX: sendfile failed for file %s (%s). Terminating\n", + fsp->fsp_name, strerror(errno) )); + exit_server("send_file_readX sendfile failed"); + } + + DEBUG( 3, ( "send_file_readX: sendfile fnum=%d max=%d nread=%d\n", + fsp->fnum, (int)smb_maxcnt, (int)nread ) ); + return -1; + } + + normal_read: + +#endif + + nread = read_file(fsp,data,startpos,smb_maxcnt); + + if (nread < 0) { + END_PROFILE(SMBreadX); + return(UNIXERROR(ERRDOS,ERRnoaccess)); + } + + SSVAL(outbuf,smb_vwv5,nread); + SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf)); + SSVAL(smb_buf(outbuf),-2,nread); + + DEBUG( 3, ( "send_file_readX fnum=%d max=%d nread=%d\n", + fsp->fnum, (int)smb_maxcnt, (int)nread ) ); + + return nread; +} + +/**************************************************************************** Reply to a read and X. ****************************************************************************/ @@ -1690,10 +1769,12 @@ int reply_read_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt { files_struct *fsp = file_fsp(inbuf,smb_vwv2); SMB_OFF_T startpos = IVAL(inbuf,smb_vwv3); + ssize_t nread = -1; size_t smb_maxcnt = SVAL(inbuf,smb_vwv5); +#if 0 size_t smb_mincnt = SVAL(inbuf,smb_vwv6); - ssize_t nread = -1; - char *data; +#endif + START_PROFILE(SMBreadX); /* If it's an IPC, pass off the pipe handler. */ @@ -1706,7 +1787,6 @@ int reply_read_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt CHECK_READ(fsp); set_message(outbuf,12,0,True); - data = smb_buf(outbuf); if(CVAL(inbuf,smb_wct) == 12) { #ifdef LARGE_SMB_OFF_T @@ -1736,22 +1816,13 @@ int reply_read_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt END_PROFILE(SMBreadX); return ERROR_DOS(ERRDOS,ERRlock); } - nread = read_file(fsp,data,startpos,smb_maxcnt); - if (nread < 0) { - END_PROFILE(SMBreadX); - return(UNIXERROR(ERRDOS,ERRnoaccess)); - } - - SSVAL(outbuf,smb_vwv5,nread); - SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf)); - SSVAL(smb_buf(outbuf),-2,nread); - - DEBUG( 3, ( "readX fnum=%d min=%d max=%d nread=%d\n", - fsp->fnum, (int)smb_mincnt, (int)smb_maxcnt, (int)nread ) ); + nread = send_file_readX(conn, inbuf, outbuf, length, fsp, startpos, smb_maxcnt); + if (nread != -1) + nread = chain_reply(inbuf,outbuf,length,bufsize); END_PROFILE(SMBreadX); - return chain_reply(inbuf,outbuf,length,bufsize); + return nread; } /**************************************************************************** diff --git a/source3/smbd/vfs-wrap.c b/source3/smbd/vfs-wrap.c index 6878a42b48..b44b778e09 100644 --- a/source3/smbd/vfs-wrap.c +++ b/source3/smbd/vfs-wrap.c @@ -192,6 +192,18 @@ SMB_OFF_T vfswrap_lseek(files_struct *fsp, int filedes, SMB_OFF_T offset, int wh return result; } +#if 0 /* JRATEST */ +ssize_t vfswrap_sendfile(int outfd, struct files_struct *fsp, int infd, DATA_BLOB *hdr, SMB_OFF_T offset, size_t n) +{ + ssize_t result; + + START_PROFILE_BYTES(syscall_sendfile, n); + result = sys_sendfile(outfd, fsp, infd, hdr, offset, n); + END_PROFILE(syscall_sendfile); + return result; +} +#endif + int vfswrap_rename(connection_struct *conn, const char *old, const char *new) { int result; |