summaryrefslogtreecommitdiff
path: root/source3/smbd
diff options
context:
space:
mode:
authorJeremy Allison <jra@samba.org>2007-05-16 00:07:38 +0000
committerGerald (Jerry) Carter <jerry@samba.org>2007-10-10 12:22:08 -0500
commit32106b23951e01fb17f814584ebbcc8d7288cb75 (patch)
treee14389fd1be0e7dae592d74f8c8518dd9a1b60a0 /source3/smbd
parent074af4b39d12312fc376e8d2f7bf84f0fd830874 (diff)
downloadsamba-32106b23951e01fb17f814584ebbcc8d7288cb75.tar.gz
samba-32106b23951e01fb17f814584ebbcc8d7288cb75.tar.bz2
samba-32106b23951e01fb17f814584ebbcc8d7288cb75.zip
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)
Diffstat (limited to 'source3/smbd')
-rw-r--r--source3/smbd/reply.c169
-rw-r--r--source3/smbd/trans2.c5
2 files changed, 111 insertions, 63 deletions
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.
@@ -2526,16 +2529,54 @@ Returning short read of maximum allowed for compatibility with Windows 2000.\n",
}
/****************************************************************************
+ 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.
****************************************************************************/
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: