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/smbd/reply.c | 169 ++++++++++++++++++++++++++++++++------------------ source3/smbd/trans2.c | 5 +- 2 files changed, 111 insertions(+), 63 deletions(-) (limited to 'source3/smbd') 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