First cut at portable sendfile code. Only used in readX at the moment
authorJeremy Allison <jra@samba.org>
Thu, 12 Sep 2002 02:12:52 +0000 (02:12 +0000)
committerJeremy Allison <jra@samba.org>
Thu, 12 Sep 2002 02:12:52 +0000 (02:12 +0000)
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.

source/Makefile.in
source/include/smbprofile.h
source/lib/sendfile.c [new file with mode: 0644]
source/smbd/reply.c
source/smbd/vfs-wrap.c

index 0a28937fc90b3837dd14a63b8ff280c76812d59e..87ee4ef540d85639f7a585e6b775853bda1ffa57 100644 (file)
@@ -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 \
index 82017cee85400be8173ef1c57328ba43e4a2bb65..870fef71e3e18ea81d119a01c70a98f5ed6d0398 100644 (file)
@@ -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 (file)
index 0000000..33282e7
--- /dev/null
@@ -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
index c3a4c5a8603a37a0fd79b3357ed425603bc499f6..02aa53807c6c2d64cdb950d3f68a83affa96b2ef 100644 (file)
@@ -1682,6 +1682,85 @@ int reply_read(connection_struct *conn, char *inbuf,char *outbuf, int size, int
        return(outsize);
 }
 
+/****************************************************************************
+ 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;
 }
 
 /****************************************************************************
index 6878a42b485f57a7af3d98496d5afaa832f9778e..b44b778e09dcd3f8b0923a6271d8e6cc64c4667a 100644 (file)
@@ -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;