r22920: Add in the UNIX capability for 24-bit readX, as discussed
authorJeremy Allison <jra@samba.org>
Wed, 16 May 2007 00:07:38 +0000 (00:07 +0000)
committerGerald (Jerry) Carter <jerry@samba.org>
Wed, 10 Oct 2007 17:22:08 +0000 (12:22 -0500)
with the Apple guys and Linux kernel guys. Still looking
at how to do writeX as there's no recvfile().
Jeremy.

source/include/client.h
source/include/smb_macros.h
source/include/trans2.h
source/lib/util_sock.c
source/libsmb/clientgen.c
source/libsmb/clifsinfo.c
source/libsmb/clireadwrite.c
source/libsmb/smb_signing.c
source/smbd/reply.c
source/smbd/trans2.c

index 4df2459fb229ddd58ed1a13f8c90ad9f663ef16b..741ce6470d90d9ab894a0cb17ecbbafd2fc777a3 100644 (file)
@@ -29,6 +29,7 @@
 #define CLI_BUFFER_SIZE (0xFFFF)
 #define CLI_SAMBA_MAX_LARGE_READX_SIZE (127*1024) /* Works for Samba servers */
 #define CLI_WINDOWS_MAX_LARGE_READX_SIZE ((64*1024)-2) /* Windows servers are broken.... */
+#define CLI_SAMBA_MAX_POSIX_LARGE_READX_SIZE (0xFFFF00) /* 24-bit len. */
 
 /*
  * These definitions depend on smb.h
@@ -152,6 +153,7 @@ struct cli_state {
        int win95;
        BOOL is_samba;
        uint32 capabilities;
+       uint32 posix_capabilities;
        BOOL dfsroot;
 
        TALLOC_CTX *mem_ctx;
index bfed27c16748480c8ccc810bbf27c834ec6bf99b..646c6bdce04c28e6872a3cbc6f8bc96eb88565dc 100644 (file)
 #define _smb_setlen(buf,len) do { buf[0] = 0; buf[1] = (len&0x10000)>>16; \
         buf[2] = (len&0xFF00)>>8; buf[3] = len&0xFF; } while (0)
 
+#define smb_len_large(buf) (PVAL(buf,3)|(PVAL(buf,2)<<8)|(PVAL(buf,1)<<16))
+#define _smb_setlen_large(buf,len) do { buf[0] = 0; buf[1] = (len&0xFF0000)>>16; \
+        buf[2] = (len&0xFF00)>>8; buf[3] = len&0xFF; } while (0)
+
 /*******************************************************************
 find the difference in milliseconds between two struct timeval
 values
index 5f7587d6ead59378dee98b08b0ea5c51cb7025a9..8b31b431c7a06c9dc629d017a0f9353f65fe2063 100644 (file)
@@ -529,8 +529,10 @@ findfirst/findnext is SMB_FIND_FILE_UNIX_INFO2.
                                                (chflags) and lsattr */
 #define CIFS_UNIX_POSIX_PATHNAMES_CAP     0x10 /* Use POSIX pathnames on the wire. */
 #define CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP       0x20 /* We can cope with POSIX open/mkdir/unlink etc. */
-#define CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP     0x40 /* We can do SPNEGO negotiations for encryption. */
-#define CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP   0x80 /* We *must* SPNEGO negotiations for encryption. */
+#define CIFS_UNIX_LARGE_READ_CAP           0x40 /* We can cope with 24 bit reads in readX. */
+#define CIFS_UNIX_LARGE_WRITE_CAP          0x80 /* We can cope with 24 bit writes in writeX. */
+#define CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP     0x100 /* We can do SPNEGO negotiations for encryption. */
+#define CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP   0x200 /* We *must* SPNEGO negotiations for encryption. */
 
 #define SMB_QUERY_POSIX_FS_INFO     0x201
 
index 46bb70952165127a331a7d960e58ef7db3e7a5a7..8f32e47bb8ef064ee96a7bbb3d1cae10910e1d87 100644 (file)
@@ -658,10 +658,12 @@ ssize_t read_smb_length(int fd, char *inbuf, unsigned int timeout)
  BUFFER_SIZE+SAFETY_MARGIN.
  The timeout is in milliseconds. 
  This function will return on receipt of a session keepalive packet.
+ maxlen is the max number of bytes to return, not including the 4 byte
+ length. If zero it means BUFFER_SIZE+SAFETY_MARGIN limit.
  Doesn't check the MAC on signed packets.
 ****************************************************************************/
 
-BOOL receive_smb_raw(int fd, char *buffer, unsigned int timeout)
+ssize_t receive_smb_raw(int fd, char *buffer, unsigned int timeout, size_t maxlen)
 {
        ssize_t len,ret;
 
@@ -679,7 +681,7 @@ BOOL receive_smb_raw(int fd, char *buffer, unsigned int timeout)
 
                if (smb_read_error == 0)
                        smb_read_error = READ_ERROR;
-               return False;
+               return -1;
        }
 
        /*
@@ -699,11 +701,15 @@ BOOL receive_smb_raw(int fd, char *buffer, unsigned int timeout)
 
                        if (smb_read_error == 0)
                                smb_read_error = READ_ERROR;
-                       return False;
+                       return -1;
                }
        }
 
        if(len > 0) {
+               if (maxlen) {
+                       len = MIN(len,maxlen);
+               }
+
                if (timeout > 0) {
                        ret = read_socket_with_timeout(fd,buffer+4,len,len,timeout);
                } else {
@@ -714,7 +720,7 @@ BOOL receive_smb_raw(int fd, char *buffer, unsigned int timeout)
                        if (smb_read_error == 0) {
                                smb_read_error = READ_ERROR;
                        }
-                       return False;
+                       return -1;
                }
                
                /* not all of samba3 properly checks for packet-termination of strings. This
@@ -722,7 +728,7 @@ BOOL receive_smb_raw(int fd, char *buffer, unsigned int timeout)
                SSVAL(buffer+4,len, 0);
        }
 
-       return True;
+       return len;
 }
 
 /****************************************************************************
@@ -732,7 +738,7 @@ BOOL receive_smb_raw(int fd, char *buffer, unsigned int timeout)
 
 BOOL receive_smb(int fd, char *buffer, unsigned int timeout)
 {
-       if (!receive_smb_raw(fd, buffer, timeout)) {
+       if (!receive_smb_raw(fd, buffer, timeout, 0)) {
                return False;
        }
 
index e1dacb3921697db10a8b0c7558f49055e72fed89..1a4b1f770f50a85040f4b0eebc7f5b6e3e1b447e 100644 (file)
@@ -2,6 +2,7 @@
    Unix SMB/CIFS implementation.
    SMB client generic functions
    Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Jeremy Allison 2007.
    
    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
@@ -54,20 +55,20 @@ int cli_set_port(struct cli_state *cli, int port)
  should never go into a blocking read.
 ****************************************************************************/
 
-static BOOL client_receive_smb(struct cli_state *cli, BOOL eat_keepalives)
+static ssize_t client_receive_smb(struct cli_state *cli, BOOL eat_keepalives, size_t maxlen)
 {
-       BOOL ret;
+       ssize_t len;
        int fd = cli->fd;
        char *buffer = cli->inbuf;
        unsigned int timeout = cli->timeout;
 
        for(;;) {
-               ret = receive_smb_raw(fd, buffer, timeout);
+               len = receive_smb_raw(fd, buffer, timeout, maxlen);
 
-               if (!ret) {
+               if (len < 0) {
                        DEBUG(10,("client_receive_smb failed\n"));
                        show_msg(buffer);
-                       return ret;
+                       return len;
                }
 
                /* Ignore session keepalive packets. */
@@ -85,11 +86,11 @@ static BOOL client_receive_smb(struct cli_state *cli, BOOL eat_keepalives)
                        cli->smb_rw_error = READ_BAD_DECRYPT;
                        close(cli->fd);
                        cli->fd = -1;
-                       return False;
+                       return -1;
                }
        }
        show_msg(buffer);
-       return ret;
+       return len;
 }
 
 /****************************************************************************
@@ -98,21 +99,21 @@ static BOOL client_receive_smb(struct cli_state *cli, BOOL eat_keepalives)
 
 BOOL cli_receive_smb_internal(struct cli_state *cli, BOOL eat_keepalives)
 {
-       BOOL ret;
+       ssize_t len;
 
        /* fd == -1 causes segfaults -- Tom (tom@ninja.nl) */
        if (cli->fd == -1)
                return False; 
 
  again:
