2007-11-02 22:21:34 +03:00
/*
2002-01-30 09:08:46 +03:00
Unix SMB / CIFS implementation .
2000-04-25 18:04:06 +04:00
client file read / write routines
Copyright ( C ) Andrew Tridgell 1994 - 1998
2007-11-02 22:21:34 +03:00
2000-04-25 18:04:06 +04: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 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
2000-04-25 18:04:06 +04:00
( at your option ) any later version .
2007-11-02 22:21:34 +03:00
2000-04-25 18:04:06 +04: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 22:21:34 +03:00
2000-04-25 18:04:06 +04:00
You should have received a copy of the GNU General Public License
2007-07-10 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2000-04-25 18:04:06 +04:00
*/
# include "includes.h"
2011-05-06 13:47:43 +04:00
# include "libsmb/libsmb.h"
2011-04-28 19:38:09 +04:00
# include "../lib/util/tevent_ntstatus.h"
2010-08-26 11:58:09 +04:00
# include "async_smb.h"
2011-02-25 02:03:01 +03:00
# include "trans2.h"
2012-05-26 13:45:09 +04:00
# include "../libcli/smb/smbXcli_base.h"
2000-04-25 18:04:06 +04:00
2008-02-28 17:21:33 +03:00
/****************************************************************************
Calculate the recommended read buffer size
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static size_t cli_read_max_bufsize ( struct cli_state * cli )
{
2011-09-12 04:45:22 +04:00
uint8_t wct = 12 ;
uint32_t min_space ;
uint32_t data_offset ;
uint32_t useable_space = 0 ;
2011-06-08 20:59:39 +04:00
2011-09-12 04:45:22 +04:00
data_offset = HDR_VWV ;
2011-06-08 20:59:39 +04:00
data_offset + = wct * sizeof ( uint16_t ) ;
2011-09-12 04:45:22 +04:00
data_offset + = sizeof ( uint16_t ) ; /* byte count */
2011-06-08 20:59:39 +04:00
data_offset + = 1 ; /* pad */
2011-09-12 04:45:22 +04:00
min_space = cli_state_available_size ( cli , data_offset ) ;
if ( cli - > server_posix_capabilities & CIFS_UNIX_LARGE_READ_CAP ) {
useable_space = 0xFFFFFF - data_offset ;
2012-05-26 14:07:38 +04:00
if ( smb1cli_conn_signing_is_active ( cli - > conn ) ) {
2011-09-12 04:45:22 +04:00
return min_space ;
}
2012-05-19 20:39:44 +04:00
if ( smb1cli_conn_encryption_on ( cli - > conn ) ) {
2011-09-12 04:45:22 +04:00
return min_space ;
}
return useable_space ;
2012-05-19 20:23:40 +04:00
} else if ( smb1cli_conn_capabilities ( cli - > conn ) & CAP_LARGE_READX ) {
2011-09-12 04:45:22 +04:00
/*
* Note : CAP_LARGE_READX also works with signing
*/
useable_space = 0x1FFFF - data_offset ;
useable_space = MIN ( useable_space , UINT16_MAX ) ;
return useable_space ;
}
2011-06-08 20:59:39 +04:00
2011-09-12 04:45:22 +04:00
return min_space ;
2008-02-28 17:21:33 +03:00
}
2008-12-13 15:46:28 +03:00
/****************************************************************************
Calculate the recommended write buffer size
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-06-08 21:01:13 +04:00
static size_t cli_write_max_bufsize ( struct cli_state * cli ,
uint16_t write_mode ,
uint8_t wct )
2008-12-13 15:46:28 +03:00
{
2011-09-12 04:45:22 +04:00
uint32_t min_space ;
uint32_t data_offset ;
uint32_t useable_space = 0 ;
data_offset = HDR_VWV ;
data_offset + = wct * sizeof ( uint16_t ) ;
data_offset + = sizeof ( uint16_t ) ; /* byte count */
data_offset + = 1 ; /* pad */
2008-12-13 15:46:28 +03:00
2011-09-12 04:45:22 +04:00
min_space = cli_state_available_size ( cli , data_offset ) ;
if ( cli - > server_posix_capabilities & CIFS_UNIX_LARGE_WRITE_CAP ) {
useable_space = 0xFFFFFF - data_offset ;
2012-05-19 20:23:40 +04:00
} else if ( smb1cli_conn_capabilities ( cli - > conn ) & CAP_LARGE_WRITEX ) {
2011-09-12 04:45:22 +04:00
useable_space = 0x1FFFF - data_offset ;
} else {
return min_space ;
2008-12-13 15:46:28 +03:00
}
2011-09-12 04:45:22 +04:00
if ( write_mode ! = 0 ) {
return min_space ;
}
2008-12-13 15:46:28 +03:00
2012-05-26 14:07:38 +04:00
if ( smb1cli_conn_signing_is_active ( cli - > conn ) ) {
2011-09-12 04:45:22 +04:00
return min_space ;
}
2011-06-08 21:01:13 +04:00
2012-05-19 20:39:44 +04:00
if ( smb1cli_conn_encryption_on ( cli - > conn ) ) {
2011-09-12 04:45:22 +04:00
return min_space ;
}
2008-12-13 15:46:28 +03:00
2011-09-12 04:45:22 +04:00
if ( strequal ( cli - > dev , " LPT1: " ) ) {
return min_space ;
2008-12-13 15:46:28 +03:00
}
2011-09-12 04:45:22 +04:00
return useable_space ;
2008-12-13 15:46:28 +03:00
}
2009-03-15 11:54:23 +03:00
struct cli_read_andx_state {
size_t size ;
uint16_t vwv [ 12 ] ;
NTSTATUS status ;
size_t received ;
uint8_t * buf ;
} ;
2008-12-13 15:46:28 +03:00
2009-03-15 11:54:23 +03:00
static void cli_read_andx_done ( struct tevent_req * subreq ) ;
2008-02-28 17:21:33 +03:00
2009-03-15 11:54:23 +03:00
struct tevent_req * cli_read_andx_create ( TALLOC_CTX * mem_ctx ,
2013-02-18 12:59:58 +04:00
struct tevent_context * ev ,
2009-05-01 02:26:43 +04:00
struct cli_state * cli , uint16_t fnum ,
2009-03-15 11:54:23 +03:00
off_t offset , size_t size ,
struct tevent_req * * psmbreq )
2008-02-28 17:21:33 +03:00
{
2009-03-15 11:54:23 +03:00
struct tevent_req * req , * subreq ;
struct cli_read_andx_state * state ;
2008-08-02 01:14:51 +04:00
uint8_t wct = 10 ;
2008-02-28 17:21:33 +03:00
2009-03-15 11:54:23 +03:00
req = tevent_req_create ( mem_ctx , & state , struct cli_read_andx_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > size = size ;
SCVAL ( state - > vwv + 0 , 0 , 0xFF ) ;
SCVAL ( state - > vwv + 0 , 1 , 0 ) ;
SSVAL ( state - > vwv + 1 , 0 , 0 ) ;
SSVAL ( state - > vwv + 2 , 0 , fnum ) ;
SIVAL ( state - > vwv + 3 , 0 , offset ) ;
SSVAL ( state - > vwv + 5 , 0 , size ) ;
SSVAL ( state - > vwv + 6 , 0 , size ) ;
SSVAL ( state - > vwv + 7 , 0 , ( size > > 16 ) ) ;
SSVAL ( state - > vwv + 8 , 0 , 0 ) ;
SSVAL ( state - > vwv + 9 , 0 , 0 ) ;
2008-08-02 01:14:51 +04:00
2012-05-19 20:23:40 +04:00
if ( smb1cli_conn_capabilities ( cli - > conn ) & CAP_LARGE_FILES ) {
2009-03-15 11:54:23 +03:00
SIVAL ( state - > vwv + 10 , 0 ,
2008-10-14 03:59:36 +04:00
( ( ( uint64_t ) offset ) > > 32 ) & 0xffffffff ) ;
2011-09-21 00:45:52 +04:00
wct = 12 ;
} else {
if ( ( ( ( uint64_t ) offset ) & 0xffffffff00000000LL ) ! = 0 ) {
DEBUG ( 10 , ( " cli_read_andx_send got large offset where "
" the server does not support it \n " ) ) ;
tevent_req_nterror ( req , NT_STATUS_INVALID_PARAMETER ) ;
return tevent_req_post ( req , ev ) ;
}
2008-08-02 01:14:51 +04:00
}
2009-03-15 11:54:23 +03:00
subreq = cli_smb_req_create ( state , ev , cli , SMBreadX , 0 , wct ,
state - > vwv , 0 , NULL ) ;
if ( subreq = = NULL ) {
TALLOC_FREE ( req ) ;
2008-02-28 17:21:33 +03:00
return NULL ;
}
2009-03-15 11:54:23 +03:00
tevent_req_set_callback ( subreq , cli_read_andx_done , req ) ;
* psmbreq = subreq ;
return req ;
}
2008-02-28 17:21:33 +03:00
2009-03-15 11:54:23 +03:00
struct tevent_req * cli_read_andx_send ( TALLOC_CTX * mem_ctx ,
2013-02-18 12:59:58 +04:00
struct tevent_context * ev ,
2009-05-01 02:26:43 +04:00
struct cli_state * cli , uint16_t fnum ,
2009-03-15 11:54:23 +03:00
off_t offset , size_t size )
{
struct tevent_req * req , * subreq ;
2009-05-14 06:13:12 +04:00
NTSTATUS status ;
2008-02-28 17:21:33 +03:00
2009-03-15 11:54:23 +03:00
req = cli_read_andx_create ( mem_ctx , ev , cli , fnum , offset , size ,
& subreq ) ;
2009-05-14 06:13:12 +04:00
if ( req = = NULL ) {
2009-03-15 11:54:23 +03:00
return NULL ;
}
2009-05-14 06:13:12 +04:00
2012-05-26 13:58:34 +04:00
status = smb1cli_req_chain_submit ( & subreq , 1 ) ;
2011-04-02 18:02:23 +04:00
if ( tevent_req_nterror ( req , status ) ) {
2009-05-14 06:13:12 +04:00
return tevent_req_post ( req , ev ) ;
}
2009-03-15 11:54:23 +03:00
return req ;
2008-02-28 17:21:33 +03:00
}
2009-03-15 11:54:23 +03:00
static void cli_read_andx_done ( struct tevent_req * subreq )
2008-02-28 17:21:33 +03:00
{
2009-03-15 11:54:23 +03:00
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct cli_read_andx_state * state = tevent_req_data (
req , struct cli_read_andx_state ) ;
uint8_t * inbuf ;
2008-08-25 15:33:41 +04:00
uint8_t wct ;
uint16_t * vwv ;
2009-03-15 11:54:23 +03:00
uint32_t num_bytes ;
2008-08-25 15:33:41 +04:00
uint8_t * bytes ;
2008-02-28 17:21:33 +03:00
2010-02-20 17:26:06 +03:00
state - > status = cli_smb_recv ( subreq , state , & inbuf , 12 , & wct , & vwv ,
2010-02-20 11:53:58 +03:00
& num_bytes , & bytes ) ;
2010-02-20 17:26:06 +03:00
TALLOC_FREE ( subreq ) ;
2009-03-15 11:54:23 +03:00
if ( NT_STATUS_IS_ERR ( state - > status ) ) {
tevent_req_nterror ( req , state - > status ) ;
return ;
2008-08-25 15:33:41 +04:00
}
2008-02-28 17:21:33 +03:00
/* size is the number of bytes the server returned.
* Might be zero . */
2009-03-15 11:54:23 +03:00
state - > received = SVAL ( vwv + 5 , 0 ) ;
state - > received | = ( ( ( unsigned int ) SVAL ( vwv + 7 , 0 ) ) < < 16 ) ;
2008-02-28 17:21:33 +03:00
2009-03-15 11:54:23 +03:00
if ( state - > received > state - > size ) {
2008-02-28 17:21:33 +03:00
DEBUG ( 5 , ( " server returned more than we wanted! \n " ) ) ;
2009-03-15 11:54:23 +03:00
tevent_req_nterror ( req , NT_STATUS_UNEXPECTED_IO_ERROR ) ;
return ;
2008-02-28 17:21:33 +03:00
}
2008-11-20 00:55:06 +03:00
/*
* bcc field must be valid for small reads , for large reads the 16 - bit
* bcc field can ' t be correct .
*/
2009-03-15 11:54:23 +03:00
if ( ( state - > received < 0xffff ) & & ( state - > received > num_bytes ) ) {
2008-11-20 00:55:06 +03:00
DEBUG ( 5 , ( " server announced more bytes than sent \n " ) ) ;
2009-03-15 11:54:23 +03:00
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
2008-11-20 00:55:06 +03:00
}
2011-05-06 00:42:05 +04:00
state - > buf = discard_const_p ( uint8_t , smb_base ( inbuf ) ) + SVAL ( vwv + 6 , 0 ) ;
2008-11-20 00:55:06 +03:00
2012-01-27 04:48:11 +04:00
if ( trans_oob ( smb_len_tcp ( inbuf ) , SVAL ( vwv + 6 , 0 ) , state - > received )
2009-09-03 10:02:21 +04:00
| | ( ( state - > received ! = 0 ) & & ( state - > buf < bytes ) ) ) {
2008-11-20 00:55:06 +03:00
DEBUG ( 5 , ( " server returned invalid read&x data offset \n " ) ) ;
2009-03-15 11:54:23 +03:00
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
2008-11-20 00:55:06 +03:00
}
2009-03-15 11:54:23 +03:00
tevent_req_done ( req ) ;
}
/*
* 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 tevent_req * req , ssize_t * received ,
uint8_t * * rcvbuf )
{
struct cli_read_andx_state * state = tevent_req_data (
req , struct cli_read_andx_state ) ;
NTSTATUS status ;
2008-11-20 00:55:06 +03:00
2009-03-15 11:54:23 +03:00
if ( tevent_req_is_nterror ( req , & status ) ) {
return status ;
}
* received = state - > received ;
* rcvbuf = state - > buf ;
2008-02-28 17:21:33 +03:00
return NT_STATUS_OK ;
}
2013-08-13 20:03:50 +04:00
struct cli_pull_chunk ;
2008-02-28 17:21:33 +03:00
struct cli_pull_state {
2013-02-18 12:59:58 +04:00
struct tevent_context * ev ;
2008-02-28 17:21:33 +03:00
struct cli_state * cli ;
uint16_t fnum ;
off_t start_offset ;
2012-04-05 08:53:08 +04:00
off_t size ;
2008-02-28 17:21:33 +03:00
NTSTATUS ( * sink ) ( char * buf , size_t n , void * priv ) ;
void * priv ;
size_t chunk_size ;
2013-08-13 20:03:50 +04:00
off_t next_offset ;
off_t remaining ;
2008-02-28 17:21:33 +03:00
/*
2013-08-13 20:03:50 +04:00
* How many bytes did we push into " sink " ?
2008-02-28 17:21:33 +03:00
*/
2013-08-13 20:03:50 +04:00
off_t pushed ;
2008-02-28 17:21:33 +03:00
/*
2013-08-13 20:03:50 +04:00
* Outstanding requests
*
* The maximum is 256 :
* - which would be a window of 256 MByte
* for SMB2 with multi - credit
* or smb1 unix extentions .
2008-02-28 17:21:33 +03:00
*/
2013-08-13 20:03:50 +04:00
uint16_t max_chunks ;
uint16_t num_chunks ;
uint16_t num_waiting ;
struct cli_pull_chunk * chunks ;
2008-02-28 17:21:33 +03:00
} ;
2013-08-13 20:03:50 +04:00
struct cli_pull_chunk {
struct cli_pull_chunk * prev , * next ;
struct tevent_req * req ; /* This is the main request! Not the subreq */
struct tevent_req * subreq ;
off_t ofs ;
uint8_t * buf ;
size_t total_size ;
size_t tmp_size ;
bool done ;
} ;
2008-02-28 17:21:33 +03:00
2013-08-13 20:03:50 +04:00
static void cli_pull_setup_chunks ( struct tevent_req * req ) ;
static void cli_pull_chunk_ship ( struct cli_pull_chunk * chunk ) ;
static void cli_pull_chunk_done ( struct tevent_req * subreq ) ;
2008-02-28 17:21:33 +03:00
/*
2013-08-13 20:03:50 +04:00
* 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 .
2008-02-28 17:21:33 +03:00
*/
2009-04-09 00:39:55 +04:00
struct tevent_req * cli_pull_send ( TALLOC_CTX * mem_ctx ,
2013-02-18 12:59:58 +04:00
struct tevent_context * ev ,
2009-04-09 00:39:55 +04:00
struct cli_state * cli ,
uint16_t fnum , off_t start_offset ,
2012-04-05 08:53:08 +04:00
off_t size , size_t window_size ,
2009-04-09 00:39:55 +04:00
NTSTATUS ( * sink ) ( char * buf , size_t n ,
void * priv ) ,
void * priv )
2008-02-28 17:21:33 +03:00
{
2009-04-09 00:39:55 +04:00
struct tevent_req * req ;
2008-02-28 17:21:33 +03:00
struct cli_pull_state * state ;
2011-09-12 04:48:25 +04:00
size_t page_size = 1024 ;
2013-08-13 20:03:50 +04:00
uint64_t tmp64 ;
2008-02-28 17:21:33 +03:00
2009-04-09 00:39:55 +04:00
req = tevent_req_create ( mem_ctx , & state , struct cli_pull_state ) ;
if ( req = = NULL ) {
2009-01-18 18:38:30 +03:00
return NULL ;
2008-02-28 17:21:33 +03:00
}
state - > cli = cli ;
2008-08-24 16:17:43 +04:00
state - > ev = ev ;
2008-02-28 17:21:33 +03:00
state - > fnum = fnum ;
state - > start_offset = start_offset ;
state - > size = size ;
state - > sink = sink ;
state - > priv = priv ;
2013-08-13 20:03:50 +04:00
state - > next_offset = start_offset ;
state - > remaining = size ;
2008-03-18 15:20:10 +03:00
if ( size = = 0 ) {
2009-04-09 00:39:55 +04:00
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
2008-03-18 15:20:10 +03:00
}
2013-08-13 20:20:08 +04:00
if ( smbXcli_conn_protocol ( state - > cli - > conn ) > = PROTOCOL_SMB2_02 ) {
state - > chunk_size = smb2cli_conn_max_read_size ( cli - > conn ) ;
} else {
state - > chunk_size = cli_read_max_bufsize ( cli ) ;
}
2011-09-12 04:48:25 +04:00
if ( state - > chunk_size > page_size ) {
state - > chunk_size & = ~ ( page_size - 1 ) ;
}
2008-02-28 17:21:33 +03:00
2013-08-13 20:03:50 +04:00
if ( window_size = = 0 ) {
/*
* We use 16 MByte as default window size .
*/
window_size = 16 * 1024 * 1024 ;
}
tmp64 = window_size / state - > chunk_size ;
if ( ( window_size % state - > chunk_size ) > 0 ) {
tmp64 + = 1 ;
}
tmp64 = MAX ( tmp64 , 1 ) ;
tmp64 = MIN ( tmp64 , 256 ) ;
state - > max_chunks = tmp64 ;
2011-09-13 18:30:30 +04:00
2013-08-13 20:03:50 +04:00
/*
* We defer the callback because of the complex
* substate / subfunction logic
*/
tevent_req_defer_callback ( req , ev ) ;
2008-02-28 17:21:33 +03:00
2013-08-13 20:03:50 +04:00
cli_pull_setup_chunks ( req ) ;
if ( ! tevent_req_is_in_progress ( req ) ) {
return tevent_req_post ( req , ev ) ;
2008-02-28 17:21:33 +03:00
}
2013-08-13 20:03:50 +04:00
return req ;
}
2008-02-28 17:21:33 +03:00
2013-08-13 20:03:50 +04:00
static void cli_pull_setup_chunks ( struct tevent_req * req )
{
struct cli_pull_state * state =
tevent_req_data ( req ,
struct cli_pull_state ) ;
struct cli_pull_chunk * chunk , * next = NULL ;
size_t i ;
2008-02-28 17:21:33 +03:00
2013-08-13 20:03:50 +04:00
for ( chunk = state - > chunks ; chunk ; chunk = next ) {
/*
* Note that chunk might be removed from this call .
*/
next = chunk - > next ;
cli_pull_chunk_ship ( chunk ) ;
if ( ! tevent_req_is_in_progress ( req ) ) {
return ;
2008-02-28 17:21:33 +03:00
}
2013-08-13 20:03:50 +04:00
}
2008-02-28 17:21:33 +03:00
2013-08-13 20:03:50 +04:00
for ( i = state - > num_chunks ; i < state - > max_chunks ; i + + ) {
2008-02-28 17:21:33 +03:00
2013-08-13 20:03:50 +04:00
if ( state - > num_waiting > 0 ) {
return ;
}
if ( state - > remaining = = 0 ) {
break ;
}
2008-02-28 17:21:33 +03:00
2013-08-13 20:03:50 +04:00
chunk = talloc_zero ( state , struct cli_pull_chunk ) ;
if ( tevent_req_nomem ( chunk , req ) ) {
return ;
}
chunk - > req = req ;
chunk - > ofs = state - > next_offset ;
chunk - > total_size = MIN ( state - > remaining , state - > chunk_size ) ;
state - > next_offset + = chunk - > total_size ;
state - > remaining - = chunk - > total_size ;
DLIST_ADD_END ( state - > chunks , chunk , NULL ) ;
state - > num_chunks + + ;
state - > num_waiting + + ;
cli_pull_chunk_ship ( chunk ) ;
if ( ! tevent_req_is_in_progress ( req ) ) {
return ;
2008-02-28 17:21:33 +03:00
}
}
2013-08-13 20:03:50 +04:00
if ( state - > remaining > 0 ) {
return ;
}
2008-02-28 17:21:33 +03:00
2013-08-13 20:03:50 +04:00
if ( state - > num_chunks > 0 ) {
return ;
}
2008-02-28 17:21:33 +03:00
2013-08-13 20:03:50 +04:00
tevent_req_done ( req ) ;
}
static void cli_pull_chunk_ship ( struct cli_pull_chunk * chunk )
2008-02-28 17:21:33 +03:00
{
2013-08-13 20:03:50 +04:00
struct tevent_req * req = chunk - > req ;
struct cli_pull_state * state =
tevent_req_data ( req ,
struct cli_pull_state ) ;
bool ok ;
off_t ofs ;
size_t size ;
2008-02-28 17:21:33 +03:00
2013-08-13 20:03:50 +04:00
if ( chunk - > done ) {
NTSTATUS status ;
if ( chunk ! = state - > chunks ) {
/*
* this chunk is not the
* first one in the list .
*
* which means we should not
* push it into the sink yet .
*/
return ;
}
if ( chunk - > tmp_size = = 0 ) {
/*
* we git a short read , we ' re done
*/
tevent_req_done ( req ) ;
return ;
2009-03-15 11:54:23 +03:00
}
2013-08-13 20:03:50 +04:00
status = state - > sink ( ( char * ) chunk - > buf ,
chunk - > tmp_size ,
state - > priv ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
state - > pushed + = chunk - > tmp_size ;
if ( chunk - > tmp_size < chunk - > total_size ) {
/*
* we git a short read , we ' re done
*/
tevent_req_done ( req ) ;
return ;
}
DLIST_REMOVE ( state - > chunks , chunk ) ;
SMB_ASSERT ( state - > num_chunks > 0 ) ;
state - > num_chunks - - ;
TALLOC_FREE ( chunk ) ;
return ;
2009-03-15 11:54:23 +03:00
}
2013-08-13 20:03:50 +04:00
if ( chunk - > subreq ! = NULL ) {
2009-03-15 11:54:23 +03:00
return ;
}
2013-08-13 20:03:50 +04:00
SMB_ASSERT ( state - > num_waiting > 0 ) ;
ofs = chunk - > ofs + chunk - > tmp_size ;
size = chunk - > total_size - chunk - > tmp_size ;
2013-08-13 20:20:08 +04:00
if ( smbXcli_conn_protocol ( state - > cli - > conn ) > = PROTOCOL_SMB2_02 ) {
uint32_t max_size ;
2008-02-28 17:21:33 +03:00
2013-08-13 20:20:08 +04:00
ok = smb2cli_conn_req_possible ( state - > cli - > conn , & max_size ) ;
if ( ! ok ) {
return ;
}
/*
* downgrade depending on the available credits
*/
size = MIN ( max_size , size ) ;
chunk - > subreq = cli_smb2_read_send ( chunk ,
state - > ev ,
state - > cli ,
state - > fnum ,
ofs ,
size ) ;
if ( tevent_req_nomem ( chunk - > subreq , req ) ) {
return ;
}
} else {
ok = smb1cli_conn_req_possible ( state - > cli - > conn ) ;
if ( ! ok ) {
return ;
}
chunk - > subreq = cli_read_andx_send ( chunk ,
state - > ev ,
state - > cli ,
state - > fnum ,
ofs ,
size ) ;
if ( tevent_req_nomem ( chunk - > subreq , req ) ) {
return ;
}
2013-08-13 20:03:50 +04:00
}
tevent_req_set_callback ( chunk - > subreq ,
cli_pull_chunk_done ,
chunk ) ;
2008-02-28 17:21:33 +03:00
2013-08-13 20:03:50 +04:00
state - > num_waiting - - ;
return ;
}
2008-02-28 17:21:33 +03:00
2013-08-13 20:03:50 +04:00
static void cli_pull_chunk_done ( struct tevent_req * subreq )
{
struct cli_pull_chunk * chunk =
tevent_req_callback_data ( subreq ,
struct cli_pull_chunk ) ;
struct tevent_req * req = chunk - > req ;
struct cli_pull_state * state =
tevent_req_data ( req ,
struct cli_pull_state ) ;
NTSTATUS status ;
size_t expected = chunk - > total_size - chunk - > tmp_size ;
ssize_t received ;
uint8_t * buf = NULL ;
2008-02-28 17:21:33 +03:00
2013-08-13 20:03:50 +04:00
chunk - > subreq = NULL ;
2009-03-15 11:54:23 +03:00
2013-08-13 20:20:08 +04:00
if ( smbXcli_conn_protocol ( state - > cli - > conn ) > = PROTOCOL_SMB2_02 ) {
status = cli_smb2_read_recv ( subreq , & received , & buf ) ;
} else {
status = cli_read_andx_recv ( subreq , & received , & buf ) ;
}
2013-08-13 20:03:50 +04:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_END_OF_FILE ) ) {
received = 0 ;
status = NT_STATUS_OK ;
}
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2008-02-28 17:21:33 +03:00
2013-08-13 20:03:50 +04:00
if ( received > expected ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
}
if ( received = = 0 ) {
/*
* We got EOF we ' re done
*/
chunk - > done = true ;
cli_pull_setup_chunks ( req ) ;
return ;
}
2008-02-28 17:21:33 +03:00
2013-08-13 20:03:50 +04:00
if ( received = = chunk - > total_size ) {
/*
* We got it in the first run .
*
* We don ' t call TALLOC_FREE ( subreq )
* here and keep the returned buffer .
*/
chunk - > buf = buf ;
} else if ( chunk - > buf = = NULL ) {
chunk - > buf = talloc_array ( chunk , uint8_t , chunk - > total_size ) ;
if ( tevent_req_nomem ( chunk - > buf , req ) ) {
2008-02-28 17:21:33 +03:00
return ;
}
2013-08-13 20:03:50 +04:00
}
2008-02-28 17:21:33 +03:00
2013-08-13 20:03:50 +04:00
if ( received ! = chunk - > total_size ) {
uint8_t * p = chunk - > buf + chunk - > tmp_size ;
memcpy ( p , buf , received ) ;
TALLOC_FREE ( subreq ) ;
2008-02-28 17:21:33 +03:00
}
2013-08-13 20:03:50 +04:00
chunk - > tmp_size + = received ;
if ( chunk - > tmp_size = = chunk - > total_size ) {
chunk - > done = true ;
} else {
state - > num_waiting + + ;
}
cli_pull_setup_chunks ( req ) ;
2008-02-28 17:21:33 +03:00
}
2012-04-05 08:53:08 +04:00
NTSTATUS cli_pull_recv ( struct tevent_req * req , off_t * received )
2008-02-28 17:21:33 +03:00
{
2009-04-09 00:39:55 +04:00
struct cli_pull_state * state = tevent_req_data (
req , struct cli_pull_state ) ;
2008-12-01 10:23:35 +03:00
NTSTATUS status ;
2008-02-28 17:21:33 +03:00
2009-04-09 00:39:55 +04:00
if ( tevent_req_is_nterror ( req , & status ) ) {
2013-08-13 20:03:50 +04:00
tevent_req_received ( req ) ;
2008-12-01 10:23:35 +03:00
return status ;
2008-02-28 17:21:33 +03:00
}
* received = state - > pushed ;
2013-08-13 20:03:50 +04:00
tevent_req_received ( req ) ;
2008-02-28 17:21:33 +03:00
return NT_STATUS_OK ;
}
NTSTATUS cli_pull ( struct cli_state * cli , uint16_t fnum ,
2012-04-05 08:53:08 +04:00
off_t start_offset , off_t size , size_t window_size ,
2008-02-28 17:21:33 +03:00
NTSTATUS ( * sink ) ( char * buf , size_t n , void * priv ) ,
2012-04-05 08:53:08 +04:00
void * priv , off_t * received )
2008-02-28 17:21:33 +03:00
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2013-02-18 12:59:58 +04:00
struct tevent_context * ev ;
2009-04-09 00:39:55 +04:00
struct tevent_req * req ;
2009-04-05 08:33:17 +04:00
NTSTATUS status = NT_STATUS_OK ;
2008-02-28 17:21:33 +03:00
2012-05-26 13:45:09 +04:00
if ( smbXcli_conn_has_async_calls ( cli - > conn ) ) {
2008-08-24 16:17:43 +04:00
/*
* Can ' t use sync call while an async call is in flight
*/
2009-04-05 08:33:17 +04:00
status = NT_STATUS_INVALID_PARAMETER ;
goto fail ;
2008-08-24 16:17:43 +04:00
}
2013-02-18 12:08:19 +04:00
ev = samba_tevent_context_init ( frame ) ;
2008-08-24 16:17:43 +04:00
if ( ev = = NULL ) {
2009-04-05 08:33:17 +04:00
status = NT_STATUS_NO_MEMORY ;
goto fail ;
2008-02-28 17:21:33 +03:00
}
2008-08-24 16:17:43 +04:00
req = cli_pull_send ( frame , ev , cli , fnum , start_offset , size ,
window_size , sink , priv ) ;
2008-02-28 17:21:33 +03:00
if ( req = = NULL ) {
2009-04-05 08:33:17 +04:00
status = NT_STATUS_NO_MEMORY ;
goto fail ;
2008-02-28 17:21:33 +03:00
}
2015-04-28 17:00:06 +03:00
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
2009-04-09 00:39:55 +04:00
goto fail ;
2008-02-28 17:21:33 +03:00
}
2009-04-05 08:33:17 +04:00
status = cli_pull_recv ( req , received ) ;
fail :
2008-02-28 17:21:33 +03:00
TALLOC_FREE ( frame ) ;
2009-04-05 08:33:17 +04:00
return status ;
2008-02-28 17:21:33 +03:00
}
2008-02-28 17:26:01 +03:00
static NTSTATUS cli_read_sink ( char * buf , size_t n , void * priv )
2000-04-25 18:04:06 +04:00
{
2008-02-28 17:26:01 +03:00
char * * pbuf = ( char * * ) priv ;
memcpy ( * pbuf , buf , n ) ;
* pbuf + = n ;
return NT_STATUS_OK ;
2000-04-25 18:04:06 +04:00
}
2011-07-19 13:11:27 +04:00
NTSTATUS cli_read ( struct cli_state * cli , uint16_t fnum ,
char * buf , off_t offset , size_t size ,
size_t * nread )
{
NTSTATUS status ;
2012-04-05 08:53:08 +04:00
off_t ret ;
2011-07-19 13:11:27 +04:00
status = cli_pull ( cli , fnum , offset , size , size ,
cli_read_sink , & buf , & ret ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
if ( nread ) {
* nread = ret ;
}
return NT_STATUS_OK ;
}
2000-04-25 18:04:06 +04:00
/****************************************************************************
write to a file using a SMBwrite and not bypassing 0 byte writes
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-06-29 04:22:22 +04:00
2011-03-27 19:04:56 +04:00
NTSTATUS cli_smbwrite ( struct cli_state * cli , uint16_t fnum , char * buf ,
off_t offset , size_t size1 , size_t * ptotal )
2000-04-25 18:04:06 +04:00
{
2011-03-27 19:04:56 +04:00
uint8_t * bytes ;
2000-04-25 18:04:06 +04:00
ssize_t total = 0 ;
2011-03-27 19:04:56 +04:00
/*
* 3 bytes prefix
*/
2007-11-02 22:21:34 +03:00
2011-06-07 05:30:12 +04:00
bytes = talloc_array ( talloc_tos ( ) , uint8_t , 3 ) ;
2011-03-27 19:04:56 +04:00
if ( bytes = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
bytes [ 0 ] = 1 ;
2001-06-29 04:22:22 +04:00
2011-03-27 19:04:56 +04:00
do {
2011-09-13 15:10:33 +04:00
uint32_t usable_space = cli_state_available_size ( cli , 48 ) ;
size_t size = MIN ( size1 , usable_space ) ;
2011-03-27 19:04:56 +04:00
struct tevent_req * req ;
uint16_t vwv [ 5 ] ;
uint16_t * ret_vwv ;
NTSTATUS status ;
SSVAL ( vwv + 0 , 0 , fnum ) ;
SSVAL ( vwv + 1 , 0 , size ) ;
SIVAL ( vwv + 2 , 0 , offset ) ;
SSVAL ( vwv + 4 , 0 , 0 ) ;
2011-06-07 05:10:15 +04:00
bytes = talloc_realloc ( talloc_tos ( ) , bytes , uint8_t ,
2011-03-27 19:04:56 +04:00
size + 3 ) ;
if ( bytes = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
SSVAL ( bytes , 1 , size ) ;
memcpy ( bytes + 3 , buf + total , size ) ;
2007-11-02 22:21:34 +03:00
2011-03-27 19:04:56 +04:00
status = cli_smb ( talloc_tos ( ) , cli , SMBwrite , 0 , 5 , vwv ,
size + 3 , bytes , & req , 1 , NULL , & ret_vwv ,
NULL , NULL ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( bytes ) ;
return status ;
}
2000-04-25 18:04:06 +04:00
2011-03-27 19:04:56 +04:00
size = SVAL ( ret_vwv + 0 , 0 ) ;
TALLOC_FREE ( req ) ;
if ( size = = 0 ) {
2001-06-29 04:22:22 +04:00
break ;
2011-03-27 19:04:56 +04:00
}
2000-04-25 18:04:06 +04:00
size1 - = size ;
total + = size ;
2002-03-20 04:47:31 +03:00
offset + = size ;
2000-04-25 18:04:06 +04:00
} while ( size1 ) ;
2011-03-27 19:04:56 +04:00
TALLOC_FREE ( bytes ) ;
if ( ptotal ! = NULL ) {
* ptotal = total ;
}
return NT_STATUS_OK ;
2000-04-25 18:04:06 +04:00
}
2008-12-20 00:58:34 +03:00
/*
* Send a write & x request
*/
2009-03-29 15:28:18 +04:00
struct cli_write_andx_state {
size_t size ;
uint16_t vwv [ 14 ] ;
size_t written ;
uint8_t pad ;
struct iovec iov [ 2 ] ;
} ;
static void cli_write_andx_done ( struct tevent_req * subreq ) ;
struct tevent_req * cli_write_andx_create ( TALLOC_CTX * mem_ctx ,
2013-02-18 12:59:58 +04:00
struct tevent_context * ev ,
2009-03-29 15:28:18 +04:00
struct cli_state * cli , uint16_t fnum ,
uint16_t mode , const uint8_t * buf ,
off_t offset , size_t size ,
struct tevent_req * * reqs_before ,
int num_reqs_before ,
struct tevent_req * * psmbreq )
2008-12-20 00:58:34 +03:00
{
2009-03-29 15:28:18 +04:00
struct tevent_req * req , * subreq ;
struct cli_write_andx_state * state ;
2012-05-19 20:23:40 +04:00
bool bigoffset = ( ( smb1cli_conn_capabilities ( cli - > conn ) & CAP_LARGE_FILES ) ! = 0 ) ;
2008-12-20 00:58:34 +03:00
uint8_t wct = bigoffset ? 14 : 12 ;
2011-06-08 21:01:13 +04:00
size_t max_write = cli_write_max_bufsize ( cli , mode , wct ) ;
2009-03-29 15:28:18 +04:00
uint16_t * vwv ;
req = tevent_req_create ( mem_ctx , & state , struct cli_write_andx_state ) ;
if ( req = = NULL ) {
return NULL ;
}
2008-12-20 00:58:34 +03:00
2011-11-08 11:25:16 +04:00
state - > size = MIN ( size , max_write ) ;
2008-12-20 00:58:34 +03:00
2009-03-29 15:28:18 +04:00
vwv = state - > vwv ;
2008-12-20 00:58:34 +03: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 ) ;
SIVAL ( vwv + 5 , 0 , 0 ) ;
SSVAL ( vwv + 7 , 0 , mode ) ;
SSVAL ( vwv + 8 , 0 , 0 ) ;
2011-11-08 11:25:16 +04:00
SSVAL ( vwv + 9 , 0 , ( state - > size > > 16 ) ) ;
SSVAL ( vwv + 10 , 0 , state - > size ) ;
2008-12-20 00:58:34 +03:00
SSVAL ( vwv + 11 , 0 ,
2012-05-26 13:58:34 +04:00
smb1cli_req_wct_ofs ( reqs_before , num_reqs_before )
2008-12-20 00:58:34 +03:00
+ 1 /* the wct field */
+ wct * 2 /* vwv */
+ 2 /* num_bytes field */
+ 1 /* pad */ ) ;
if ( bigoffset ) {
SIVAL ( vwv + 12 , 0 , ( ( ( uint64_t ) offset ) > > 32 ) & 0xffffffff ) ;
}
2009-03-29 15:28:18 +04:00
state - > pad = 0 ;
2009-05-12 22:45:37 +04:00
state - > iov [ 0 ] . iov_base = ( void * ) & state - > pad ;
2009-03-29 15:28:18 +04:00
state - > iov [ 0 ] . iov_len = 1 ;
2011-05-06 00:42:05 +04:00
state - > iov [ 1 ] . iov_base = discard_const_p ( void , buf ) ;
2011-11-09 11:09:17 +04:00
state - > iov [ 1 ] . iov_len = state - > size ;
2009-03-29 15:28:18 +04:00
subreq = cli_smb_req_create ( state , ev , cli , SMBwriteX , 0 , wct , vwv ,
2 , state - > iov ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , cli_write_andx_done , req ) ;
* psmbreq = subreq ;
return req ;
2008-12-20 00:58:34 +03:00
}
2009-03-29 15:28:18 +04:00
struct tevent_req * cli_write_andx_send ( TALLOC_CTX * mem_ctx ,
2013-02-18 12:59:58 +04:00
struct tevent_context * ev ,
2009-03-29 15:28:18 +04:00
struct cli_state * cli , uint16_t fnum ,
uint16_t mode , const uint8_t * buf ,
off_t offset , size_t size )
2008-12-20 00:58:34 +03:00
{
2009-03-29 15:28:18 +04:00
struct tevent_req * req , * subreq ;
2009-05-14 06:13:12 +04:00
NTSTATUS status ;
2009-03-29 15:28:18 +04:00
req = cli_write_andx_create ( mem_ctx , ev , cli , fnum , mode , buf , offset ,
size , NULL , 0 , & subreq ) ;
2009-05-14 06:13:12 +04:00
if ( req = = NULL ) {
2009-03-29 15:28:18 +04:00
return NULL ;
}
2009-05-14 06:13:12 +04:00
2012-05-26 13:58:34 +04:00
status = smb1cli_req_chain_submit ( & subreq , 1 ) ;
2011-04-02 18:02:23 +04:00
if ( tevent_req_nterror ( req , status ) ) {
2009-05-14 06:13:12 +04:00
return tevent_req_post ( req , ev ) ;
}
2009-03-29 15:28:18 +04:00
return req ;
}
static void cli_write_andx_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct cli_write_andx_state * state = tevent_req_data (
req , struct cli_write_andx_state ) ;
2008-12-20 00:58:34 +03:00
uint8_t wct ;
uint16_t * vwv ;
NTSTATUS status ;
2012-06-04 17:59:42 +04:00
status = cli_smb_recv ( subreq , state , NULL , 6 , & wct , & vwv ,
2010-02-20 17:27:22 +03:00
NULL , NULL ) ;
TALLOC_FREE ( subreq ) ;
2009-03-29 15:28:18 +04:00
if ( NT_STATUS_IS_ERR ( status ) ) {
tevent_req_nterror ( req , status ) ;
return ;
2008-12-20 00:58:34 +03:00
}
2009-03-29 15:28:18 +04:00
state - > written = SVAL ( vwv + 2 , 0 ) ;
2011-11-08 11:25:16 +04:00
if ( state - > size > UINT16_MAX ) {
/*
* It is important that we only set the
* high bits only if we asked for a large write .
*
* OS / 2 print shares get this wrong and may send
* invalid values .
*
* See bug # 5326.
*/
state - > written | = SVAL ( vwv + 4 , 0 ) < < 16 ;
}
2009-03-29 15:28:18 +04:00
tevent_req_done ( req ) ;
}
2008-12-20 00:58:34 +03:00
2009-03-29 15:28:18 +04:00
NTSTATUS cli_write_andx_recv ( struct tevent_req * req , size_t * pwritten )
{
struct cli_write_andx_state * state = tevent_req_data (
req , struct cli_write_andx_state ) ;
NTSTATUS status ;
2008-12-20 00:58:34 +03:00
2009-03-29 15:28:18 +04:00
if ( tevent_req_is_nterror ( req , & status ) ) {
2008-12-20 00:58:34 +03:00
return status ;
}
2011-04-30 12:58:35 +04:00
if ( pwritten ! = 0 ) {
* pwritten = state - > written ;
}
2008-12-20 00:58:34 +03:00
return NT_STATUS_OK ;
}
struct cli_writeall_state {
2013-02-18 12:59:58 +04:00
struct tevent_context * ev ;
2008-12-20 00:58:34 +03:00
struct cli_state * cli ;
uint16_t fnum ;
uint16_t mode ;
const uint8_t * buf ;
off_t offset ;
size_t size ;
size_t written ;
} ;
2009-03-29 15:28:18 +04:00
static void cli_writeall_written ( struct tevent_req * req ) ;
2008-12-20 00:58:34 +03:00
2009-04-09 00:54:28 +04:00
static struct tevent_req * cli_writeall_send ( TALLOC_CTX * mem_ctx ,
2013-02-18 12:59:58 +04:00
struct tevent_context * ev ,
2009-04-09 00:54:28 +04:00
struct cli_state * cli ,
uint16_t fnum ,
uint16_t mode ,
const uint8_t * buf ,
off_t offset , size_t size )
2008-12-20 00:58:34 +03:00
{
2009-04-09 00:54:28 +04:00
struct tevent_req * req , * subreq ;
2008-12-20 00:58:34 +03:00
struct cli_writeall_state * state ;
2009-04-09 00:54:28 +04:00
req = tevent_req_create ( mem_ctx , & state , struct cli_writeall_state ) ;
if ( req = = NULL ) {
2009-01-18 18:38:30 +03:00
return NULL ;
2008-12-20 00:58:34 +03:00
}
state - > ev = ev ;
state - > cli = cli ;
state - > fnum = fnum ;
state - > mode = mode ;
state - > buf = buf ;
state - > offset = offset ;
state - > size = size ;
state - > written = 0 ;
subreq = cli_write_andx_send ( state , state - > ev , state - > cli , state - > fnum ,
state - > mode , state - > buf , state - > offset ,
state - > size ) ;
2009-04-09 00:54:28 +04:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
2008-12-20 00:58:34 +03:00
}
2009-04-09 00:54:28 +04:00
tevent_req_set_callback ( subreq , cli_writeall_written , req ) ;
return req ;
2008-12-20 00:58:34 +03:00
}
2009-03-29 15:28:18 +04:00
static void cli_writeall_written ( struct tevent_req * subreq )
2008-12-20 00:58:34 +03:00
{
2009-04-09 00:54:28 +04:00
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct cli_writeall_state * state = tevent_req_data (
req , struct cli_writeall_state ) ;
2008-12-20 00:58:34 +03:00
NTSTATUS status ;
size_t written , to_write ;
status = cli_write_andx_recv ( subreq , & written ) ;
TALLOC_FREE ( subreq ) ;
2011-04-02 18:02:23 +04:00
if ( tevent_req_nterror ( req , status ) ) {
2008-12-20 00:58:34 +03:00
return ;
}
state - > written + = written ;
if ( state - > written > state - > size ) {
2009-04-09 00:54:28 +04:00
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
2008-12-20 00:58:34 +03:00
return ;
}
to_write = state - > size - state - > written ;
if ( to_write = = 0 ) {
2009-04-09 00:54:28 +04:00
tevent_req_done ( req ) ;
2008-12-20 00:58:34 +03:00
return ;
}
subreq = cli_write_andx_send ( state , state - > ev , state - > cli , state - > fnum ,
state - > mode ,
state - > buf + state - > written ,
state - > offset + state - > written , to_write ) ;
2009-04-09 00:54:28 +04:00
if ( tevent_req_nomem ( subreq , req ) ) {
2008-12-20 00:58:34 +03:00
return ;
}
2009-03-29 15:28:18 +04:00
tevent_req_set_callback ( subreq , cli_writeall_written , req ) ;
2008-12-20 00:58:34 +03:00
}
2011-04-26 21:52:39 +04:00
static NTSTATUS cli_writeall_recv ( struct tevent_req * req ,
size_t * pwritten )
2008-12-20 00:58:34 +03:00
{
2011-04-26 21:52:39 +04:00
struct cli_writeall_state * state = tevent_req_data (
req , struct cli_writeall_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
return status ;
}
if ( pwritten ! = NULL ) {
* pwritten = state - > written ;
}
return NT_STATUS_OK ;
}
NTSTATUS cli_writeall ( struct cli_state * cli , uint16_t fnum , uint16_t mode ,
const uint8_t * buf , off_t offset , size_t size ,
size_t * pwritten )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2013-02-18 12:59:58 +04:00
struct tevent_context * ev ;
2011-04-26 21:52:39 +04:00
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
2012-05-26 13:45:09 +04:00
if ( smbXcli_conn_has_async_calls ( cli - > conn ) ) {
2011-04-26 21:52:39 +04:00
/*
* Can ' t use sync call while an async call is in flight
*/
status = NT_STATUS_INVALID_PARAMETER ;
goto fail ;
}
2013-02-18 12:08:19 +04:00
ev = samba_tevent_context_init ( frame ) ;
2011-04-26 21:52:39 +04:00
if ( ev = = NULL ) {
goto fail ;
}
2013-08-13 20:38:57 +04:00
if ( smbXcli_conn_protocol ( cli - > conn ) > = PROTOCOL_SMB2_02 ) {
req = cli_smb2_writeall_send ( frame , ev , cli , fnum , mode ,
buf , offset , size ) ;
} else {
req = cli_writeall_send ( frame , ev , cli , fnum , mode ,
buf , offset , size ) ;
}
2011-04-26 21:52:39 +04:00
if ( req = = NULL ) {
goto fail ;
}
2015-04-28 17:00:06 +03:00
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
2011-04-26 21:52:39 +04:00
goto fail ;
}
2013-08-13 20:38:57 +04:00
if ( smbXcli_conn_protocol ( cli - > conn ) > = PROTOCOL_SMB2_02 ) {
status = cli_smb2_writeall_recv ( req , pwritten ) ;
} else {
status = cli_writeall_recv ( req , pwritten ) ;
}
2011-04-26 21:52:39 +04:00
fail :
TALLOC_FREE ( frame ) ;
return status ;
2008-12-20 00:58:34 +03:00
}
2013-08-13 16:10:59 +04:00
struct cli_push_chunk ;
2008-12-20 00:58:34 +03:00
2009-03-10 12:56:33 +03:00
struct cli_push_state {
2013-02-18 12:59:58 +04:00
struct tevent_context * ev ;
2008-12-20 00:58:34 +03:00
struct cli_state * cli ;
uint16_t fnum ;
uint16_t mode ;
off_t start_offset ;
2009-03-12 11:02:02 +03:00
size_t ( * source ) ( uint8_t * buf , size_t n , void * priv ) ;
2008-12-20 00:58:34 +03:00
void * priv ;
bool eof ;
2009-03-10 12:56:33 +03:00
size_t chunk_size ;
off_t next_offset ;
2008-12-20 00:58:34 +03:00
/*
* Outstanding requests
2013-08-13 16:10:59 +04:00
*
* The maximum is 256 :
* - which would be a window of 256 MByte
* for SMB2 with multi - credit
* or smb1 unix extentions .
2008-12-20 00:58:34 +03:00
*/
2013-08-13 16:10:59 +04:00
uint16_t max_chunks ;
uint16_t num_chunks ;
uint16_t num_waiting ;
struct cli_push_chunk * chunks ;
2008-12-20 00:58:34 +03:00
} ;
2013-08-13 16:10:59 +04:00
struct cli_push_chunk {
struct cli_push_chunk * prev , * next ;
struct tevent_req * req ; /* This is the main request! Not the subreq */
2009-04-09 00:54:28 +04:00
struct tevent_req * subreq ;
2013-08-13 16:10:59 +04:00
off_t ofs ;
uint8_t * buf ;
size_t total_size ;
size_t tmp_size ;
bool done ;
} ;
2009-03-10 12:56:33 +03:00
2013-08-13 16:10:59 +04:00
static void cli_push_setup_chunks ( struct tevent_req * req ) ;
static void cli_push_chunk_ship ( struct cli_push_chunk * chunk ) ;
static void cli_push_chunk_done ( struct tevent_req * subreq ) ;
2009-03-10 12:56:33 +03:00
2013-02-18 12:59:58 +04:00
struct tevent_req * cli_push_send ( TALLOC_CTX * mem_ctx , struct tevent_context * ev ,
2009-04-09 00:54:28 +04:00
struct cli_state * cli ,
uint16_t fnum , uint16_t mode ,
off_t start_offset , size_t window_size ,
size_t ( * source ) ( uint8_t * buf , size_t n ,
void * priv ) ,
void * priv )
2008-12-20 00:58:34 +03:00
{
2009-04-09 00:54:28 +04:00
struct tevent_req * req ;
2008-12-20 00:58:34 +03:00
struct cli_push_state * state ;
2011-09-12 04:48:25 +04:00
size_t page_size = 1024 ;
2013-08-13 16:10:59 +04:00
uint64_t tmp64 ;
2008-12-20 00:58:34 +03:00
2009-04-09 00:54:28 +04:00
req = tevent_req_create ( mem_ctx , & state , struct cli_push_state ) ;
if ( req = = NULL ) {
2009-01-18 18:38:30 +03:00
return NULL ;
2008-12-20 00:58:34 +03:00
}
state - > cli = cli ;
state - > ev = ev ;
state - > fnum = fnum ;
state - > start_offset = start_offset ;
state - > mode = mode ;
state - > source = source ;
state - > priv = priv ;
2009-03-10 12:56:33 +03:00
state - > next_offset = start_offset ;
2008-12-20 00:58:34 +03:00
2013-08-13 18:33:30 +04:00
if ( smbXcli_conn_protocol ( state - > cli - > conn ) > = PROTOCOL_SMB2_02 ) {
state - > chunk_size = smb2cli_conn_max_write_size ( cli - > conn ) ;
} else {
state - > chunk_size = cli_write_max_bufsize ( cli , mode , 14 ) ;
}
2011-09-12 04:48:25 +04:00
if ( state - > chunk_size > page_size ) {
state - > chunk_size & = ~ ( page_size - 1 ) ;
}
2008-12-20 00:58:34 +03:00
2009-03-10 12:56:33 +03:00
if ( window_size = = 0 ) {
2013-08-13 16:10:59 +04:00
/*
* We use 16 MByte as default window size .
*/
window_size = 16 * 1024 * 1024 ;
2009-03-10 12:56:33 +03:00
}
2013-08-13 16:10:59 +04:00
tmp64 = window_size / state - > chunk_size ;
2009-03-10 12:56:33 +03:00
if ( ( window_size % state - > chunk_size ) > 0 ) {
2013-08-13 16:10:59 +04:00
tmp64 + = 1 ;
2009-03-10 12:56:33 +03:00
}
2013-08-13 16:10:59 +04:00
tmp64 = MAX ( tmp64 , 1 ) ;
tmp64 = MIN ( tmp64 , 256 ) ;
state - > max_chunks = tmp64 ;
2008-12-20 00:58:34 +03:00
2013-08-13 16:10:59 +04:00
/*
* We defer the callback because of the complex
* substate / subfunction logic
*/
tevent_req_defer_callback ( req , ev ) ;
cli_push_setup_chunks ( req ) ;
if ( ! tevent_req_is_in_progress ( req ) ) {
return tevent_req_post ( req , ev ) ;
2008-12-20 00:58:34 +03:00
}
2013-08-13 16:10:59 +04:00
return req ;
}
static void cli_push_setup_chunks ( struct tevent_req * req )
{
struct cli_push_state * state =
tevent_req_data ( req ,
struct cli_push_state ) ;
struct cli_push_chunk * chunk , * next = NULL ;
size_t i ;
for ( chunk = state - > chunks ; chunk ; chunk = next ) {
/*
* Note that chunk might be removed from this call .
*/
next = chunk - > next ;
cli_push_chunk_ship ( chunk ) ;
if ( ! tevent_req_is_in_progress ( req ) ) {
return ;
}
}
for ( i = state - > num_chunks ; i < state - > max_chunks ; i + + ) {
if ( state - > num_waiting > 0 ) {
return ;
2008-12-20 00:58:34 +03:00
}
2009-03-10 12:56:33 +03:00
if ( state - > eof ) {
break ;
}
2013-08-13 16:10:59 +04:00
chunk = talloc_zero ( state , struct cli_push_chunk ) ;
if ( tevent_req_nomem ( chunk , req ) ) {
return ;
}
chunk - > req = req ;
chunk - > ofs = state - > next_offset ;
chunk - > buf = talloc_array ( chunk ,
uint8_t ,
state - > chunk_size ) ;
if ( tevent_req_nomem ( chunk - > buf , req ) ) {
return ;
}
chunk - > total_size = state - > source ( chunk - > buf ,
state - > chunk_size ,
state - > priv ) ;
if ( chunk - > total_size = = 0 ) {
/* nothing to send */
talloc_free ( chunk ) ;
state - > eof = true ;
break ;
}
state - > next_offset + = chunk - > total_size ;
DLIST_ADD_END ( state - > chunks , chunk , NULL ) ;
state - > num_chunks + + ;
state - > num_waiting + + ;
cli_push_chunk_ship ( chunk ) ;
if ( ! tevent_req_is_in_progress ( req ) ) {
return ;
}
2008-12-20 00:58:34 +03:00
}
2013-08-13 16:10:59 +04:00
if ( ! state - > eof ) {
return ;
2008-12-20 00:58:34 +03:00
}
2013-08-13 16:10:59 +04:00
if ( state - > num_chunks > 0 ) {
return ;
}
tevent_req_done ( req ) ;
}
static void cli_push_chunk_ship ( struct cli_push_chunk * chunk )
{
struct tevent_req * req = chunk - > req ;
struct cli_push_state * state =
tevent_req_data ( req ,
struct cli_push_state ) ;
bool ok ;
const uint8_t * buf ;
off_t ofs ;
size_t size ;
if ( chunk - > done ) {
DLIST_REMOVE ( state - > chunks , chunk ) ;
SMB_ASSERT ( state - > num_chunks > 0 ) ;
state - > num_chunks - - ;
TALLOC_FREE ( chunk ) ;
return ;
}
if ( chunk - > subreq ! = NULL ) {
return ;
}
SMB_ASSERT ( state - > num_waiting > 0 ) ;
buf = chunk - > buf + chunk - > tmp_size ;
ofs = chunk - > ofs + chunk - > tmp_size ;
size = chunk - > total_size - chunk - > tmp_size ;
2013-08-13 18:33:30 +04:00
if ( smbXcli_conn_protocol ( state - > cli - > conn ) > = PROTOCOL_SMB2_02 ) {
uint32_t max_size ;
2013-08-13 16:10:59 +04:00
2013-08-13 18:33:30 +04:00
ok = smb2cli_conn_req_possible ( state - > cli - > conn , & max_size ) ;
if ( ! ok ) {
return ;
}
/*
* downgrade depending on the available credits
*/
size = MIN ( max_size , size ) ;
chunk - > subreq = cli_smb2_write_send ( chunk ,
state - > ev ,
state - > cli ,
state - > fnum ,
state - > mode ,
buf ,
ofs ,
size ) ;
if ( tevent_req_nomem ( chunk - > subreq , req ) ) {
return ;
}
} else {
ok = smb1cli_conn_req_possible ( state - > cli - > conn ) ;
if ( ! ok ) {
return ;
}
chunk - > subreq = cli_write_andx_send ( chunk ,
state - > ev ,
state - > cli ,
state - > fnum ,
state - > mode ,
buf ,
ofs ,
size ) ;
if ( tevent_req_nomem ( chunk - > subreq , req ) ) {
return ;
}
2013-08-13 16:10:59 +04:00
}
tevent_req_set_callback ( chunk - > subreq ,
cli_push_chunk_done ,
chunk ) ;
2008-12-20 00:58:34 +03:00
2013-08-13 16:10:59 +04:00
state - > num_waiting - - ;
return ;
2008-12-20 00:58:34 +03:00
}
2013-08-13 16:10:59 +04:00
static void cli_push_chunk_done ( struct tevent_req * subreq )
2008-12-20 00:58:34 +03:00
{
2013-08-13 16:10:59 +04:00
struct cli_push_chunk * chunk =
tevent_req_callback_data ( subreq ,
struct cli_push_chunk ) ;
struct tevent_req * req = chunk - > req ;
struct cli_push_state * state =
tevent_req_data ( req ,
struct cli_push_state ) ;
2008-12-20 00:58:34 +03:00
NTSTATUS status ;
2013-08-13 16:10:59 +04:00
size_t expected = chunk - > total_size - chunk - > tmp_size ;
size_t written ;
2008-12-20 00:58:34 +03:00
2013-08-13 16:10:59 +04:00
chunk - > subreq = NULL ;
2008-12-20 00:58:34 +03:00
2013-08-13 18:33:30 +04:00
if ( smbXcli_conn_protocol ( state - > cli - > conn ) > = PROTOCOL_SMB2_02 ) {
status = cli_smb2_write_recv ( subreq , & written ) ;
} else {
status = cli_write_andx_recv ( subreq , & written ) ;
}
2009-03-10 12:56:33 +03:00
TALLOC_FREE ( subreq ) ;
2011-04-02 18:02:23 +04:00
if ( tevent_req_nterror ( req , status ) ) {
2008-12-20 00:58:34 +03:00
return ;
}
2013-08-13 16:10:59 +04:00
if ( written > expected ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
2008-12-20 00:58:34 +03:00
}
2013-08-13 16:10:59 +04:00
if ( written = = 0 ) {
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
2008-12-20 00:58:34 +03:00
return ;
}
2013-08-13 16:10:59 +04:00
chunk - > tmp_size + = written ;
if ( chunk - > tmp_size = = chunk - > total_size ) {
chunk - > done = true ;
} else {
state - > num_waiting + + ;
}
cli_push_setup_chunks ( req ) ;
2008-12-20 00:58:34 +03:00
}
2009-04-09 00:54:28 +04:00
NTSTATUS cli_push_recv ( struct tevent_req * req )
2008-12-20 00:58:34 +03:00
{
2009-04-09 00:54:28 +04:00
return tevent_req_simple_recv_ntstatus ( req ) ;
2008-12-20 00:58:34 +03:00
}
NTSTATUS cli_push ( struct cli_state * cli , uint16_t fnum , uint16_t mode ,
off_t start_offset , size_t window_size ,
2009-03-12 11:02:02 +03:00
size_t ( * source ) ( uint8_t * buf , size_t n , void * priv ) ,
2008-12-20 00:58:34 +03:00
void * priv )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2013-02-18 12:59:58 +04:00
struct tevent_context * ev ;
2009-04-09 00:54:28 +04:00
struct tevent_req * req ;
2009-04-05 08:33:24 +04:00
NTSTATUS status = NT_STATUS_OK ;
2008-12-20 00:58:34 +03:00
2012-05-26 13:45:09 +04:00
if ( smbXcli_conn_has_async_calls ( cli - > conn ) ) {
2008-12-20 00:58:34 +03:00
/*
* Can ' t use sync call while an async call is in flight
*/
2009-04-05 08:33:24 +04:00
status = NT_STATUS_INVALID_PARAMETER ;
goto fail ;
2008-12-20 00:58:34 +03:00
}
2013-02-18 12:08:19 +04:00
ev = samba_tevent_context_init ( frame ) ;
2008-12-20 00:58:34 +03:00
if ( ev = = NULL ) {
2009-04-05 08:33:24 +04:00
status = NT_STATUS_NO_MEMORY ;
goto fail ;
2008-12-20 00:58:34 +03:00
}
req = cli_push_send ( frame , ev , cli , fnum , mode , start_offset ,
2009-03-12 11:02:02 +03:00
window_size , source , priv ) ;
2008-12-20 00:58:34 +03:00
if ( req = = NULL ) {
2009-04-05 08:33:24 +04:00
status = NT_STATUS_NO_MEMORY ;
goto fail ;
2008-12-20 00:58:34 +03:00
}
2015-04-28 17:00:06 +03:00
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
2009-04-09 00:54:28 +04:00
goto fail ;
2008-12-20 00:58:34 +03:00
}
2009-04-05 08:33:24 +04:00
status = cli_push_recv ( req ) ;
fail :
2008-12-20 00:58:34 +03:00
TALLOC_FREE ( frame ) ;
2009-04-05 08:33:24 +04:00
return status ;
2008-12-20 00:58:34 +03:00
}
2015-05-28 01:13:15 +03:00
# define SPLICE_BLOCK_SIZE 1024 * 1024
static NTSTATUS cli_splice_fallback ( TALLOC_CTX * frame ,
struct cli_state * srccli ,
struct cli_state * dstcli ,
uint16_t src_fnum , uint16_t dst_fnum ,
off_t initial_size ,
off_t src_offset , off_t dst_offset ,
off_t * written ,
int ( * splice_cb ) ( off_t n , void * priv ) ,
void * priv )
{
NTSTATUS status ;
uint8_t * buf = talloc_size ( frame , SPLICE_BLOCK_SIZE ) ;
size_t nread ;
off_t remaining = initial_size ;
while ( remaining ) {
status = cli_read ( srccli , src_fnum ,
( char * ) buf , src_offset , SPLICE_BLOCK_SIZE ,
& nread ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
status = cli_writeall ( dstcli , dst_fnum , 0 ,
buf , dst_offset , nread , NULL ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
if ( ( src_offset > INT64_MAX - nread ) | |
( dst_offset > INT64_MAX - nread ) ) {
return NT_STATUS_FILE_TOO_LARGE ;
}
src_offset + = nread ;
dst_offset + = nread ;
if ( remaining < nread ) {
return NT_STATUS_INTERNAL_ERROR ;
}
remaining - = nread ;
if ( ! splice_cb ( initial_size - remaining , priv ) ) {
return NT_STATUS_CANCELLED ;
}
}
return NT_STATUS_OK ;
}
NTSTATUS cli_splice ( struct cli_state * srccli , struct cli_state * dstcli ,
uint16_t src_fnum , uint16_t dst_fnum ,
off_t size ,
off_t src_offset , off_t dst_offset ,
off_t * written ,
int ( * splice_cb ) ( off_t n , void * priv ) , void * priv )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct tevent_context * ev ;
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
bool retry_fallback = false ;
if ( smbXcli_conn_has_async_calls ( srccli - > conn ) | |
smbXcli_conn_has_async_calls ( dstcli - > conn ) )
{
/*
* Can ' t use sync call while an async call is in flight
*/
status = NT_STATUS_INVALID_PARAMETER ;
goto out ;
}
do {
ev = samba_tevent_context_init ( frame ) ;
if ( ev = = NULL ) {
goto out ;
}
if ( srccli = = dstcli & &
smbXcli_conn_protocol ( srccli - > conn ) > = PROTOCOL_SMB2_02 & &
! retry_fallback )
{
req = cli_smb2_splice_send ( frame , ev ,
srccli , src_fnum , dst_fnum ,
size , src_offset , dst_offset ,
splice_cb , priv ) ;
} else {
status = cli_splice_fallback ( frame ,
srccli , dstcli ,
src_fnum , dst_fnum ,
size ,
src_offset , dst_offset ,
written ,
splice_cb , priv ) ;
goto out ;
}
if ( req = = NULL ) {
goto out ;
}
if ( ! tevent_req_poll ( req , ev ) ) {
status = map_nt_error_from_unix ( errno ) ;
goto out ;
}
status = cli_smb2_splice_recv ( req , written ) ;
/*
* Older versions of Samba don ' t support
* FSCTL_SRV_COPYCHUNK_WRITE so use the fallback .
*/
retry_fallback = NT_STATUS_EQUAL ( status , NT_STATUS_INVALID_DEVICE_REQUEST ) ;
} while ( retry_fallback ) ;
out :
TALLOC_FREE ( frame ) ;
return status ;
}