summaryrefslogtreecommitdiff
path: root/source3/libsmb
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/libsmb
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/libsmb')
-rw-r--r--source3/libsmb/clientgen.c127
-rw-r--r--source3/libsmb/clifsinfo.c2
-rw-r--r--source3/libsmb/clireadwrite.c50
-rw-r--r--source3/libsmb/smb_signing.c10
4 files changed, 164 insertions, 25 deletions
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
@@ -658,6 +658,16 @@ BOOL client_set_trans_sign_state_off(struct cli_state *cli, uint16 mid)
}
/***********************************************************
+ 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.
************************************************************/