-       ret = client_receive_smb(cli, eat_keepalives);
+       len = client_receive_smb(cli, eat_keepalives, 0);
 
-       if (ret && !eat_keepalives && (CVAL(cli->inbuf,0) == SMBkeepalive)) {
+       if (len >= 0 && !eat_keepalives && (CVAL(cli->inbuf,0) == SMBkeepalive)) {
                /* Give back the keepalive. */
                return True;
        }
        
-       if (ret) {
+       if (len > 0) {
                /* it might be an oplock break request */
                if (!(CVAL(cli->inbuf, smb_flg) & FLAG_REPLY) &&
                    CVAL(cli->inbuf,smb_com) == SMBlockingX &&
@@ -121,7 +122,9 @@ BOOL cli_receive_smb_internal(struct cli_state *cli, BOOL eat_keepalives)
                        if (cli->oplock_handler) {
                                int fnum = SVAL(cli->inbuf,smb_vwv2);
                                unsigned char level = CVAL(cli->inbuf,smb_vwv3+1);
-                               if (!cli->oplock_handler(cli, fnum, level)) return False;
+                               if (!cli->oplock_handler(cli, fnum, level)) {
+                                       return False;
+                               }
                        }
                        /* try to prevent loops */
                        SCVAL(cli->inbuf,smb_com,0xFF);
@@ -130,12 +133,12 @@ BOOL cli_receive_smb_internal(struct cli_state *cli, BOOL eat_keepalives)
        }
 
        /* If the server is not responding, note that now */
-       if (!ret) {
+       if (len <= 0) {
                 DEBUG(0, ("Receiving SMB: Server stopped responding\n"));
                cli->smb_rw_error = smb_read_error;
                close(cli->fd);
                cli->fd = -1;
-               return ret;
+               return False;
        }
 
        if (!cli_check_sign_mac(cli)) {
@@ -187,6 +190,102 @@ BOOL cli_receive_smb_return_keepalive(struct cli_state *cli)
        return cli_receive_smb_internal(cli, False);
 }
 
+/****************************************************************************
+ Read the data portion of a readX smb.
+ The timeout is in milliseconds
+****************************************************************************/
+
+ssize_t cli_receive_smb_data(struct cli_state *cli, char *buffer, size_t len)
+{
+       if (cli->timeout > 0) {
+               return read_socket_with_timeout(cli->fd, buffer, len, len, cli->timeout);
+       } else {
+               return read_data(cli->fd, buffer, len);
+       }
+}
+
+/****************************************************************************
+ Read a smb readX header.
+ We can only use this if encryption and signing are off.
+****************************************************************************/
+
+BOOL cli_receive_smb_readX_header(struct cli_state *cli)
+{
+       ssize_t len, offset;
+
+       if (cli->fd == -1)
+               return False; 
+
+ again:
+
+       /* Read up to the size of a readX header reply. */
+       len = client_receive_smb(cli, True, (smb_size - 4) + 24);
+       
+       if (len > 0) {
+               /* it might be an oplock break request */
+               if (!(CVAL(cli->inbuf, smb_flg) & FLAG_REPLY) &&
+                   CVAL(cli->inbuf,smb_com) == SMBlockingX &&
+                   SVAL(cli->inbuf,smb_vwv6) == 0 &&
+                   SVAL(cli->inbuf,smb_vwv7) == 0) {
+                       ssize_t total_len = smb_len(cli->inbuf);
+
+                       if (total_len > CLI_SAMBA_MAX_LARGE_READX_SIZE+SAFETY_MARGIN) {
+                               goto read_err;
+                       }
+
+                       /* Read the rest of the data. */
+                       if (!cli_receive_smb_data(cli,cli->inbuf+len,total_len - len)) {
+                               goto read_err;
+                       }
+
+                       if (cli->oplock_handler) {
+                               int fnum = SVAL(cli->inbuf,smb_vwv2);
+                               unsigned char level = CVAL(cli->inbuf,smb_vwv3+1);
+                               if (!cli->oplock_handler(cli, fnum, level)) return False;
+                       }
+                       /* try to prevent loops */
+                       SCVAL(cli->inbuf,smb_com,0xFF);
+                       goto again;
+               }
+       }
+
+       /* Check it's a non-chained readX reply. */
+       if (!(CVAL(cli->inbuf, smb_flg) & FLAG_REPLY) ||
+               (CVAL(cli->inbuf,smb_vwv0) != 0xFF) ||
+               (CVAL(cli->inbuf,smb_com) != SMBreadX)) {
+               /* 
+                * We're not coping here with asnyc replies to
+                * other calls. Punt here - we need async client
+                * libs for this.
+                */
+               goto read_err;
+       }
+
+       /* 
+        * We know it's a readX reply - ensure we've read the
+        * padding bytes also.
+        */
+
+       offset = SVAL(cli->inbuf,smb_vwv6);
+       if (offset > len) {
+               ssize_t ret;
+               size_t padbytes = offset - len;
+               ret = cli_receive_smb_data(cli,smb_buf(cli->inbuf),padbytes);
+                if (ret != padbytes) {
+                       goto read_err;
+               }
+       }
+
+       return True;
+
+  read_err:
+
+       cli->smb_rw_error = smb_read_error = READ_ERROR;
+       close(cli->fd);
+       cli->fd = -1;
+       return False;
+}
+
 static ssize_t write_socket(int fd, const char *buf, size_t len)
 {
         ssize_t ret=0;
index d8ada1a896dd625d6b33c2addf185403516a9a37..28facb511df21aa092d98e92c7b7607435b3cb5d 100644 (file)
@@ -66,7 +66,7 @@ BOOL cli_unix_extensions_version(struct cli_state *cli, uint16 *pmajor, uint16 *
 
        *pmajor = SVAL(rdata,0);
        *pminor = SVAL(rdata,2);
-       *pcaplow = IVAL(rdata,4);
+       cli->posix_capabilities = *pcaplow = IVAL(rdata,4);
        *pcaphigh = IVAL(rdata,8);
 
        /* todo: but not yet needed 
index 1c72cb2942c386e635ef6364a891b03bb35ae2f2..35d2afe2e77339205990e0bd7446743903ee390f 100644 (file)
@@ -46,7 +46,7 @@ static BOOL cli_issue_read(struct cli_state *cli, int fnum, off_t offset,
        SIVAL(cli->outbuf,smb_vwv3,offset);
        SSVAL(cli->outbuf,smb_vwv5,size);
        SSVAL(cli->outbuf,smb_vwv6,size);
-       SSVAL(cli->outbuf,smb_vwv7,((size >> 16) & 1));
+       SSVAL(cli->outbuf,smb_vwv7,(size >> 16));
        SSVAL(cli->outbuf,smb_mid,cli->mid + i);
 
        if (bigoffset) {
@@ -63,9 +63,11 @@ static BOOL cli_issue_read(struct cli_state *cli, int fnum, off_t offset,
 ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_t size)
 {
        char *p;
-       int size2;
-       int readsize;
+       size_t size2;
+       size_t readsize;
        ssize_t total = 0;
+       /* We can only do direct reads if not signing. */
+       BOOL direct_reads = !client_is_signing_on(cli);
 
        if (size == 0) 
                return 0;
@@ -75,7 +77,11 @@ ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_
         * rounded down to a multiple of 1024.
         */
 
-       if (cli->capabilities & CAP_LARGE_READX) {
+       if (client_is_signing_on(cli) == False &&
+                       cli_encryption_on(cli) == False &&
+                       (cli->posix_capabilities & CIFS_UNIX_LARGE_READ_CAP)) {
+               readsize = CLI_SAMBA_MAX_POSIX_LARGE_READX_SIZE;
+       } else if (cli->capabilities & CAP_LARGE_READX) {
                if (cli->is_samba) {
                        readsize = CLI_SAMBA_MAX_LARGE_READX_SIZE;
                } else {
@@ -93,8 +99,13 @@ ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_
                if (!cli_issue_read(cli, fnum, offset, readsize, 0))
                        return -1;
 
-               if (!cli_receive_smb(cli))
-                       return -1;
+               if (direct_reads) {
+                       if (!cli_receive_smb_readX_header(cli))
+                               return -1;
+               } else {
+                       if (!cli_receive_smb(cli))
+                               return -1;
+               }
 
                /* Check for error.  Make sure to check for DOS and NT
                    errors. */
@@ -125,7 +136,7 @@ ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_
                }
 
                size2 = SVAL(cli->inbuf, smb_vwv5);
-               size2 |= (((unsigned int)(SVAL(cli->inbuf, smb_vwv7) & 1)) << 16);
+               size2 |= (((unsigned int)(SVAL(cli->inbuf, smb_vwv7))) << 16);
 
                if (size2 > readsize) {
                        DEBUG(5,("server returned more than we wanted!\n"));
@@ -135,10 +146,29 @@ ssize_t cli_read(struct cli_state *cli, int fnum, char *buf, off_t offset, size_
                        return -1;
                }
 
-               /* Copy data into buffer */
+               if (!direct_reads) {
+                       /* Copy data into buffer */
+                       p = smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_vwv6);
+                       memcpy(buf + total, p, size2);
+               } else {
+                       /* Ensure the remaining data matches the return size. */
+                       ssize_t toread = smb_len_large(cli->inbuf) - SVAL(cli->inbuf,smb_vwv6);
 
-               p = smb_base(cli->inbuf) + SVAL(cli->inbuf,smb_vwv6);
-               memcpy(buf + total, p, size2);
+                       /* Ensure the size is correct. */
+                       if (toread != size2) {
+                               DEBUG(5,("direct read logic fail toread (%d) != size2 (%u)\n",
+                                       (int)toread, (unsigned int)size2 ));
+                               return -1;
+                       }
+
+                       /* Read data directly into buffer */
+                       toread = cli_receive_smb_data(cli,buf+total,size2);
+                       if (toread != size2) {
+                               DEBUG(5,("direct read read failure toread (%d) != size2 (%u)\n",
+                                       (int)toread, (unsigned int)size2 ));
+                               return -1;
+                       }
+               }
 
                total += size2;
                offset += size2;
index d3384ce365a4028180cfb78fc66ec6db3eadd655..30f41476e3a49fb3c54f3ad21bf885e8d58d13bf 100644 (file)
@@ -657,6 +657,16 @@ BOOL client_set_trans_sign_state_off(struct cli_state *cli, uint16 mid)
        return True;
 }
 
+/***********************************************************
+ Is client signing on ?
+************************************************************/
+
+BOOL client_is_signing_on(struct cli_state *cli)
+{
+       struct smb_sign_info *si = &cli->sign_info;
+       return si->doing_signing;
+}
+
 /***********************************************************
  SMB signing - Server implementation - send the MAC.
 ************************************************************/
index 5353e392b1bf703f5048ed94b7ffd69fba010295..4285e0ea7798c5b3e9c4144f5af1efba5f619414 100644 (file)
@@ -2149,39 +2149,42 @@ static void fail_readraw(void)
        exit_server_cleanly(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)
+static ssize_t fake_sendfile(files_struct *fsp, SMB_OFF_T startpos, size_t nread, char *buf, size_t bufsize)
 {
-       ssize_t ret=0;
+       size_t tosend = nread;
 
-       /* Paranioa check... */
-       if (nread > bufsize) {
-               fail_readraw();
-       }
+       while (tosend > 0) {
+               ssize_t ret;
+               size_t cur_read;
 
-       if (nread > 0) {
-               ret = read_file(fsp,buf,startpos,nread);
+               if (tosend > bufsize) {
+                       cur_read = bufsize;
+               } else {
+                       cur_read = tosend;
+               }
+               ret = read_file(fsp,buf,startpos,cur_read);
                if (ret == -1) {
                        return -1;
                }
-       }
 
-       /* If we had a short read, fill with zeros. */
-       if (ret < nread) {
-               memset(buf, '\0', nread - ret);
-       }
+               /* If we had a short read, fill with zeros. */
+               if (ret < cur_read) {
+                       memset(buf, '\0', cur_read - ret);
+               }
 
-       if (write_data(smbd_server_fd(),buf,nread) != nread) {
-               return -1;
-       }       
+               if (write_data(smbd_server_fd(),buf,cur_read) != cur_read) {
+                       return -1;
+               }
+               tosend -= cur_read;
+               startpos += cur_read;
+       }
 
        return (ssize_t)nread;
 }
-#endif
 
 /****************************************************************************
  Use sendfile in readbraw.
@@ -2525,6 +2528,27 @@ Returning short read of maximum allowed for compatibility with Windows 2000.\n",
        return(outsize);
 }
 
+/****************************************************************************
+ Setup readX header.
+****************************************************************************/
+
+static int setup_readX_header(char *inbuf, char *outbuf, size_t smb_maxcnt)
+{
+       int outsize;
+       char *data = smb_buf(outbuf);
+
+       SSVAL(outbuf,smb_vwv2,0xFFFF); /* Remaining - must be -1. */
+       SSVAL(outbuf,smb_vwv5,smb_maxcnt);
+       SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf));
+       SSVAL(outbuf,smb_vwv7,(smb_maxcnt >> 16));
+       SSVAL(smb_buf(outbuf),-2,smb_maxcnt);
+       SCVAL(outbuf,smb_vwv0,0xFF);
+       outsize = set_message(inbuf, outbuf,12,smb_maxcnt,False);
+       /* Reset the outgoing length, set_message truncates at 0x1FFFF. */
+       _smb_setlen_large(outbuf,(smb_size + 12*2 + smb_maxcnt - 4));
+       return outsize;
+}
+
 /****************************************************************************
  Reply to a read and X - possibly using sendfile.
 ****************************************************************************/
@@ -2532,10 +2556,27 @@ Returning short read of maximum allowed for compatibility with Windows 2000.\n",
 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)
 {
+       SMB_STRUCT_STAT sbuf;
        int outsize = 0;
        ssize_t nread = -1;
        char *data = smb_buf(outbuf);
 
+       if(SMB_VFS_FSTAT(fsp,fsp->fh->fd, &sbuf) == -1) {
+               return(UNIXERROR(ERRDOS,ERRnoaccess));
+       }
+
+       if (startpos > sbuf.st_size) {
+               smb_maxcnt = 0;
+       }
+
+       if (smb_maxcnt > (sbuf.st_size - startpos)) {
+               smb_maxcnt = (sbuf.st_size - startpos);
+       }
+
+       if (smb_maxcnt == 0) {
+               goto normal_read;
+       }
+
 #if defined(WITH_SENDFILE)
        /*
         * We can only use sendfile on a non-chained packet 
@@ -2545,33 +2586,15 @@ int send_file_readX(connection_struct *conn, char *inbuf,char *outbuf,int length
 
        if ((chain_size == 0) && (CVAL(inbuf,smb_vwv0) == 0xFF) &&
            lp_use_sendfile(SNUM(conn)) && (fsp->wcp == NULL) ) {
-               SMB_STRUCT_STAT sbuf;
                DATA_BLOB header;
 
-               if(SMB_VFS_FSTAT(fsp,fsp->fh->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_vwv2,0xFFFF); /* Remaining - must be -1. */
-               SSVAL(outbuf,smb_vwv5,smb_maxcnt);
-               SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf));
-               SSVAL(outbuf,smb_vwv7,((smb_maxcnt >> 16) & 1));
-               SSVAL(smb_buf(outbuf),-2,smb_maxcnt);
-               SCVAL(outbuf,smb_vwv0,0xFF);
+               setup_readX_header(inbuf,outbuf,smb_maxcnt);
                set_message(inbuf,outbuf,12,smb_maxcnt,False);
                header.data = (uint8 *)outbuf;
                header.length = data - outbuf;
@@ -2621,24 +2644,41 @@ int send_file_readX(connection_struct *conn, char *inbuf,char *outbuf,int length
 
 #endif
 
-       nread = read_file(fsp,data,startpos,smb_maxcnt);
-  
-       if (nread < 0) {
-               return(UNIXERROR(ERRDOS,ERRnoaccess));
-       }
+       if ((smb_maxcnt && 0xFF0000) > 0x10000) {
+               int sendlen = setup_readX_header(inbuf,outbuf,smb_maxcnt) - smb_maxcnt;
+               /* Send out the header. */
+               if (write_data(smbd_server_fd(),outbuf,sendlen) != sendlen) {
+                       DEBUG(0,("send_file_readX: write_data failed for file %s (%s). Terminating\n",
+                               fsp->fsp_name, strerror(errno) ));
+                       exit_server_cleanly("send_file_readX sendfile failed");
+               }
+               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_cleanly("send_file_readX: fake_sendfile failed");
+               }
+               return -1;
+       } else {
+               nread = read_file(fsp,data,startpos,smb_maxcnt);
 
-       outsize = set_message(inbuf,outbuf,12,nread,False);
-       SSVAL(outbuf,smb_vwv2,0xFFFF); /* Remaining - must be -1. */
-       SSVAL(outbuf,smb_vwv5,nread);
-       SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf));
-       SSVAL(outbuf,smb_vwv7,((nread >> 16) & 1));
-       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 ) );
+               if (nread < 0) {
+                       return(UNIXERROR(ERRDOS,ERRnoaccess));
+               }
 
