summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2002-09-12 02:12:52 +0000
committerJeremy Allison <jra@samba.org>2002-09-12 02:12:52 +0000
commitfd772ca7b16cd86e0d50c7ed8d537c202976a6d2 (patch)
tree8d455b2c4e8fce4913773af0e7886e0a51519b39
parentf75d61b03a3377f3a791b56fc307dc7e56e4707a (diff)
downloadsamba-fd772ca7b16cd86e0d50c7ed8d537c202976a6d2.tar.gz
samba-fd772ca7b16cd86e0d50c7ed8d537c202976a6d2.tar.xz
samba-fd772ca7b16cd86e0d50c7ed8d537c202976a6d2.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.
-rw-r--r--source/Makefile.in2
-rw-r--r--source/include/smbprofile.h3
-rw-r--r--source/lib/sendfile.c158
-rw-r--r--source/smbd/reply.c103
-rw-r--r--source/smbd/vfs-wrap.c12
5 files changed, 261 insertions, 17 deletions
diff --git a/source/Makefile.in b/source/Makefile.in
index 0a28937fc90..87ee4ef540d 100644
--- a/source/Makefile.in
+++ b/source/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/source/include/smbprofile.h b/source/include/smbprofile.h
index 82017cee854..870fef71e3e 100644
--- a/source/include/smbprofile.h
+++ b/source/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/source/lib/sendfile.c b/source/lib/sendfile.c
new file mode 100644
index 00000000000..33282e704a5
--- /dev/null
+++ b/source/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/source/smbd/reply.c b/source/smbd/reply.c
index c3a4c5a8603..02aa53807c6 100644
--- a/source/smbd/reply.c
+++ b/source/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/source/smbd/vfs-wrap.c b/source/smbd/vfs-wrap.c
index 6878a42b485..b44b778e09d 100644
--- a/source/smbd/vfs-wrap.c
+++ b/source/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;