1
0
mirror of https://github.com/samba-team/samba.git synced 2024-12-23 17:34:34 +03:00

Add new parameter, "min receivefile size" (by default set

to zero). If non-zero, writeX calls greater than this
value will be left in the socket buffer for later handling
with recvfile (or userspace equivalent). Definition of
recvfile for your system is left as an exercise for
the reader (I'm working on getting splice working :-).
Jeremy.
(This used to be commit 11c03b75dd)
This commit is contained in:
Jeremy Allison 2007-10-30 16:22:24 -07:00
parent 4367f4b4d4
commit c3250149e1
11 changed files with 527 additions and 160 deletions

View File

@ -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. */

View File

@ -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

View File

@ -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.
****************************************************************************/

View File

@ -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 */

View File

@ -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);

View File

@ -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.

View File

@ -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);

View File

@ -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);

View File

@ -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)) {
@ -3840,6 +3840,64 @@ void reply_write(connection_struct *conn, struct smb_request *req)
return;
}
/****************************************************************************
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;

View File

@ -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;

View File

@ -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);