-       /* Returning the number of bytes we want to send back - including header. */
-       return outsize;
+               outsize = set_message(inbuf, outbuf,12,nread,False);
+               SSVAL(outbuf,smb_vwv2,0xFFFF); /* Remaining - must be -1. */
+               SSVAL(outbuf,smb_vwv5,nread);
+               SSVAL(outbuf,smb_vwv6,smb_offset(data,outbuf));
+               SSVAL(outbuf,smb_vwv7,((nread >> 16) & 1));
+               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 ) );
+
+               /* Returning the number of bytes we want to send back - including header. */
+               return outsize;
+       }
 }
 
 /****************************************************************************
@@ -2651,6 +2691,7 @@ int reply_read_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt
        SMB_OFF_T startpos = IVAL_TO_SMB_OFF_T(inbuf,smb_vwv3);
        ssize_t nread = -1;
        size_t smb_maxcnt = SVAL(inbuf,smb_vwv5);
+       BOOL big_readX = False;
 #if 0
        size_t smb_mincnt = SVAL(inbuf,smb_vwv6);
 #endif
@@ -2671,14 +2712,18 @@ int reply_read_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt
        set_message(inbuf,outbuf,12,0,True);
 
        if (global_client_caps & CAP_LARGE_READX) {
-               if (SVAL(inbuf,smb_vwv7) == 1) {
-                       smb_maxcnt |= (1<<16);
-               }
-               if (smb_maxcnt > BUFFER_SIZE) {
-                       DEBUG(0,("reply_read_and_X - read too large (%u) for reply buffer %u\n",
-                               (unsigned int)smb_maxcnt, (unsigned int)BUFFER_SIZE));
-                       END_PROFILE(SMBreadX);
-                       return ERROR_NT(NT_STATUS_INVALID_PARAMETER);
+               size_t upper_size = SVAL(inbuf,smb_vwv7);
+               smb_maxcnt |= (upper_size<<16);
+               if (upper_size > 1) {
+                       /* Can't do this on a chained packet. */
+                       if ((CVAL(inbuf,smb_vwv0) != 0xFF)) {
+                               return ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+                       }
+                       /* We currently don't do this on signed or sealed data. */
+                       if (srv_is_signing_active() || srv_encryption_on()) {
+                               return ERROR_NT(NT_STATUS_NOT_SUPPORTED);
+                       }
+                       big_readX = True;
                }
        }
 
