From 32106b23951e01fb17f814584ebbcc8d7288cb75 Mon Sep 17 00:00:00 2001 From: Jeremy Allison Date: Wed, 16 May 2007 00:07:38 +0000 Subject: r22920: Add in the UNIX capability for 24-bit readX, as discussed with the Apple guys and Linux kernel guys. Still looking at how to do writeX as there's no recvfile(). Jeremy. (This used to be commit a53268fb2082de586e2df250d8ddfcff53379102) --- source3/include/client.h | 2 + source3/include/smb_macros.h | 4 + source3/include/trans2.h | 6 +- source3/lib/util_sock.c | 18 +++-- source3/libsmb/clientgen.c | 127 +++++++++++++++++++++++++++---- source3/libsmb/clifsinfo.c | 2 +- source3/libsmb/clireadwrite.c | 50 ++++++++++--- source3/libsmb/smb_signing.c | 10 +++ source3/smbd/reply.c | 169 ++++++++++++++++++++++++++---------------- source3/smbd/trans2.c | 5 +- 10 files changed, 297 insertions(+), 96 deletions(-) diff --git a/source3/include/client.h b/source3/include/client.h index 4df2459fb2..741ce6470d 100644 --- a/source3/include/client.h +++ b/source3/include/client.h @@ -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; diff --git a/source3/include/smb_macros.h b/source3/include/smb_macros.h index bfed27c167..646c6bdce0 100644 --- a/source3/include/smb_macros.h +++ b/source3/include/smb_macros.h @@ -191,6 +191,10 @@ #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 diff --git a/source3/include/trans2.h b/source3/include/trans2.h index 5f7587d6ea..8b31b431c7 100644 --- a/source3/include/trans2.h +++ b/source3/include/trans2.h @@ -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 diff --git a/source3/lib/util_sock.c b/source3/lib/util_sock.c index 46bb709521..8f32e47bb8 100644 --- a/source3/lib/util_sock.c +++ b/source3/lib/util_sock.c @@ -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; } diff --git a/source3/libsmb/clientgen.c b/source3/libsmb/clientgen.c index e1dacb3921..1a4b1f770f 100644 --- a/source3/libsmb/clientgen.c +++ b/source3/libsmb/clientgen.c @@ -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; diff --git a/source3/libsmb/clifsinfo.c b/source3/libsmb/clifsinfo.c index d8ada1a896..28facb511d 100644 --- a/source3/libsmb/clifsinfo.c +++ b/source3/libsmb/clifsinfo.c @@ -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 diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c index 1c72cb2942..35d2afe2e7 100644 --- a/source3/libsmb/clireadwrite.c +++ b/source3/libsmb/clireadwrite.c @@ -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; diff --git a/source3/libsmb/smb_signing.c b/source3/libsmb/smb_signing.c index d3384ce365..30f41476e3 100644 --- a/source3/libsmb/smb_signing.c +++ b/source3/libsmb/smb_signing.c @@ -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. ************************************************************/ diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c index 5353e392b1..4285e0ea77 100644 --- a/source3/smbd/reply.c +++ b/source3/smbd/reply.c @@ -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; } diff --git a/source3/smbd/trans2.c b/source3/smbd/trans2.c index 1c55684330..0730041899 100644 --- a/source3/smbd/trans2.c +++ b/source3/smbd/trans2.c @@ -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: -- cgit