summaryrefslogtreecommitdiff
path: root/source3
diff options
context:
space:
mode:
Diffstat (limited to 'source3')
-rw-r--r--source3/include/smb.h1
-rw-r--r--source3/lib/recvfile.c40
-rw-r--r--source3/lib/util_sock.c106
-rw-r--r--source3/param/loadparm.c5
-rw-r--r--source3/smbd/blocking.c4
-rw-r--r--source3/smbd/fileio.c41
-rw-r--r--source3/smbd/notify.c2
-rw-r--r--source3/smbd/process.c62
-rw-r--r--source3/smbd/reply.c95
-rw-r--r--source3/smbd/server.c299
-rw-r--r--source3/smbd/vfs.c32
11 files changed, 527 insertions, 160 deletions
diff --git a/source3/include/smb.h b/source3/include/smb.h
index 4c51acf6f4..303f7606d3 100644
--- a/source3/include/smb.h
+++ b/source3/include/smb.h
@@ -675,6 +675,7 @@ struct smb_request {
uint8 wct;
const uint8 *inbuf;
uint8 *outbuf;
+ size_t unread_bytes;
};
/* Defines for the sent_oplock_break field above. */
diff --git a/source3/lib/recvfile.c b/source3/lib/recvfile.c
index cd9fb12716..9d77f94a29 100644
--- a/source3/lib/recvfile.c
+++ b/source3/lib/recvfile.c
@@ -125,13 +125,49 @@ static ssize_t default_sys_recvfile(int fromfd,
}
#if defined(HAVE_SPLICE_SYSCALL)
+
+#ifdef JRA_SPLICE_TEST
+#include <linux/unistd.h>
+#include <sys/syscall.h>
+
+#define __NR_splice 313
+_syscall6( long, splice,
+ int, fromfd,
+ loff_t *, fromoffset,
+ int, tofd,
+ loff_t *, tooffset,
+ size_t, count,
+ unsigned int, flags);
+#endif
+
ssize_t sys_recvfile(int fromfd,
int tofd,
SMB_OFF_T offset,
size_t count)
{
- errno = ENOSYS
- return -1;
+ size_t total = 0;
+
+ if (count == 0) {
+ return 0;
+ }
+
+ while (total < count) {
+ ssize_t ret = splice(fromfd,
+ NULL,
+ tofd,
+ &offset,
+ count,
+ 0);
+ if (ret == -1) {
+ if (errno != EINTR) {
+ return -1;
+ }
+ continue;
+ }
+ total += ret;
+ count -= ret;
+ }
+ return total;
}
#else
diff --git a/source3/lib/util_sock.c b/source3/lib/util_sock.c
index e66bd5f15a..bbcbcacb4a 100644
--- a/source3/lib/util_sock.c
+++ b/source3/lib/util_sock.c
@@ -1113,7 +1113,7 @@ bool send_keepalive(int client)
Timeout is in milliseconds.
****************************************************************************/
-static ssize_t read_smb_length_return_keepalive(int fd,
+ssize_t read_smb_length_return_keepalive(int fd,
char *inbuf,
unsigned int timeout)
{
@@ -1260,86 +1260,6 @@ ssize_t receive_smb_raw(int fd,
return len;
}
-static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, int fd,
- char **buffer, unsigned int timeout)
-{
- char lenbuf[4];
- ssize_t len,ret;
-
- smb_read_error = 0;
-
- len = read_smb_length_return_keepalive(fd, lenbuf, timeout);
- if (len < 0) {
- DEBUG(10,("receive_smb_raw: length < 0!\n"));
-
- /*
- * Correct fix. smb_read_error may have already been
- * set. Only set it here if not already set. Global
- * variables still suck :-). JRA.
- */
-
- if (smb_read_error == 0)
- smb_read_error = READ_ERROR;
- return -1;
- }
-
- /*
- * A WRITEX with CAP_LARGE_WRITEX can be 64k worth of data plus 65 bytes
- * of header. Don't print the error if this fits.... JRA.
- */
-
- if (len > (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE)) {
- DEBUG(0,("Invalid packet length! (%lu bytes).\n",
- (unsigned long)len));
- if (len > BUFFER_SIZE + (SAFETY_MARGIN/2)) {
-
- /*
- * Correct fix. smb_read_error may have already been
- * set. Only set it here if not already set. Global
- * variables still suck :-). JRA.
- */
-
- if (smb_read_error == 0)
- smb_read_error = READ_ERROR;
- return -1;
- }
- }
-
- /*
- * The +4 here can't wrap, we've checked the length above already.
- */
-
- *buffer = TALLOC_ARRAY(mem_ctx, char, len+4);
-
- if (*buffer == NULL) {
- DEBUG(0, ("Could not allocate inbuf of length %d\n",
- (int)len+4));
- if (smb_read_error == 0)
- smb_read_error = READ_ERROR;
- return -1;
- }
-
- memcpy(*buffer, lenbuf, sizeof(lenbuf));
-
- if(len > 0) {
- if (timeout > 0) {
- ret = read_socket_with_timeout(fd,(*buffer)+4, len,
- len, timeout);
- } else {
- ret = read_data(fd, (*buffer)+4, len);
- }
-
- if (ret != len) {
- if (smb_read_error == 0) {
- smb_read_error = READ_ERROR;
- }
- return -1;
- }
- }
-
- return len + 4;
-}
-
/****************************************************************************
Wrapper for receive_smb_raw().
Checks the MAC on signed packets.
@@ -1364,30 +1284,6 @@ bool receive_smb(int fd, char *buffer, unsigned int timeout)
return true;
}
-ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer,
- unsigned int timeout)
-{
- ssize_t len;
-
- len = receive_smb_raw_talloc(mem_ctx, fd, buffer, timeout);
-
- if (len < 0) {
- return -1;
- }
-
- /* Check the incoming SMB signature. */
- if (!srv_check_sign_mac(*buffer, true)) {
- DEBUG(0, ("receive_smb: SMB Signature verification failed on "
- "incoming packet!\n"));
- if (smb_read_error == 0) {
- smb_read_error = READ_BAD_SIG;
- }
- return -1;
- }
-
- return len;
-}
-
/****************************************************************************
Send an smb to a fd.
****************************************************************************/
diff --git a/source3/param/loadparm.c b/source3/param/loadparm.c
index dcec6bce89..a5b2647567 100644
--- a/source3/param/loadparm.c
+++ b/source3/param/loadparm.c
@@ -331,6 +331,7 @@ typedef struct {
bool bResetOnZeroVC;
int iKeepalive;
+ int iminreceivefile;
param_opt_struct *param_opt;
} global;
@@ -998,6 +999,7 @@ static struct parm_struct parm_table[] = {
{"max protocol", P_ENUM, P_GLOBAL, &Globals.maxprotocol, NULL, enum_protocol, FLAG_ADVANCED},
{"protocol", P_ENUM, P_GLOBAL, &Globals.maxprotocol, NULL, enum_protocol, FLAG_ADVANCED},
{"min protocol", P_ENUM, P_GLOBAL, &Globals.minprotocol, NULL, enum_protocol, FLAG_ADVANCED},
+ {"min receivefile size", P_INTEGER, P_GLOBAL, &Globals.iminreceivefile, NULL, NULL, FLAG_ADVANCED},
{"read raw", P_BOOL, P_GLOBAL, &Globals.bReadRaw, NULL, NULL, FLAG_ADVANCED},
{"write raw", P_BOOL, P_GLOBAL, &Globals.bWriteRaw, NULL, NULL, FLAG_ADVANCED},
{"disable netbios", P_BOOL, P_GLOBAL, &Globals.bDisableNetbios, NULL, NULL, FLAG_ADVANCED},
@@ -1708,6 +1710,8 @@ static void init_globals(bool first_time_only)
/* By default no shares out of the registry */
Globals.bRegistryShares = False;
+
+ Globals.iminreceivefile = 0;
}
/*******************************************************************
@@ -2165,6 +2169,7 @@ FN_GLOBAL_INTEGER(lp_algorithmic_rid_base, &Globals.AlgorithmicRidBase)
FN_GLOBAL_INTEGER(lp_name_cache_timeout, &Globals.name_cache_timeout)
FN_GLOBAL_INTEGER(lp_client_signing, &Globals.client_signing)
FN_GLOBAL_INTEGER(lp_server_signing, &Globals.server_signing)
+FN_GLOBAL_INTEGER(lp_min_receive_file_size, &Globals.iminreceivefile);
FN_GLOBAL_INTEGER(lp_client_ldap_sasl_wrapping, &Globals.client_ldap_sasl_wrapping)
/* local prototypes */
diff --git a/source3/smbd/blocking.c b/source3/smbd/blocking.c
index 66a61449a1..0078bb7d13 100644
--- a/source3/smbd/blocking.c
+++ b/source3/smbd/blocking.c
@@ -259,7 +259,7 @@ static void reply_lockingX_success(blocking_lock_record *blr)
smb_panic("Could not allocate smb_request");
}
- init_smb_request(req, (uint8 *)blr->inbuf);
+ init_smb_request(req, (uint8 *)blr->inbuf, 0);
reply_outbuf(req, 2, 0);
/*
@@ -531,7 +531,7 @@ static bool process_trans2(blocking_lock_record *blr)
return True;
}
- init_smb_request(req, (uint8 *)blr->inbuf);
+ init_smb_request(req, (uint8 *)blr->inbuf, 0);
SCVAL(req->inbuf, smb_com, SMBtrans2);
SSVAL(params,0,0);
diff --git a/source3/smbd/fileio.c b/source3/smbd/fileio.c
index 14056ddc80..9e296f2204 100644
--- a/source3/smbd/fileio.c
+++ b/source3/smbd/fileio.c
@@ -116,12 +116,16 @@ static unsigned int allocated_write_caches;
*Really* write to a file.
****************************************************************************/
-static ssize_t real_write_file(files_struct *fsp,const char *data, SMB_OFF_T pos, size_t n)
+static ssize_t real_write_file(struct smb_request *req,
+ files_struct *fsp,
+ const char *data,
+ SMB_OFF_T pos,
+ size_t n)
{
ssize_t ret;
if (pos == -1) {
- ret = vfs_write_data(fsp, data, n);
+ ret = vfs_write_data(req, fsp, data, n);
} else {
fsp->fh->pos = pos;
if (pos && lp_strict_allocate(SNUM(fsp->conn))) {
@@ -129,7 +133,7 @@ static ssize_t real_write_file(files_struct *fsp,const char *data, SMB_OFF_T pos
return -1;
}
}
- ret = vfs_pwrite_data(fsp, data, n, pos);
+ ret = vfs_pwrite_data(req, fsp, data, n, pos);
}
DEBUG(10,("real_write_file (%s): pos = %.0f, size = %lu, returned %ld\n",
@@ -191,11 +195,15 @@ static int wcp_file_size_change(files_struct *fsp)
Write to a file.
****************************************************************************/
-ssize_t write_file(files_struct *fsp, const char *data, SMB_OFF_T pos, size_t n)
+ssize_t write_file(struct smb_request *req,
+ files_struct *fsp,
+ const char *data,
+ SMB_OFF_T pos,
+ size_t n)
{
write_cache *wcp = fsp->wcp;
ssize_t total_written = 0;
- int write_path = -1;
+ int write_path = -1;
if (fsp->print_file) {
fstring sharename;
@@ -234,8 +242,8 @@ ssize_t write_file(files_struct *fsp, const char *data, SMB_OFF_T pos, size_t n)
if (EXCLUSIVE_OPLOCK_TYPE(fsp->oplock_type) && !wcp) {
setup_write_cache(fsp, st.st_size);
wcp = fsp->wcp;
- }
- }
+ }
+ }
}
#ifdef WITH_PROFILE
@@ -280,9 +288,18 @@ nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n",
}
#endif
+ if (wcp && req->unread_bytes) {
+ /* If we're using receivefile don't
+ * deal with a write cache.
+ */
+ flush_write_cache(fsp, WRITE_FLUSH);
+ delete_write_cache(fsp);
+ wcp = NULL;
+ }
+
if(!wcp) {
DO_PROFILE_INC(writecache_direct_writes);
- total_written = real_write_file(fsp, data, pos, n);
+ total_written = real_write_file(req, fsp, data, pos, n);
return total_written;
}
@@ -291,7 +308,7 @@ nonop=%u allocated=%u active=%u direct=%u perfect=%u readhits=%u\n",
fsp->fh->pos = pos + n;
- /*
+ /*
* If we have active cache and it isn't contiguous then we flush.
* NOTE: There is a small problem with running out of disk ....
*/
@@ -610,7 +627,7 @@ len = %u\n",fsp->fh->fd, (double)pos, (unsigned int)n, (double)wcp->offset, (uns
if ( n <= wcp->alloc_size && n > wcp->data_size) {
cache_flush_needed = True;
} else {
- ssize_t ret = real_write_file(fsp, data, pos, n);
+ ssize_t ret = real_write_file(NULL,fsp, data, pos, n);
/*
* If the write overlaps the entire cache, then
@@ -657,7 +674,7 @@ n = %u, wcp->offset=%.0f, wcp->data_size=%u\n",
*/
if (n > wcp->alloc_size ) {
- ssize_t ret = real_write_file(fsp, data, pos, n);
+ ssize_t ret = real_write_file(NULL,fsp, data, pos, n);
if (ret == -1) {
return -1;
}
@@ -828,7 +845,7 @@ ssize_t flush_write_cache(files_struct *fsp, enum flush_reason_enum reason)
}
#endif
- ret = real_write_file(fsp, wcp->data, wcp->offset, data_size);
+ ret = real_write_file(NULL, fsp, wcp->data, wcp->offset, data_size);
/*
* Ensure file size if kept up to date if write extends file.
diff --git a/source3/smbd/notify.c b/source3/smbd/notify.c
index ecb7d9dce8..0dd7fbb20e 100644
--- a/source3/smbd/notify.c
+++ b/source3/smbd/notify.c
@@ -189,7 +189,7 @@ void change_notify_reply(const uint8 *request_buf, uint32 max_param,
smb_setlen((char *)tmp_request, smb_size);
SCVAL(tmp_request, smb_wct, 0);
- init_smb_request(req, tmp_request);
+ init_smb_request(req, tmp_request,0);
send_nt_replies(req, NT_STATUS_OK, prs_data_p(&ps),
prs_offset(&ps), NULL, 0);
diff --git a/source3/smbd/process.c b/source3/smbd/process.c
index ed1bf762e9..1c8d8a6e76 100644
--- a/source3/smbd/process.c
+++ b/source3/smbd/process.c
@@ -25,7 +25,7 @@ extern int smb_echo_count;
const int total_buffer_size = (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN);
-/*
+/*
* Size of data we can send to client. Set
* by the client for all protocols above CORE.
* Set by us for CORE protocol.
@@ -48,7 +48,9 @@ extern int max_send;
* Initialize a struct smb_request from an inbuf
*/
-void init_smb_request(struct smb_request *req, const uint8 *inbuf)
+void init_smb_request(struct smb_request *req,
+ const uint8 *inbuf,
+ size_t unread_bytes)
{
size_t req_size = smb_len(inbuf) + 4;
/* Ensure we have at least smb_size bytes. */
@@ -63,6 +65,8 @@ void init_smb_request(struct smb_request *req, const uint8 *inbuf)
req->vuid = SVAL(inbuf, smb_uid);
req->tid = SVAL(inbuf, smb_tid);
req->wct = CVAL(inbuf, smb_wct);
+ req->unread_bytes = unread_bytes;
+
/* Ensure we have at least wct words and 2 bytes of bcc. */
if (smb_size + req->wct*2 > req_size) {
DEBUG(0,("init_smb_request: invalid wct number %u (size %u)\n",
@@ -231,6 +235,14 @@ bool push_deferred_smb_message(struct smb_request *req,
{
struct timeval end_time;
+ if (req->unread_bytes) {
+ DEBUG(0,("push_deferred_smb_message: logic error ! "
+ "unread_bytes = %u\n",
+ (unsigned int)req->unread_bytes ));
+ smb_panic("push_deferred_smb_message: "
+ "logic error unread_bytes != 0" );
+ }
+
end_time = timeval_sum(&request_time, &timeout);
DEBUG(10,("push_deferred_open_smb_message: pushing message len %u mid %u "
@@ -382,8 +394,11 @@ static int select_on_fd(int fd, int maxfd, fd_set *fds)
The timeout is in milliseconds
****************************************************************************/
-static bool receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
- size_t *buffer_len, int timeout)
+static bool receive_message_or_smb(TALLOC_CTX *mem_ctx,
+ char **buffer,
+ size_t *buffer_len,
+ int timeout,
+ size_t *p_unread)
{
fd_set r_fds, w_fds;
int selrtn;
@@ -391,6 +406,7 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
int maxfd = 0;
ssize_t len;
+ *p_unread = 0;
smb_read_error = 0;
again:
@@ -565,7 +581,7 @@ static bool receive_message_or_smb(TALLOC_CTX *mem_ctx, char **buffer,
goto again;
}
- len = receive_smb_talloc(mem_ctx, smbd_server_fd(), buffer, 0);
+ len = receive_smb_talloc(mem_ctx, smbd_server_fd(), buffer, 0, p_unread);
if (len == -1) {
return False;
@@ -1115,7 +1131,7 @@ static void switch_message(uint8 type, struct smb_request *req, int size)
Construct a reply to the incoming packet.
****************************************************************************/
-static void construct_reply(char *inbuf, int size)
+static void construct_reply(char *inbuf, int size, size_t unread_bytes)
{
uint8 type = CVAL(inbuf,smb_com);
struct smb_request *req;
@@ -1127,10 +1143,19 @@ static void construct_reply(char *inbuf, int size)
if (!(req = talloc(talloc_tos(), struct smb_request))) {
smb_panic("could not allocate smb_request");
}
- init_smb_request(req, (uint8 *)inbuf);
+ init_smb_request(req, (uint8 *)inbuf, unread_bytes);
switch_message(type, req, size);
+ if (req->unread_bytes) {
+ /* writeX failed. drain socket. */
+ if (drain_socket(smbd_server_fd(), req->unread_bytes) !=
+ req->unread_bytes) {
+ smb_panic("failed to drain pending bytes");
+ }
+ req->unread_bytes = 0;
+ }
+
if (req->outbuf == NULL) {
return;
}
@@ -1152,7 +1177,7 @@ static void construct_reply(char *inbuf, int size)
Process an smb from the client
****************************************************************************/
-static void process_smb(char *inbuf, size_t nread)
+static void process_smb(char *inbuf, size_t nread, size_t unread_bytes)
{
static int trans_num;
int msg_type = CVAL(inbuf,0);
@@ -1176,7 +1201,9 @@ static void process_smb(char *inbuf, size_t nread)
DEBUG( 6, ( "got message type 0x%x of len 0x%x\n", msg_type,
smb_len(inbuf) ) );
- DEBUG( 3, ( "Transaction %d of length %d\n", trans_num, (int)nread ) );
+ DEBUG( 3, ( "Transaction %d of length %d (%u toread)\n", trans_num,
+ (int)nread,
+ (unsigned int)unread_bytes ));
if (msg_type != 0) {
/*
@@ -1188,8 +1215,8 @@ static void process_smb(char *inbuf, size_t nread)
show_msg(inbuf);
- construct_reply(inbuf,nread);
-
+ construct_reply(inbuf,nread,unread_bytes);
+
trans_num++;
}
@@ -1348,7 +1375,7 @@ void chain_reply(struct smb_request *req)
if (!(req2 = talloc(talloc_tos(), struct smb_request))) {
smb_panic("could not allocate smb_request");
}
- init_smb_request(req2, (uint8 *)inbuf2);
+ init_smb_request(req2, (uint8 *)inbuf2,0);
/* process the request */
switch_message(smb_com2, req2, new_size);
@@ -1625,6 +1652,7 @@ void smbd_process(void)
{
time_t last_timeout_processing_time = time(NULL);
unsigned int num_smbs = 0;
+ size_t unread_bytes = 0;
max_recv = MIN(lp_maxxmit(),BUFFER_SIZE);
@@ -1635,8 +1663,8 @@ void smbd_process(void)
size_t inbuf_len;
TALLOC_CTX *frame = talloc_stackframe();
- errno = 0;
-
+ errno = 0;
+
/* Did someone ask for immediate checks on things like blocking locks ? */
if (select_timeout == 0) {
if(!timeout_processing(&select_timeout,
@@ -1648,7 +1676,7 @@ void smbd_process(void)
run_events(smbd_event_context(), 0, NULL, NULL);
while (!receive_message_or_smb(NULL, &inbuf, &inbuf_len,
- select_timeout)) {
+ select_timeout, &unread_bytes)) {
if(!timeout_processing(&select_timeout,
&last_timeout_processing_time))
return;
@@ -1664,10 +1692,10 @@ void smbd_process(void)
* faster than the select timeout, thus starving out the
* essential processing (change notify, blocking locks) that
* the timeout code does. JRA.
- */
+ */
num_echos = smb_echo_count;
- process_smb(inbuf, inbuf_len);
+ process_smb(inbuf, inbuf_len, unread_bytes);
TALLOC_FREE(inbuf);
diff --git a/source3/smbd/reply.c b/source3/smbd/reply.c
index 38ce797eeb..4c1ed56d4f 100644
--- a/source3/smbd/reply.c
+++ b/source3/smbd/reply.c
@@ -3494,7 +3494,7 @@ void reply_writebraw(connection_struct *conn, struct smb_request *req)
}
if (numtowrite>0) {
- nwritten = write_file(fsp,data,startpos,numtowrite);
+ nwritten = write_file(req,fsp,data,startpos,numtowrite);
}
DEBUG(3,("reply_writebraw: initial write fnum=%d start=%.0f num=%d "
@@ -3572,7 +3572,7 @@ void reply_writebraw(connection_struct *conn, struct smb_request *req)
exit_server_cleanly("secondary writebraw failed");
}
- nwritten = write_file(fsp,buf+4,startpos+nwritten,numtowrite);
+ nwritten = write_file(req,fsp,buf+4,startpos+nwritten,numtowrite);
if (nwritten == -1) {
TALLOC_FREE(buf);
reply_unixerror(req, ERRHRD, ERRdiskfull);
@@ -3686,7 +3686,7 @@ void reply_writeunlock(connection_struct *conn, struct smb_request *req)
if(numtowrite == 0) {
nwritten = 0;
} else {
- nwritten = write_file(fsp,data,startpos,numtowrite);
+ nwritten = write_file(req,fsp,data,startpos,numtowrite);
}
status = sync_file(conn, fsp, False /* write through */);
@@ -3808,7 +3808,7 @@ void reply_write(connection_struct *conn, struct smb_request *req)
return;
}
} else
- nwritten = write_file(fsp,data,startpos,numtowrite);
+ nwritten = write_file(req,fsp,data,startpos,numtowrite);
status = sync_file(conn, fsp, False);
if (!NT_STATUS_IS_OK(status)) {
@@ -3841,6 +3841,64 @@ void reply_write(connection_struct *conn, struct smb_request *req)
}
/****************************************************************************
+ Ensure a buffer is a valid writeX for recvfile purposes.
+****************************************************************************/
+
+#define STANDARD_WRITE_AND_X_HEADER_SIZE (smb_size - 4 + /* basic header */ \
+ (2*14) + /* word count (including bcc) */ \
+ 1 /* pad byte */)
+
+bool is_valid_writeX_buffer(char *inbuf)
+{
+ size_t numtowrite;
+ connection_struct *conn = NULL;
+ unsigned int doff = 0;
+ size_t len = smb_len(inbuf);
+
+ if (CVAL(inbuf,smb_com) != SMBwriteX ||
+ CVAL(inbuf,smb_vwv0) != 0xFF ||
+ CVAL(inbuf,smb_wct) != 14) {
+ return false;
+ }
+ conn = conn_find(SVAL(inbuf, smb_tid));
+ if (conn == NULL) {
+ return false;
+ }
+ if (IS_IPC(conn)) {
+ return false;
+ }
+ numtowrite = SVAL(inbuf,smb_vwv10);
+ numtowrite |= ((((size_t)SVAL(inbuf,smb_vwv9)) & 1 )<<16);
+ if (numtowrite == 0) {
+ return false;
+ }
+ /* Ensure the sizes match up. */
+ doff = SVAL(inbuf,smb_vwv11);
+
+ if (doff < STANDARD_WRITE_AND_X_HEADER_SIZE) {
+ /* no pad byte...old smbclient :-( */
+ return false;
+ }
+
+ if (len - doff != numtowrite) {
+ DEBUG(10,("is_valid_writeX_buffer: doff mismatch "
+ "len = %u, doff = %u, numtowrite = %u\n",
+ (unsigned int)len,
+ (unsigned int)doff,
+ (unsigned int)numtowrite ));
+ return false;
+ }
+
+ DEBUG(10,("is_valid_writeX_buffer: true "
+ "len = %u, doff = %u, numtowrite = %u\n",
+ (unsigned int)len,
+ (unsigned int)doff,
+ (unsigned int)numtowrite ));
+
+ return true;
+}
+
+/****************************************************************************
Reply to a write and X.
****************************************************************************/
@@ -3875,10 +3933,18 @@ void reply_write_and_X(connection_struct *conn, struct smb_request *req)
numtowrite |= ((((size_t)SVAL(req->inbuf,smb_vwv9)) & 1 )<<16);
}
- if(smb_doff > smblen || (smb_doff + numtowrite > smblen)) {
- reply_doserror(req, ERRDOS, ERRbadmem);
- END_PROFILE(SMBwriteX);
- return;
+ if (req->unread_bytes) {
+ if (numtowrite != req->unread_bytes) {
+ reply_doserror(req, ERRDOS, ERRbadmem);
+ END_PROFILE(SMBwriteX);
+ return;
+ }
+ } else {
+ if (smb_doff > smblen || smb_doff + numtowrite > smblen) {
+ reply_doserror(req, ERRDOS, ERRbadmem);
+ END_PROFILE(SMBwriteX);
+ return;
+ }
}
/* If it's an IPC, pass off the pipe handler. */
@@ -3947,15 +4013,16 @@ void reply_write_and_X(connection_struct *conn, struct smb_request *req)
nwritten = 0;
} else {
- if (schedule_aio_write_and_X(conn, req, fsp, data, startpos,
- numtowrite)) {
+ if (req->unread_bytes == 0 &&
+ schedule_aio_write_and_X(conn, req, fsp, data,
+ startpos, numtowrite)) {
END_PROFILE(SMBwriteX);
return;
}
- nwritten = write_file(fsp,data,startpos,numtowrite);
+ nwritten = write_file(req,fsp,data,startpos,numtowrite);
}
-
+
if(((nwritten == 0) && (numtowrite != 0))||(nwritten < 0)) {
reply_unixerror(req, ERRHRD, ERRdiskfull);
END_PROFILE(SMBwriteX);
@@ -4264,7 +4331,7 @@ void reply_writeclose(connection_struct *conn, struct smb_request *req)
return;
}
- nwritten = write_file(fsp,data,startpos,numtowrite);
+ nwritten = write_file(req,fsp,data,startpos,numtowrite);
set_filetime(conn, fsp->fsp_name, mtime);
@@ -4726,7 +4793,7 @@ void reply_printwrite(connection_struct *conn, struct smb_request *req)
data = smb_buf(req->inbuf) + 3;
- if (write_file(fsp,data,-1,numtowrite) != numtowrite) {
+ if (write_file(req,fsp,data,-1,numtowrite) != numtowrite) {
reply_unixerror(req, ERRHRD, ERRdiskfull);
END_PROFILE(SMBsplwr);
return;
diff --git a/source3/smbd/server.c b/source3/smbd/server.c
index b9ab7ef7ac..25e2d2cb5e 100644
--- a/source3/smbd/server.c
+++ b/source3/smbd/server.c
@@ -1,20 +1,22 @@
-/*
+/*
Unix SMB/CIFS implementation.
Main SMB server routines
Copyright (C) Andrew Tridgell 1992-1998
Copyright (C) Martin Pool 2002
Copyright (C) Jelmer Vernooij 2002-2003
-
+ Copyright (C) Volker Lendecke 1993-2007
+ Copyright (C) Jeremy Allison 1993-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
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
-
+
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
-
+
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
@@ -37,6 +39,8 @@ extern SIG_ATOMIC_T got_sig_term;
extern SIG_ATOMIC_T reload_after_sighup;
static SIG_ATOMIC_T got_sig_cld;
+extern int smb_read_error;
+
#ifdef WITH_DFS
extern int dcelogin_atmost_once;
#endif /* WITH_DFS */
@@ -60,6 +64,293 @@ static void smbd_set_server_fd(int fd)
client_setfd(fd);
}
+/* Socket functions for smbd packet processing. */
+
+static bool valid_packet_size(len)
+{
+ /*
+ * A WRITEX with CAP_LARGE_WRITEX can be 64k worth of data plus 65 bytes
+ * of header. Don't print the error if this fits.... JRA.
+ */
+
+ if (len > (BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE)) {
+ DEBUG(0,("Invalid packet length! (%lu bytes).\n",
+ (unsigned long)len));
+ if (len > BUFFER_SIZE + (SAFETY_MARGIN/2)) {
+
+ /*
+ * Correct fix. smb_read_error may have already been
+ * set. Only set it here if not already set. Global
+ * variables still suck :-). JRA.
+ */
+
+ if (smb_read_error == 0)
+ smb_read_error = READ_ERROR;
+ return false;
+ }
+ }
+ return true;
+}
+
+static ssize_t read_packet_remainder(int fd,
+ char *buffer,
+ unsigned int timeout,
+ ssize_t len)
+{
+ ssize_t ret;
+
+ if(len <= 0) {
+ return len;
+ }
+
+ if (timeout > 0) {
+ ret = read_socket_with_timeout(fd,
+ buffer,
+ len,
+ len,
+ timeout);
+ } else {
+ ret = read_data(fd, buffer, len);
+ }
+
+ if (ret != len) {
+ if (smb_read_error == 0) {
+ smb_read_error = READ_ERROR;
+ }
+ return -1;
+ }
+
+ return len;
+}
+
+/****************************************************************************
+ Attempt a zerocopy writeX read. We know here that len > smb_size-4
+****************************************************************************/
+
+/*
+ * Unfortunately, earlier versions of smbclient/libsmbclient
+ * don't send this "standard" writeX header. I've fixed this
+ * for 3.2 but we'll use the old method with earlier versions.
+ * Windows and CIFSFS at least use this standard size. Not
+ * sure about MacOSX.
+ */
+
+#define STANDARD_WRITE_AND_X_HEADER_SIZE (smb_size - 4 + /* basic header */ \
+ (2*14) + /* word count (including bcc) */ \
+ 1 /* pad byte */)
+
+ssize_t receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx,
+ const char lenbuf[4],
+ int fd,
+ char **buffer,
+ unsigned int timeout,
+ size_t *p_unread)
+{
+ /* Size of a WRITEX call (+4 byte len). */
+ char writeX_header[4 + STANDARD_WRITE_AND_X_HEADER_SIZE];
+ ssize_t len = smb_len(lenbuf);
+ ssize_t toread;
+ ssize_t ret;
+
+ memcpy(writeX_header, lenbuf, sizeof(lenbuf));
+
+ if (timeout > 0) {
+ ret = read_socket_with_timeout(fd,
+ writeX_header + 4,
+ STANDARD_WRITE_AND_X_HEADER_SIZE,
+ STANDARD_WRITE_AND_X_HEADER_SIZE,
+ timeout);
+ } else {
+ ret = read_data(fd,
+ writeX_header+4,
+ STANDARD_WRITE_AND_X_HEADER_SIZE);
+ }
+
+ if (ret != STANDARD_WRITE_AND_X_HEADER_SIZE) {
+ if (smb_read_error == 0) {
+ smb_read_error = READ_ERROR;
+ }
+ return -1;
+ }
+
+ /*
+ * Ok - now try and see if this is a possible
+ * valid writeX call.
+ */
+
+ if (is_valid_writeX_buffer(writeX_header)) {
+ /*
+ * If the data offset is beyond what
+ * we've read, drain the extra bytes.
+ */
+ uint16_t doff = SVAL(writeX_header,smb_vwv11);
+ ssize_t newlen;
+
+ if (doff > STANDARD_WRITE_AND_X_HEADER_SIZE) {
+ size_t drain = doff - STANDARD_WRITE_AND_X_HEADER_SIZE;
+ if (drain_socket(smbd_server_fd(), drain) != drain) {
+ smb_panic("receive_smb_raw_talloc_partial_read:"
+ " failed to drain pending bytes");
+ }
+ } else {
+ doff = STANDARD_WRITE_AND_X_HEADER_SIZE;
+ }
+
+ /* Spoof down the length and null out the bcc. */
+ set_message_bcc(writeX_header, 0);
+ newlen = smb_len(writeX_header);
+
+ /* Copy the header we've written. */
+
+ *buffer = TALLOC_MEMDUP(mem_ctx,
+ writeX_header,
+ sizeof(writeX_header));
+
+ if (*buffer == NULL) {
+ DEBUG(0, ("Could not allocate inbuf of length %d\n",
+ (int)sizeof(writeX_header)));
+ if (smb_read_error == 0)
+ smb_read_error = READ_ERROR;
+ return -1;
+ }
+
+ /* Work out the remaining bytes. */
+ *p_unread = len - STANDARD_WRITE_AND_X_HEADER_SIZE;
+
+ return newlen + 4;
+ }
+
+ if (!valid_packet_size(len)) {
+ return -1;
+ }
+
+ /*
+ * Not a valid writeX call. Just do the standard
+ * talloc and return.
+ */
+
+ *buffer = TALLOC_ARRAY(mem_ctx, char, len+4);
+
+ if (*buffer == NULL) {
+ DEBUG(0, ("Could not allocate inbuf of length %d\n",
+ (int)len+4));
+ if (smb_read_error == 0)
+ smb_read_error = READ_ERROR;
+ return -1;
+ }
+
+ /* Copy in what we already read. */
+ memcpy(*buffer,
+ writeX_header,
+ 4 + STANDARD_WRITE_AND_X_HEADER_SIZE);
+ toread = len - STANDARD_WRITE_AND_X_HEADER_SIZE;
+
+ if(toread > 0) {
+ ret = read_packet_remainder(fd,
+ (*buffer) + 4 + STANDARD_WRITE_AND_X_HEADER_SIZE,
+ timeout,
+ toread);
+ if (ret != toread) {
+ return -1;
+ }
+ }
+
+ return len + 4;
+}
+
+static ssize_t receive_smb_raw_talloc(TALLOC_CTX *mem_ctx,
+ int fd,
+ char **buffer,
+ unsigned int timeout,
+ size_t *p_unread)
+{
+ char lenbuf[4];
+ ssize_t len,ret;
+ int min_recv_size = lp_min_receive_file_size();
+
+ smb_read_error = 0;
+ *p_unread = 0;
+
+ len = read_smb_length_return_keepalive(fd, lenbuf, timeout);
+ if (len < 0) {
+ DEBUG(10,("receive_smb_raw: length < 0!\n"));
+
+ /*
+ * Correct fix. smb_read_error may have already been
+ * set. Only set it here if not already set. Global
+ * variables still suck :-). JRA.
+ */
+
+ if (smb_read_error == 0)
+ smb_read_error = READ_ERROR;
+ return -1;
+ }
+
+ if (CVAL(lenbuf,0) != SMBkeepalive &&
+ min_recv_size &&
+ len > min_recv_size &&
+ !srv_is_signing_active()) {
+
+ return receive_smb_raw_talloc_partial_read(mem_ctx,
+ lenbuf,
+ fd,
+ buffer,
+ timeout,
+ p_unread);
+ }
+
+ if (!valid_packet_size(len)) {
+ return -1;
+ }
+
+ /*
+ * The +4 here can't wrap, we've checked the length above already.
+ */
+
+ *buffer = TALLOC_ARRAY(mem_ctx, char, len+4);
+
+ if (*buffer == NULL) {
+ DEBUG(0, ("Could not allocate inbuf of length %d\n",
+ (int)len+4));
+ if (smb_read_error == 0)
+ smb_read_error = READ_ERROR;
+ return -1;
+ }
+
+ memcpy(*buffer, lenbuf, sizeof(lenbuf));
+
+ ret = read_packet_remainder(fd, (*buffer)+4, timeout, len);
+ if (ret != len) {
+ return -1;
+ }
+
+ return len + 4;
+}
+
+ssize_t receive_smb_talloc(TALLOC_CTX *mem_ctx, int fd, char **buffer,
+ unsigned int timeout, size_t *p_unread)
+{
+ ssize_t len;
+
+ len = receive_smb_raw_talloc(mem_ctx, fd, buffer, timeout, p_unread);
+
+ if (len < 0) {
+ return -1;
+ }
+
+ /* Check the incoming SMB signature. */
+ if (!srv_check_sign_mac(*buffer, true)) {
+ DEBUG(0, ("receive_smb: SMB Signature verification failed on "
+ "incoming packet!\n"));
+ if (smb_read_error == 0) {
+ smb_read_error = READ_BAD_SIG;
+ }
+ return -1;
+ }
+
+ return len;
+}
+
struct event_context *smbd_event_context(void)
{
static struct event_context *ctx;
diff --git a/source3/smbd/vfs.c b/source3/smbd/vfs.c
index e862710b6c..c1c1939153 100644
--- a/source3/smbd/vfs.c
+++ b/source3/smbd/vfs.c
@@ -418,11 +418,24 @@ ssize_t vfs_pread_data(files_struct *fsp, char *buf,
Write data to a fd on the vfs.
****************************************************************************/
-ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
+ssize_t vfs_write_data(struct smb_request *req,
+ files_struct *fsp,
+ const char *buffer,
+ size_t N)
{
size_t total=0;
ssize_t ret;
+ if (req && req->unread_bytes) {
+ SMB_ASSERT(req->unread_bytes == N);
+ req->unread_bytes = 0;
+ return SMB_VFS_RECVFILE(smbd_server_fd(),
+ fsp,
+ fsp->fh->fd,
+ (SMB_OFF_T)-1,
+ N);
+ }
+
while (total < N) {
ret = SMB_VFS_WRITE(fsp,fsp->fh->fd,buffer + total,N - total);
@@ -436,12 +449,25 @@ ssize_t vfs_write_data(files_struct *fsp,const char *buffer,size_t N)
return (ssize_t)total;
}
-ssize_t vfs_pwrite_data(files_struct *fsp,const char *buffer,
- size_t N, SMB_OFF_T offset)
+ssize_t vfs_pwrite_data(struct smb_request *req,
+ files_struct *fsp,
+ const char *buffer,
+ size_t N,
+ SMB_OFF_T offset)
{
size_t total=0;
ssize_t ret;
+ if (req && req->unread_bytes) {
+ SMB_ASSERT(req->unread_bytes == N);
+ req->unread_bytes = 0;
+ return SMB_VFS_RECVFILE(smbd_server_fd(),
+ fsp,
+ fsp->fh->fd,
+ offset,
+ N);
+ }
+
while (total < N) {
ret = SMB_VFS_PWRITE(fsp, fsp->fh->fd, buffer + total,
N - total, offset + total);