@@ -2711,7 +2756,7 @@ int reply_read_and_X(connection_struct *conn, char *inbuf,char *outbuf,int lengt
                return ERROR_DOS(ERRDOS,ERRlock);
        }
 
-       if (schedule_aio_read_and_X(conn, inbuf, outbuf, length, bufsize, fsp, startpos, smb_maxcnt)) {
+       if (!big_readX && schedule_aio_read_and_X(conn, inbuf, outbuf, length, bufsize, fsp, startpos, smb_maxcnt)) {
                END_PROFILE(SMBreadX);
                return -1;
        }
index 1c5568433001751ba59fab64b2e48ecc35e166f2..0730041899f95013478b2c74fe20dbeb2ae6916e 100644 (file)
@@ -2556,7 +2556,10 @@ cBytesSector=%u, cUnitTotal=%u, cUnitAvail=%d\n", (unsigned int)bsize, (unsigned
                                        CIFS_UNIX_POSIX_PATHNAMES_CAP|
                                        CIFS_UNIX_FCNTL_LOCKS_CAP|
                                        CIFS_UNIX_EXTATTR_CAP|
-                                       CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP)));
+                                       CIFS_UNIX_POSIX_PATH_OPERATIONS_CAP|
+                                       /* Ensure we don't do this on signed or sealed data. */
+                                       (srv_is_signing_active() ? 0 : CIFS_UNIX_LARGE_READ_CAP)
+                                       )));
                        break;
 
                case SMB_QUERY_POSIX_FS_INFO: