2007-11-02 12:21:34 -07:00
/*
2002-01-30 06:08:46 +00:00
Unix SMB / CIFS implementation .
2000-04-25 14:04:06 +00:00
client file read / write routines
Copyright ( C ) Andrew Tridgell 1994 - 1998
2007-11-02 12:21:34 -07:00
2000-04-25 14:04:06 +00:00
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
2007-07-09 19:25:36 +00:00
the Free Software Foundation ; either version 3 of the License , or
2000-04-25 14:04:06 +00:00
( at your option ) any later version .
2007-11-02 12:21:34 -07:00
2000-04-25 14:04:06 +00:00
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 .
2007-11-02 12:21:34 -07:00
2000-04-25 14:04:06 +00:00
You should have received a copy of the GNU General Public License
2007-07-10 00:52:41 +00:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2000-04-25 14:04:06 +00:00
*/
# include "includes.h"
2008-02-28 15:21:33 +01:00
/****************************************************************************
Calculate the recommended read buffer size
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static size_t cli_read_max_bufsize ( struct cli_state * cli )
{
2008-03-08 22:28:01 +01:00
if ( ! client_is_signing_on ( cli ) & & ! cli_encryption_on ( cli )
2008-02-28 15:21:33 +01:00
& & ( cli - > posix_capabilities & CIFS_UNIX_LARGE_READ_CAP ) ) {
return CLI_SAMBA_MAX_POSIX_LARGE_READX_SIZE ;
}
if ( cli - > capabilities & CAP_LARGE_READX ) {
return cli - > is_samba
? CLI_SAMBA_MAX_LARGE_READX_SIZE
: CLI_WINDOWS_MAX_LARGE_READX_SIZE ;
}
return ( cli - > max_xmit - ( smb_size + 32 ) ) & ~ 1023 ;
}
/*
* Send a read & x request
*/
struct async_req * cli_read_andx_send ( TALLOC_CTX * mem_ctx ,
2008-08-24 14:17:43 +02:00
struct event_context * ev ,
2008-02-28 15:21:33 +01:00
struct cli_state * cli , int fnum ,
off_t offset , size_t size )
{
struct async_req * result ;
struct cli_request * req ;
bool bigoffset = False ;
2008-08-01 23:14:51 +02:00
uint16_t vwv [ 12 ] ;
uint8_t wct = 10 ;
2008-02-28 15:21:33 +01:00
if ( size > cli_read_max_bufsize ( cli ) ) {
DEBUG ( 0 , ( " cli_read_andx_send got size=%d, can only handle "
" size=%d \n " , ( int ) size ,
( int ) cli_read_max_bufsize ( cli ) ) ) ;
return NULL ;
}
2008-08-01 23:14:51 +02:00
SCVAL ( vwv + 0 , 0 , 0xFF ) ;
SCVAL ( vwv + 0 , 1 , 0 ) ;
SSVAL ( vwv + 1 , 0 , 0 ) ;
SSVAL ( vwv + 2 , 0 , fnum ) ;
SIVAL ( vwv + 3 , 0 , offset ) ;
SSVAL ( vwv + 5 , 0 , size ) ;
SSVAL ( vwv + 6 , 0 , size ) ;
SSVAL ( vwv + 7 , 0 , ( size > > 16 ) ) ;
SSVAL ( vwv + 8 , 0 , 0 ) ;
SSVAL ( vwv + 9 , 0 , 0 ) ;
2008-10-14 01:59:36 +02:00
if ( ( uint64_t ) offset > > 32 ) {
2008-08-01 23:14:51 +02:00
bigoffset = True ;
SIVAL ( vwv + 10 , 0 ,
2008-10-14 01:59:36 +02:00
( ( ( uint64_t ) offset ) > > 32 ) & 0xffffffff ) ;
2008-08-01 23:14:51 +02:00
wct + = 2 ;
}
2008-08-24 14:17:43 +02:00
result = cli_request_send ( mem_ctx , ev , cli , SMBreadX , 0 , wct , vwv ,
2008-08-01 23:14:51 +02:00
0 , NULL ) ;
2008-02-28 15:21:33 +01:00
if ( result = = NULL ) {
return NULL ;
}
2008-08-28 15:44:14 +02:00
req = talloc_get_type_abort ( result - > private_data , struct cli_request ) ;
2008-08-01 23:14:51 +02:00
2008-02-28 15:21:33 +01:00
req - > data . read . ofs = offset ;
req - > data . read . size = size ;
req - > data . read . received = 0 ;
req - > data . read . rcvbuf = NULL ;
return result ;
}
/*
* Pull the data out of a finished async read_and_x request . rcvbuf is
* talloced from the request , so better make sure that you copy it away before
* you talloc_free ( req ) . " rcvbuf " is NOT a talloc_ctx of its own , so do not
* talloc_move it !
*/
NTSTATUS cli_read_andx_recv ( struct async_req * req , ssize_t * received ,
uint8_t * * rcvbuf )
{
2008-08-28 15:44:14 +02:00
struct cli_request * cli_req = talloc_get_type_abort (
req - > private_data , struct cli_request ) ;
2008-08-25 13:33:41 +02:00
uint8_t wct ;
uint16_t * vwv ;
uint16_t num_bytes ;
uint8_t * bytes ;
2008-02-28 15:21:33 +01:00
NTSTATUS status ;
size_t size ;
SMB_ASSERT ( req - > state > = ASYNC_REQ_DONE ) ;
if ( req - > state = = ASYNC_REQ_ERROR ) {
return req - > status ;
}
2008-08-25 13:33:41 +02:00
status = cli_pull_reply ( req , & wct , & vwv , & num_bytes , & bytes ) ;
2008-02-28 15:21:33 +01:00
2008-03-19 22:37:24 +01:00
if ( NT_STATUS_IS_ERR ( status ) ) {
2008-02-28 15:21:33 +01:00
return status ;
}
2008-08-25 13:33:41 +02:00
if ( wct < 12 ) {
return NT_STATUS_INVALID_NETWORK_RESPONSE ;
}
2008-02-28 15:21:33 +01:00
/* size is the number of bytes the server returned.
* Might be zero . */
2008-08-25 13:33:41 +02:00
size = SVAL ( vwv + 5 , 0 ) ;
size | = ( ( ( unsigned int ) SVAL ( vwv + 7 , 0 ) ) < < 16 ) ;
2008-02-28 15:21:33 +01:00
if ( size > cli_req - > data . read . size ) {
DEBUG ( 5 , ( " server returned more than we wanted! \n " ) ) ;
return NT_STATUS_UNEXPECTED_IO_ERROR ;
}
2008-08-25 13:33:41 +02:00
* rcvbuf = ( uint8_t * ) ( smb_base ( cli_req - > inbuf ) + SVAL ( vwv + 6 , 0 ) ) ;
2008-02-28 15:21:33 +01:00
* received = size ;
return NT_STATUS_OK ;
}
/*
* Parallel read support .
*
* cli_pull sends as many read & x requests as the server would allow via
* max_mux at a time . When replies flow back in , the data is written into
* the callback function " sink " in the right order .
*/
struct cli_pull_state {
struct async_req * req ;
2008-08-24 14:17:43 +02:00
struct event_context * ev ;
2008-02-28 15:21:33 +01:00
struct cli_state * cli ;
uint16_t fnum ;
off_t start_offset ;
2008-03-21 13:39:48 +01:00
SMB_OFF_T size ;
2008-02-28 15:21:33 +01:00
NTSTATUS ( * sink ) ( char * buf , size_t n , void * priv ) ;
void * priv ;
size_t chunk_size ;
/*
* Outstanding requests
*/
int num_reqs ;
struct async_req * * reqs ;
/*
* For how many bytes did we send requests already ?
*/
2008-03-28 15:21:14 +01:00
SMB_OFF_T requested ;
2008-02-28 15:21:33 +01:00
/*
* Next request index to push into " sink " . This walks around the " req "
* array , taking care that the requests are pushed to " sink " in the
* right order . If necessary ( i . e . replies don ' t come in in the right
* order ) , replies are held back in " reqs " .
*/
int top_req ;
/*
* How many bytes did we push into " sink " ?
*/
2008-03-28 15:21:14 +01:00
SMB_OFF_T pushed ;
2008-02-28 15:21:33 +01:00
} ;
static char * cli_pull_print ( TALLOC_CTX * mem_ctx , struct async_req * req )
{
struct cli_pull_state * state = talloc_get_type_abort (
req - > private_data , struct cli_pull_state ) ;
char * result ;
result = async_req_print ( mem_ctx , req ) ;
if ( result = = NULL ) {
return NULL ;
}
return talloc_asprintf_append_buffer (
result , " num_reqs=%d, top_req=%d " ,
state - > num_reqs , state - > top_req ) ;
}
static void cli_pull_read_done ( struct async_req * read_req ) ;
/*
* Prepare an async pull request
*/
2008-08-24 14:17:43 +02:00
struct async_req * cli_pull_send ( TALLOC_CTX * mem_ctx ,
struct event_context * ev ,
struct cli_state * cli ,
2008-02-28 15:21:33 +01:00
uint16_t fnum , off_t start_offset ,
2008-03-21 13:39:48 +01:00
SMB_OFF_T size , size_t window_size ,
2008-02-28 15:21:33 +01:00
NTSTATUS ( * sink ) ( char * buf , size_t n ,
void * priv ) ,
void * priv )
{
struct async_req * result ;
struct cli_pull_state * state ;
int i ;
2008-08-24 14:17:43 +02:00
result = async_req_new ( mem_ctx , ev ) ;
2008-02-28 15:21:33 +01:00
if ( result = = NULL ) {
goto failed ;
}
state = talloc ( result , struct cli_pull_state ) ;
if ( state = = NULL ) {
goto failed ;
}
result - > private_data = state ;
result - > print = cli_pull_print ;
state - > req = result ;
state - > cli = cli ;
2008-08-24 14:17:43 +02:00
state - > ev = ev ;
2008-02-28 15:21:33 +01:00
state - > fnum = fnum ;
state - > start_offset = start_offset ;
state - > size = size ;
state - > sink = sink ;
state - > priv = priv ;
state - > pushed = 0 ;
state - > top_req = 0 ;
2008-03-18 13:20:10 +01:00
if ( size = = 0 ) {
if ( ! async_post_status ( result , NT_STATUS_OK ) ) {
goto failed ;
}
return result ;
}
2008-02-28 15:21:33 +01:00
state - > chunk_size = cli_read_max_bufsize ( cli ) ;
state - > num_reqs = MAX ( window_size / state - > chunk_size , 1 ) ;
state - > num_reqs = MIN ( state - > num_reqs , cli - > max_mux ) ;
state - > reqs = TALLOC_ZERO_ARRAY ( state , struct async_req * ,
state - > num_reqs ) ;
if ( state - > reqs = = NULL ) {
goto failed ;
}
state - > requested = 0 ;
for ( i = 0 ; i < state - > num_reqs ; i + + ) {
2008-03-21 13:39:48 +01:00
SMB_OFF_T size_left ;
size_t request_thistime ;
2008-02-28 15:21:33 +01:00
if ( state - > requested > = size ) {
state - > num_reqs = i ;
break ;
}
size_left = size - state - > requested ;
request_thistime = MIN ( size_left , state - > chunk_size ) ;
state - > reqs [ i ] = cli_read_andx_send (
2008-08-24 14:17:43 +02:00
state - > reqs , ev , cli , fnum ,
2008-02-28 15:21:33 +01:00
state - > start_offset + state - > requested ,
request_thistime ) ;
if ( state - > reqs [ i ] = = NULL ) {
goto failed ;
}
state - > reqs [ i ] - > async . fn = cli_pull_read_done ;
state - > reqs [ i ] - > async . priv = result ;
state - > requested + = request_thistime ;
}
return result ;
failed :
TALLOC_FREE ( result ) ;
return NULL ;
}
/*
* Handle incoming read replies , push the data into sink and send out new
* requests if necessary .
*/
static void cli_pull_read_done ( struct async_req * read_req )
{
struct async_req * pull_req = talloc_get_type_abort (
read_req - > async . priv , struct async_req ) ;
struct cli_pull_state * state = talloc_get_type_abort (
pull_req - > private_data , struct cli_pull_state ) ;
2008-08-28 15:44:14 +02:00
struct cli_request * read_state = talloc_get_type_abort (
read_req - > private_data , struct cli_request ) ;
2008-02-28 15:21:33 +01:00
NTSTATUS status ;
status = cli_read_andx_recv ( read_req , & read_state - > data . read . received ,
& read_state - > data . read . rcvbuf ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
async_req_error ( state - > req , status ) ;
return ;
}
/*
* This loop is the one to take care of out - of - order replies . All
* pending requests are in state - > reqs , state - > reqs [ top_req ] is the
* one that is to be pushed next . If however a request later than
* top_req is replied to , then we can ' t push yet . If top_req is
* replied to at a later point then , we need to push all the finished
* requests .
*/
while ( state - > reqs [ state - > top_req ] ! = NULL ) {
struct cli_request * top_read ;
DEBUG ( 11 , ( " cli_pull_read_done: top_req = %d \n " ,
state - > top_req ) ) ;
if ( state - > reqs [ state - > top_req ] - > state < ASYNC_REQ_DONE ) {
DEBUG ( 11 , ( " cli_pull_read_done: top request not yet "
" done \n " ) ) ;
return ;
}
2008-08-28 15:44:14 +02:00
top_read = talloc_get_type_abort (
state - > reqs [ state - > top_req ] - > private_data ,
struct cli_request ) ;
2008-02-28 15:21:33 +01:00
DEBUG ( 10 , ( " cli_pull_read_done: Pushing %d bytes, %d already "
" pushed \n " , ( int ) top_read - > data . read . received ,
( int ) state - > pushed ) ) ;
status = state - > sink ( ( char * ) top_read - > data . read . rcvbuf ,
top_read - > data . read . received ,
state - > priv ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
async_req_error ( state - > req , status ) ;
return ;
}
state - > pushed + = top_read - > data . read . received ;
TALLOC_FREE ( state - > reqs [ state - > top_req ] ) ;
if ( state - > requested < state - > size ) {
struct async_req * new_req ;
2008-03-21 13:39:48 +01:00
SMB_OFF_T size_left ;
size_t request_thistime ;
2008-02-28 15:21:33 +01:00
size_left = state - > size - state - > requested ;
request_thistime = MIN ( size_left , state - > chunk_size ) ;
DEBUG ( 10 , ( " cli_pull_read_done: Requesting %d bytes "
" at %d, position %d \n " ,
( int ) request_thistime ,
( int ) ( state - > start_offset
+ state - > requested ) ,
state - > top_req ) ) ;
new_req = cli_read_andx_send (
2008-08-24 14:17:43 +02:00
state - > reqs , state - > ev , state - > cli ,
state - > fnum ,
2008-02-28 15:21:33 +01:00
state - > start_offset + state - > requested ,
request_thistime ) ;
if ( async_req_nomem ( new_req , state - > req ) ) {
return ;
}
new_req - > async . fn = cli_pull_read_done ;
new_req - > async . priv = pull_req ;
state - > reqs [ state - > top_req ] = new_req ;
state - > requested + = request_thistime ;
}
state - > top_req = ( state - > top_req + 1 ) % state - > num_reqs ;
}
async_req_done ( pull_req ) ;
}
2008-03-28 15:21:14 +01:00
NTSTATUS cli_pull_recv ( struct async_req * req , SMB_OFF_T * received )
2008-02-28 15:21:33 +01:00
{
struct cli_pull_state * state = talloc_get_type_abort (
req - > private_data , struct cli_pull_state ) ;
SMB_ASSERT ( req - > state > = ASYNC_REQ_DONE ) ;
if ( req - > state = = ASYNC_REQ_ERROR ) {
return req - > status ;
}
* received = state - > pushed ;
return NT_STATUS_OK ;
}
NTSTATUS cli_pull ( struct cli_state * cli , uint16_t fnum ,
2008-03-21 13:39:48 +01:00
off_t start_offset , SMB_OFF_T size , size_t window_size ,
2008-02-28 15:21:33 +01:00
NTSTATUS ( * sink ) ( char * buf , size_t n , void * priv ) ,
2008-03-28 15:21:14 +01:00
void * priv , SMB_OFF_T * received )
2008-02-28 15:21:33 +01:00
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2008-08-24 14:17:43 +02:00
struct event_context * ev ;
2008-02-28 15:21:33 +01:00
struct async_req * req ;
NTSTATUS result = NT_STATUS_NO_MEMORY ;
2008-08-24 14:17:43 +02:00
if ( cli - > fd_event ! = NULL ) {
/*
* Can ' t use sync call while an async call is in flight
*/
return NT_STATUS_INVALID_PARAMETER ;
}
ev = event_context_init ( frame ) ;
if ( ev = = NULL ) {
2008-02-28 15:21:33 +01:00
goto nomem ;
}
2008-08-24 14:17:43 +02:00
req = cli_pull_send ( frame , ev , cli , fnum , start_offset , size ,
window_size , sink , priv ) ;
2008-02-28 15:21:33 +01:00
if ( req = = NULL ) {
goto nomem ;
}
while ( req - > state < ASYNC_REQ_DONE ) {
2008-08-24 14:17:43 +02:00
event_loop_once ( ev ) ;
2008-02-28 15:21:33 +01:00
}
result = cli_pull_recv ( req , received ) ;
nomem :
TALLOC_FREE ( frame ) ;
return result ;
}
2008-02-28 15:26:01 +01:00
static NTSTATUS cli_read_sink ( char * buf , size_t n , void * priv )
2000-04-25 14:04:06 +00:00
{
2008-02-28 15:26:01 +01:00
char * * pbuf = ( char * * ) priv ;
memcpy ( * pbuf , buf , n ) ;
* pbuf + = n ;
return NT_STATUS_OK ;
2000-04-25 14:04:06 +00:00
}
2008-02-28 15:26:01 +01:00
ssize_t cli_read ( struct cli_state * cli , int fnum , char * buf ,
off_t offset , size_t size )
2001-06-22 01:09:40 +00:00
{
2008-02-28 15:26:01 +01:00
NTSTATUS status ;
2008-03-28 15:21:14 +01:00
SMB_OFF_T ret ;
2000-04-25 14:04:06 +00:00
2008-02-28 15:26:01 +01:00
status = cli_pull ( cli , fnum , offset , size , size ,
cli_read_sink , & buf , & ret ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
cli_set_error ( cli , status ) ;
return - 1 ;
2000-04-25 14:04:06 +00:00
}
2008-02-28 15:26:01 +01:00
return ret ;
2000-04-25 14:04:06 +00:00
}
/****************************************************************************
2007-11-02 12:21:34 -07:00
Issue a single SMBwrite and don ' t wait for a reply .
2000-04-25 14:04:06 +00:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-06-29 00:22:22 +00:00
2007-11-02 12:21:34 -07:00
static bool cli_issue_write ( struct cli_state * cli ,
int fnum ,
off_t offset ,
uint16 mode ,
const char * buf ,
size_t size ,
int i )
2000-04-25 14:04:06 +00:00
{
char * p ;
2007-11-02 12:21:34 -07:00
bool large_writex = false ;
2007-12-26 17:12:36 -08:00
/* We can only do direct writes if not signing and not encrypting. */
bool direct_writes = ! client_is_signing_on ( cli ) & & ! cli_encryption_on ( cli ) ;
2000-04-25 14:04:06 +00:00
2007-11-02 12:21:34 -07:00
if ( ! direct_writes & & size + 1 > cli - > bufsize ) {
2006-07-31 03:53:39 +00:00
cli - > outbuf = ( char * ) SMB_REALLOC ( cli - > outbuf , size + 1024 ) ;
r13915: Fixed a very interesting class of realloc() bugs found by Coverity.
realloc can return NULL in one of two cases - (1) the realloc failed,
(2) realloc succeeded but the new size requested was zero, in which
case this is identical to a free() call.
The error paths dealing with these two cases should be different,
but mostly weren't. Secondly the standard idiom for dealing with
realloc when you know the new size is non-zero is the following :
tmp = realloc(p, size);
if (!tmp) {
SAFE_FREE(p);
return error;
} else {
p = tmp;
}
However, there were *many* *many* places in Samba where we were
using the old (broken) idiom of :
p = realloc(p, size)
if (!p) {
return error;
}
which will leak the memory pointed to by p on realloc fail.
This commit (hopefully) fixes all these cases by moving to
a standard idiom of :
p = SMB_REALLOC(p, size)
if (!p) {
return error;
}
Where if the realloc returns null due to the realloc failing
or size == 0 we *guarentee* that the storage pointed to by p
has been freed. This allows me to remove a lot of code that
was dealing with the standard (more verbose) method that required
a tmp pointer. This is almost always what you want. When a
realloc fails you never usually want the old memory, you
want to free it and get into your error processing asap.
For the 11 remaining cases where we really do need to keep the
old pointer I have invented the new macro SMB_REALLOC_KEEP_OLD_ON_ERROR,
which can be used as follows :
tmp = SMB_REALLOC_KEEP_OLD_ON_ERROR(p, size);
if (!tmp) {
SAFE_FREE(p);
return error;
} else {
p = tmp;
}
SMB_REALLOC_KEEP_OLD_ON_ERROR guarentees never to free the
pointer p, even on size == 0 or realloc fail. All this is
done by a hidden extra argument to Realloc(), BOOL free_old_on_error
which is set appropriately by the SMB_REALLOC and SMB_REALLOC_KEEP_OLD_ON_ERROR
macros (and their array counterparts).
It remains to be seen what this will do to our Coverity bug count :-).
Jeremy.
(This used to be commit 1d710d06a214f3f1740e80e0bffd6aab44aac2b0)
2006-03-07 06:31:04 +00:00
if ( ! cli - > outbuf ) {
return False ;
}
2006-07-31 03:53:39 +00:00
cli - > inbuf = ( char * ) SMB_REALLOC ( cli - > inbuf , size + 1024 ) ;
r13915: Fixed a very interesting class of realloc() bugs found by Coverity.
realloc can return NULL in one of two cases - (1) the realloc failed,
(2) realloc succeeded but the new size requested was zero, in which
case this is identical to a free() call.
The error paths dealing with these two cases should be different,
but mostly weren't. Secondly the standard idiom for dealing with
realloc when you know the new size is non-zero is the following :
tmp = realloc(p, size);
if (!tmp) {
SAFE_FREE(p);
return error;
} else {
p = tmp;
}
However, there were *many* *many* places in Samba where we were
using the old (broken) idiom of :
p = realloc(p, size)
if (!p) {
return error;
}
which will leak the memory pointed to by p on realloc fail.
This commit (hopefully) fixes all these cases by moving to
a standard idiom of :
p = SMB_REALLOC(p, size)
if (!p) {
return error;
}
Where if the realloc returns null due to the realloc failing
or size == 0 we *guarentee* that the storage pointed to by p
has been freed. This allows me to remove a lot of code that
was dealing with the standard (more verbose) method that required
a tmp pointer. This is almost always what you want. When a
realloc fails you never usually want the old memory, you
want to free it and get into your error processing asap.
For the 11 remaining cases where we really do need to keep the
old pointer I have invented the new macro SMB_REALLOC_KEEP_OLD_ON_ERROR,
which can be used as follows :
tmp = SMB_REALLOC_KEEP_OLD_ON_ERROR(p, size);
if (!tmp) {
SAFE_FREE(p);
return error;
} else {
p = tmp;
}
SMB_REALLOC_KEEP_OLD_ON_ERROR guarentees never to free the
pointer p, even on size == 0 or realloc fail. All this is
done by a hidden extra argument to Realloc(), BOOL free_old_on_error
which is set appropriately by the SMB_REALLOC and SMB_REALLOC_KEEP_OLD_ON_ERROR
macros (and their array counterparts).
It remains to be seen what this will do to our Coverity bug count :-).
Jeremy.
(This used to be commit 1d710d06a214f3f1740e80e0bffd6aab44aac2b0)
2006-03-07 06:31:04 +00:00
if ( cli - > inbuf = = NULL ) {
SAFE_FREE ( cli - > outbuf ) ;
2002-03-20 01:43:06 +00:00
return False ;
r13915: Fixed a very interesting class of realloc() bugs found by Coverity.
realloc can return NULL in one of two cases - (1) the realloc failed,
(2) realloc succeeded but the new size requested was zero, in which
case this is identical to a free() call.
The error paths dealing with these two cases should be different,
but mostly weren't. Secondly the standard idiom for dealing with
realloc when you know the new size is non-zero is the following :
tmp = realloc(p, size);
if (!tmp) {
SAFE_FREE(p);
return error;
} else {
p = tmp;
}
However, there were *many* *many* places in Samba where we were
using the old (broken) idiom of :
p = realloc(p, size)
if (!p) {
return error;
}
which will leak the memory pointed to by p on realloc fail.
This commit (hopefully) fixes all these cases by moving to
a standard idiom of :
p = SMB_REALLOC(p, size)
if (!p) {
return error;
}
Where if the realloc returns null due to the realloc failing
or size == 0 we *guarentee* that the storage pointed to by p
has been freed. This allows me to remove a lot of code that
was dealing with the standard (more verbose) method that required
a tmp pointer. This is almost always what you want. When a
realloc fails you never usually want the old memory, you
want to free it and get into your error processing asap.
For the 11 remaining cases where we really do need to keep the
old pointer I have invented the new macro SMB_REALLOC_KEEP_OLD_ON_ERROR,
which can be used as follows :
tmp = SMB_REALLOC_KEEP_OLD_ON_ERROR(p, size);
if (!tmp) {
SAFE_FREE(p);
return error;
} else {
p = tmp;
}
SMB_REALLOC_KEEP_OLD_ON_ERROR guarentees never to free the
pointer p, even on size == 0 or realloc fail. All this is
done by a hidden extra argument to Realloc(), BOOL free_old_on_error
which is set appropriately by the SMB_REALLOC and SMB_REALLOC_KEEP_OLD_ON_ERROR
macros (and their array counterparts).
It remains to be seen what this will do to our Coverity bug count :-).
Jeremy.
(This used to be commit 1d710d06a214f3f1740e80e0bffd6aab44aac2b0)
2006-03-07 06:31:04 +00:00
}
2002-03-20 01:43:06 +00:00
cli - > bufsize = size + 1024 ;
}
2000-04-25 14:04:06 +00:00
memset ( cli - > outbuf , ' \0 ' , smb_size ) ;
memset ( cli - > inbuf , ' \0 ' , smb_size ) ;
2007-10-30 12:54:39 -07:00
if ( cli - > capabilities & CAP_LARGE_FILES ) {
2004-12-15 01:25:24 +00:00
large_writex = True ;
}
2002-12-30 06:12:13 +00:00
2007-11-02 12:21:34 -07:00
if ( large_writex ) {
2007-12-26 17:12:36 -08:00
cli_set_message ( cli - > outbuf , 14 , 0 , True ) ;
2007-11-02 12:21:34 -07:00
} else {
2007-12-26 17:12:36 -08:00
cli_set_message ( cli - > outbuf , 12 , 0 , True ) ;
2007-11-02 12:21:34 -07:00
}
2007-10-30 12:54:39 -07:00
2002-01-11 19:10:25 +00:00
SCVAL ( cli - > outbuf , smb_com , SMBwriteX ) ;
2000-04-25 14:04:06 +00:00
SSVAL ( cli - > outbuf , smb_tid , cli - > cnum ) ;
cli_setup_packet ( cli ) ;
2007-10-30 12:54:39 -07:00
2002-01-11 19:10:25 +00:00
SCVAL ( cli - > outbuf , smb_vwv0 , 0xFF ) ;
2000-04-25 14:04:06 +00:00
SSVAL ( cli - > outbuf , smb_vwv2 , fnum ) ;
SIVAL ( cli - > outbuf , smb_vwv3 , offset ) ;
2002-12-30 06:12:13 +00:00
SIVAL ( cli - > outbuf , smb_vwv5 , 0 ) ;
2000-04-25 14:04:06 +00:00
SSVAL ( cli - > outbuf , smb_vwv7 , mode ) ;
2003-01-15 18:57:41 +00:00
SSVAL ( cli - > outbuf , smb_vwv8 , ( mode & 0x0008 ) ? size : 0 ) ;
2002-12-30 06:12:13 +00:00
/*
2003-01-15 18:57:41 +00:00
* According to CIFS - TR - 1 p00 , this following field should only
* be set if CAP_LARGE_WRITEX is set . We should check this
* locally . However , this check might already have been
* done by our callers .
2002-12-30 06:12:13 +00:00
*/
2007-11-02 12:21:34 -07:00
SSVAL ( cli - > outbuf , smb_vwv9 , ( size > > 16 ) ) ;
2000-04-25 14:04:06 +00:00
SSVAL ( cli - > outbuf , smb_vwv10 , size ) ;
2007-10-30 12:54:39 -07:00
/* +1 is pad byte. */
2000-04-25 14:04:06 +00:00
SSVAL ( cli - > outbuf , smb_vwv11 ,
2007-10-30 12:54:39 -07:00
smb_buf ( cli - > outbuf ) - smb_base ( cli - > outbuf ) + 1 ) ;
2002-12-30 06:12:13 +00:00
2006-01-24 20:15:08 +00:00
if ( large_writex ) {
2008-10-14 01:59:36 +02:00
SIVAL ( cli - > outbuf , smb_vwv12 , ( ( ( uint64_t ) offset ) > > 32 ) & 0xffffffff ) ;
2006-01-24 20:15:08 +00:00
}
2007-10-30 12:54:39 -07:00
p = smb_base ( cli - > outbuf ) + SVAL ( cli - > outbuf , smb_vwv11 ) - 1 ;
* p + + = ' \0 ' ; /* pad byte. */
2007-11-02 12:21:34 -07:00
if ( ! direct_writes ) {
memcpy ( p , buf , size ) ;
}
if ( size > 0x1FFFF ) {
/* This is a POSIX 14 word large write. */
set_message_bcc ( cli - > outbuf , 0 ) ; /* Set bcc to zero. */
_smb_setlen_large ( cli - > outbuf , smb_size + 28 + 1 /* pad */ + size - 4 ) ;
} else {
cli_setup_bcc ( cli , p + size ) ;
}
2000-04-25 14:04:06 +00:00
SSVAL ( cli - > outbuf , smb_mid , cli - > mid + i ) ;
2007-10-30 12:54:39 -07:00
2000-04-25 14:04:06 +00:00
show_msg ( cli - > outbuf ) ;
2007-11-02 12:21:34 -07:00
if ( direct_writes ) {
/* For direct writes we now need to write the data
* directly out of buf . */
return cli_send_smb_direct_writeX ( cli , buf , size ) ;
} else {
return cli_send_smb ( cli ) ;
}
2000-04-25 14:04:06 +00:00
}
/****************************************************************************
write to a file
write_mode : 0x0001 disallow write cacheing
0x0002 return bytes remaining
0x0004 use raw named pipe protocol
0x0008 start of message mode named pipe protocol
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-06-29 00:22:22 +00:00
2005-09-30 17:13:37 +00:00
ssize_t cli_write ( struct cli_state * cli ,
2004-10-14 03:19:57 +00:00
int fnum , uint16 write_mode ,
const char * buf , off_t offset , size_t size )
2000-04-25 14:04:06 +00:00
{
2005-09-30 17:13:37 +00:00
ssize_t bwritten = 0 ;
unsigned int issued = 0 ;
unsigned int received = 0 ;
2004-09-17 00:49:41 +00:00
int mpx = 1 ;
2007-11-02 12:21:34 -07:00
size_t writesize ;
int blocks ;
2000-04-25 14:04:06 +00:00
2004-09-17 00:53:17 +00:00
if ( cli - > max_mux > 1 ) {
2004-09-17 00:49:41 +00:00
mpx = cli - > max_mux - 1 ;
2004-09-17 00:53:17 +00:00
} else {
mpx = 1 ;
2004-09-17 00:49:41 +00:00
}
2008-01-14 13:46:06 -08:00
/* Default (small) writesize. */
writesize = ( cli - > max_xmit - ( smb_size + 32 ) ) & ~ 1023 ;
2007-11-06 14:12:38 -08:00
if ( write_mode = = 0 & &
! client_is_signing_on ( cli ) & &
2007-12-26 17:12:36 -08:00
! cli_encryption_on ( cli ) & &
2007-11-02 12:21:34 -07:00
( cli - > posix_capabilities & CIFS_UNIX_LARGE_WRITE_CAP ) & &
( cli - > capabilities & CAP_LARGE_FILES ) ) {
/* Only do massive writes if we can do them direct
2007-12-26 17:12:36 -08:00
* with no signing or encrypting - not on a pipe . */
2007-11-02 12:21:34 -07:00
writesize = CLI_SAMBA_MAX_POSIX_LARGE_WRITEX_SIZE ;
2008-05-21 12:39:08 -07:00
} else if ( ( cli - > capabilities & CAP_LARGE_WRITEX ) & &
( strcmp ( cli - > dev , " LPT1: " ) ! = 0 ) ) {
/* Printer devices are restricted to max_xmit
* writesize in Vista and XPSP3 . */
2007-11-02 12:21:34 -07:00
if ( cli - > is_samba ) {
2007-12-26 17:12:36 -08:00
writesize = CLI_SAMBA_MAX_LARGE_WRITEX_SIZE ;
2008-01-14 13:46:06 -08:00
} else if ( ! client_is_signing_on ( cli ) ) {
/* Windows restricts signed writes to max_xmit.
* Found by Volker . */
2007-12-26 17:12:36 -08:00
writesize = CLI_WINDOWS_MAX_LARGE_WRITEX_SIZE ;
2007-11-02 12:21:34 -07:00
}
}
blocks = ( size + ( writesize - 1 ) ) / writesize ;
2000-04-25 14:04:06 +00:00
while ( received < blocks ) {
2001-06-29 00:22:22 +00:00
while ( ( issued - received < mpx ) & & ( issued < blocks ) ) {
2007-11-02 12:21:34 -07:00
ssize_t bsent = issued * writesize ;
ssize_t size1 = MIN ( writesize , size - bsent ) ;
2000-04-25 14:04:06 +00:00
2001-06-29 00:22:22 +00:00
if ( ! cli_issue_write ( cli , fnum , offset + bsent ,
2000-04-25 14:04:06 +00:00
write_mode ,
buf + bsent ,
2001-06-29 00:22:22 +00:00
size1 , issued ) )
return - 1 ;
2000-04-25 14:04:06 +00:00
issued + + ;
}
2007-11-02 12:21:34 -07:00
if ( ! cli_receive_smb ( cli ) ) {
2000-04-25 14:04:06 +00:00
return bwritten ;
2007-11-02 12:21:34 -07:00
}
2000-04-25 14:04:06 +00:00
received + + ;
2001-09-05 11:32:59 +00:00
if ( cli_is_error ( cli ) )
2000-04-25 14:04:06 +00:00
break ;
bwritten + = SVAL ( cli - > inbuf , smb_vwv2 ) ;
2008-03-28 10:12:07 -07:00
if ( writesize > 0xFFFF ) {
bwritten + = ( ( ( int ) ( SVAL ( cli - > inbuf , smb_vwv4 ) ) ) < < 16 ) ;
}
2000-04-25 14:04:06 +00:00
}
2007-11-02 12:21:34 -07:00
while ( received < issued & & cli_receive_smb ( cli ) ) {
2000-04-25 14:04:06 +00:00
received + + ;
2007-11-02 12:21:34 -07:00
}
2000-04-25 14:04:06 +00:00
return bwritten ;
}
/****************************************************************************
write to a file using a SMBwrite and not bypassing 0 byte writes
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-06-29 00:22:22 +00:00
2000-04-25 14:04:06 +00:00
ssize_t cli_smbwrite ( struct cli_state * cli ,
int fnum , char * buf , off_t offset , size_t size1 )
{
char * p ;
ssize_t total = 0 ;
do {
size_t size = MIN ( size1 , cli - > max_xmit - 48 ) ;
2007-11-02 12:21:34 -07:00
2000-04-25 14:04:06 +00:00
memset ( cli - > outbuf , ' \0 ' , smb_size ) ;
memset ( cli - > inbuf , ' \0 ' , smb_size ) ;
2007-12-26 17:12:36 -08:00
cli_set_message ( cli - > outbuf , 5 , 0 , True ) ;
2000-04-25 14:04:06 +00:00
2002-01-11 19:10:25 +00:00
SCVAL ( cli - > outbuf , smb_com , SMBwrite ) ;
2000-04-25 14:04:06 +00:00
SSVAL ( cli - > outbuf , smb_tid , cli - > cnum ) ;
cli_setup_packet ( cli ) ;
2007-11-02 12:21:34 -07:00
2000-04-25 14:04:06 +00:00
SSVAL ( cli - > outbuf , smb_vwv0 , fnum ) ;
SSVAL ( cli - > outbuf , smb_vwv1 , size ) ;
SIVAL ( cli - > outbuf , smb_vwv2 , offset ) ;
SSVAL ( cli - > outbuf , smb_vwv4 , 0 ) ;
2007-11-02 12:21:34 -07:00
2000-04-25 14:04:06 +00:00
p = smb_buf ( cli - > outbuf ) ;
* p + + = 1 ;
2001-06-21 09:10:42 +00:00
SSVAL ( p , 0 , size ) ; p + = 2 ;
2007-05-25 23:50:35 +00:00
memcpy ( p , buf + total , size ) ; p + = size ;
2001-06-21 09:10:42 +00:00
cli_setup_bcc ( cli , p ) ;
2007-11-02 12:21:34 -07:00
2001-06-29 00:22:22 +00:00
if ( ! cli_send_smb ( cli ) )
return - 1 ;
if ( ! cli_receive_smb ( cli ) )
2000-04-25 14:04:06 +00:00
return - 1 ;
2007-11-02 12:21:34 -07:00
2001-09-05 11:32:59 +00:00
if ( cli_is_error ( cli ) )
2000-04-25 14:04:06 +00:00
return - 1 ;
size = SVAL ( cli - > inbuf , smb_vwv0 ) ;
2001-06-29 00:22:22 +00:00
if ( size = = 0 )
break ;
2000-04-25 14:04:06 +00:00
size1 - = size ;
total + = size ;
2002-03-20 01:47:31 +00:00
offset + = size ;
2000-04-25 14:04:06 +00:00
} while ( size1 ) ;
return total ;
}