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"
2011-05-06 11:47:43 +02:00
# include "libsmb/libsmb.h"
2011-04-28 17:38:09 +02:00
# include "../lib/util/tevent_ntstatus.h"
2010-08-26 09:58:09 +02:00
# include "async_smb.h"
2011-02-25 00:03:01 +01:00
# include "trans2.h"
2000-04-25 14:04:06 +00:00
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 )
{
2011-09-12 02:45:22 +02:00
uint8_t wct = 12 ;
uint32_t min_space ;
uint32_t data_offset ;
uint32_t useable_space = 0 ;
2011-06-08 18:59:39 +02:00
2011-09-12 02:45:22 +02:00
data_offset = HDR_VWV ;
2011-06-08 18:59:39 +02:00
data_offset + = wct * sizeof ( uint16_t ) ;
2011-09-12 02:45:22 +02:00
data_offset + = sizeof ( uint16_t ) ; /* byte count */
2011-06-08 18:59:39 +02:00
data_offset + = 1 ; /* pad */
2011-09-12 02:45:22 +02: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 ;
if ( client_is_signing_on ( cli ) ) {
return min_space ;
}
2011-09-15 16:15:18 +02:00
if ( cli_state_encryption_on ( cli ) ) {
2011-09-12 02:45:22 +02:00
return min_space ;
}
return useable_space ;
} else if ( cli_state_capabilities ( cli ) & CAP_LARGE_READX ) {
/*
* 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 18:59:39 +02:00
2011-09-12 02:45:22 +02:00
return min_space ;
2008-02-28 15:21:33 +01:00
}
2008-12-13 13:46:28 +01:00
/****************************************************************************
Calculate the recommended write buffer size
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-06-08 19:01:13 +02:00
static size_t cli_write_max_bufsize ( struct cli_state * cli ,
uint16_t write_mode ,
uint8_t wct )
2008-12-13 13:46:28 +01:00
{
2011-09-12 02:45:22 +02: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 13:46:28 +01:00
2011-09-12 02:45:22 +02: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 ;
} else if ( cli_state_capabilities ( cli ) & CAP_LARGE_WRITEX ) {
useable_space = 0x1FFFF - data_offset ;
} else {
return min_space ;
2008-12-13 13:46:28 +01:00
}
2011-09-12 02:45:22 +02:00
if ( write_mode ! = 0 ) {
return min_space ;
}
2008-12-13 13:46:28 +01:00
2011-09-12 02:45:22 +02:00
if ( client_is_signing_on ( cli ) ) {
return min_space ;
}
2011-06-08 19:01:13 +02:00
2011-09-15 16:15:18 +02:00
if ( cli_state_encryption_on ( cli ) ) {
2011-09-12 02:45:22 +02:00
return min_space ;
}
2008-12-13 13:46:28 +01:00
2011-09-12 02:45:22 +02:00
if ( strequal ( cli - > dev , " LPT1: " ) ) {
return min_space ;
2008-12-13 13:46:28 +01:00
}
2011-09-12 02:45:22 +02:00
return useable_space ;
2008-12-13 13:46:28 +01:00
}
2009-03-15 09:54:23 +01:00
struct cli_read_andx_state {
size_t size ;
uint16_t vwv [ 12 ] ;
NTSTATUS status ;
size_t received ;
uint8_t * buf ;
} ;
2008-12-13 13:46:28 +01:00
2009-03-15 09:54:23 +01:00
static void cli_read_andx_done ( struct tevent_req * subreq ) ;
2008-02-28 15:21:33 +01:00
2009-03-15 09:54:23 +01:00
struct tevent_req * cli_read_andx_create ( TALLOC_CTX * mem_ctx ,
struct event_context * ev ,
2009-04-30 15:26:43 -07:00
struct cli_state * cli , uint16_t fnum ,
2009-03-15 09:54:23 +01:00
off_t offset , size_t size ,
struct tevent_req * * psmbreq )
2008-02-28 15:21:33 +01:00
{
2009-03-15 09:54:23 +01:00
struct tevent_req * req , * subreq ;
struct cli_read_andx_state * state ;
2008-08-01 23:14:51 +02:00
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 ;
}
2009-03-15 09:54:23 +01: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-01 23:14:51 +02:00
2011-09-20 22:45:52 +02:00
if ( cli_state_capabilities ( cli ) & CAP_LARGE_FILES ) {
2009-03-15 09:54:23 +01:00
SIVAL ( state - > vwv + 10 , 0 ,
2008-10-14 01:59:36 +02:00
( ( ( uint64_t ) offset ) > > 32 ) & 0xffffffff ) ;
2011-09-20 22:45:52 +02: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-01 23:14:51 +02:00
}
2009-03-15 09:54:23 +01: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 15:21:33 +01:00
return NULL ;
}
2009-03-15 09:54:23 +01:00
tevent_req_set_callback ( subreq , cli_read_andx_done , req ) ;
* psmbreq = subreq ;
return req ;
}
2008-02-28 15:21:33 +01:00
2009-03-15 09:54:23 +01:00
struct tevent_req * cli_read_andx_send ( TALLOC_CTX * mem_ctx ,
struct event_context * ev ,
2009-04-30 15:26:43 -07:00
struct cli_state * cli , uint16_t fnum ,
2009-03-15 09:54:23 +01:00
off_t offset , size_t size )
{
struct tevent_req * req , * subreq ;
2009-05-14 10:13:12 +08:00
NTSTATUS status ;
2008-02-28 15:21:33 +01:00
2009-03-15 09:54:23 +01:00
req = cli_read_andx_create ( mem_ctx , ev , cli , fnum , offset , size ,
& subreq ) ;
2009-05-14 10:13:12 +08:00
if ( req = = NULL ) {
2009-03-15 09:54:23 +01:00
return NULL ;
}
2009-05-14 10:13:12 +08:00
status = cli_smb_req_send ( subreq ) ;
2011-04-02 16:02:23 +02:00
if ( tevent_req_nterror ( req , status ) ) {
2009-05-14 10:13:12 +08:00
return tevent_req_post ( req , ev ) ;
}
2009-03-15 09:54:23 +01:00
return req ;
2008-02-28 15:21:33 +01:00
}
2009-03-15 09:54:23 +01:00
static void cli_read_andx_done ( struct tevent_req * subreq )
2008-02-28 15:21:33 +01:00
{
2009-03-15 09:54:23 +01: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 13:33:41 +02:00
uint8_t wct ;
uint16_t * vwv ;
2009-03-15 09:54:23 +01:00
uint32_t num_bytes ;
2008-08-25 13:33:41 +02:00
uint8_t * bytes ;
2008-02-28 15:21:33 +01:00
2010-02-20 15:26:06 +01:00
state - > status = cli_smb_recv ( subreq , state , & inbuf , 12 , & wct , & vwv ,
2010-02-20 09:53:58 +01:00
& num_bytes , & bytes ) ;
2010-02-20 15:26:06 +01:00
TALLOC_FREE ( subreq ) ;
2009-03-15 09:54:23 +01:00
if ( NT_STATUS_IS_ERR ( state - > status ) ) {
tevent_req_nterror ( req , state - > status ) ;
return ;
2008-08-25 13:33:41 +02:00
}
2008-02-28 15:21:33 +01:00
/* size is the number of bytes the server returned.
* Might be zero . */
2009-03-15 09:54:23 +01:00
state - > received = SVAL ( vwv + 5 , 0 ) ;
state - > received | = ( ( ( unsigned int ) SVAL ( vwv + 7 , 0 ) ) < < 16 ) ;
2008-02-28 15:21:33 +01:00
2009-03-15 09:54:23 +01:00
if ( state - > received > state - > size ) {
2008-02-28 15:21:33 +01:00
DEBUG ( 5 , ( " server returned more than we wanted! \n " ) ) ;
2009-03-15 09:54:23 +01:00
tevent_req_nterror ( req , NT_STATUS_UNEXPECTED_IO_ERROR ) ;
return ;
2008-02-28 15:21:33 +01:00
}
2008-11-19 22:55:06 +01:00
/*
* bcc field must be valid for small reads , for large reads the 16 - bit
* bcc field can ' t be correct .
*/
2009-03-15 09:54:23 +01:00
if ( ( state - > received < 0xffff ) & & ( state - > received > num_bytes ) ) {
2008-11-19 22:55:06 +01:00
DEBUG ( 5 , ( " server announced more bytes than sent \n " ) ) ;
2009-03-15 09:54:23 +01:00
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
2008-11-19 22:55:06 +01:00
}
2011-05-05 13:42:05 -07:00
state - > buf = discard_const_p ( uint8_t , smb_base ( inbuf ) ) + SVAL ( vwv + 6 , 0 ) ;
2008-11-19 22:55:06 +01:00
2012-01-27 11:48:11 +11:00
if ( trans_oob ( smb_len_tcp ( inbuf ) , SVAL ( vwv + 6 , 0 ) , state - > received )
2009-09-03 08:02:21 +02:00
| | ( ( state - > received ! = 0 ) & & ( state - > buf < bytes ) ) ) {
2008-11-19 22:55:06 +01:00
DEBUG ( 5 , ( " server returned invalid read&x data offset \n " ) ) ;
2009-03-15 09:54:23 +01:00
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
return ;
2008-11-19 22:55:06 +01:00
}
2009-03-15 09:54:23 +01: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-19 22:55:06 +01:00
2009-03-15 09:54:23 +01:00
if ( tevent_req_is_nterror ( req , & status ) ) {
return status ;
}
* received = state - > received ;
* rcvbuf = state - > buf ;
2008-02-28 15:21:33 +01:00
return NT_STATUS_OK ;
}
2009-09-14 03:21:19 +02:00
struct cli_readall_state {
struct tevent_context * ev ;
struct cli_state * cli ;
uint16_t fnum ;
off_t start_offset ;
size_t size ;
size_t received ;
uint8_t * buf ;
} ;
static void cli_readall_done ( struct tevent_req * subreq ) ;
static struct tevent_req * cli_readall_send ( TALLOC_CTX * mem_ctx ,
struct event_context * ev ,
struct cli_state * cli ,
uint16_t fnum ,
off_t offset , size_t size )
{
struct tevent_req * req , * subreq ;
struct cli_readall_state * state ;
req = tevent_req_create ( mem_ctx , & state , struct cli_readall_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > cli = cli ;
state - > fnum = fnum ;
state - > start_offset = offset ;
state - > size = size ;
state - > received = 0 ;
state - > buf = NULL ;
subreq = cli_read_andx_send ( state , ev , cli , fnum , offset , size ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , cli_readall_done , req ) ;
return req ;
}
static void cli_readall_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct cli_readall_state * state = tevent_req_data (
req , struct cli_readall_state ) ;
ssize_t received ;
uint8_t * buf ;
NTSTATUS status ;
status = cli_read_andx_recv ( subreq , & received , & buf ) ;
2011-04-02 16:02:23 +02:00
if ( tevent_req_nterror ( req , status ) ) {
2009-09-14 03:21:19 +02:00
return ;
}
2009-09-14 19:09:54 -07:00
if ( received = = 0 ) {
/* EOF */
tevent_req_done ( req ) ;
return ;
}
2009-09-14 03:21:19 +02:00
if ( ( state - > received = = 0 ) & & ( received = = state - > size ) ) {
/* Ideal case: Got it all in one run */
state - > buf = buf ;
state - > received + = received ;
tevent_req_done ( req ) ;
return ;
}
/*
* We got a short read , issue a read for the
* rest . Unfortunately we have to allocate the buffer
* ourselves now , as our caller expects to receive a single
* buffer . cli_read_andx does it from the buffer received from
* the net , but with a short read we have to put it together
* from several reads .
*/
if ( state - > buf = = NULL ) {
state - > buf = talloc_array ( state , uint8_t , state - > size ) ;
if ( tevent_req_nomem ( state - > buf , req ) ) {
return ;
}
}
memcpy ( state - > buf + state - > received , buf , received ) ;
state - > received + = received ;
TALLOC_FREE ( subreq ) ;
if ( state - > received > = state - > size ) {
tevent_req_done ( req ) ;
return ;
}
subreq = cli_read_andx_send ( state , state - > ev , state - > cli , state - > fnum ,
state - > start_offset + state - > received ,
state - > size - state - > received ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , cli_readall_done , req ) ;
}
static NTSTATUS cli_readall_recv ( struct tevent_req * req , ssize_t * received ,
uint8_t * * rcvbuf )
{
struct cli_readall_state * state = tevent_req_data (
req , struct cli_readall_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
return status ;
}
* received = state - > received ;
* rcvbuf = state - > buf ;
return NT_STATUS_OK ;
}
2009-03-15 09:54:23 +01:00
struct cli_pull_subreq {
struct tevent_req * req ;
ssize_t received ;
uint8_t * buf ;
} ;
2008-02-28 15:21:33 +01: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 .
*/
struct cli_pull_state {
2009-04-08 22:39:55 +02:00
struct tevent_req * req ;
2008-02-28 15:21:33 +01:00
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 ;
2012-04-05 14:53:08 +10:00
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
*/
2011-09-13 16:30:30 +02:00
uint16_t max_reqs ;
2008-02-28 15:21:33 +01:00
int num_reqs ;
2009-03-15 09:54:23 +01:00
struct cli_pull_subreq * reqs ;
2008-02-28 15:21:33 +01:00
/*
* For how many bytes did we send requests already ?
*/
2012-04-05 14:53:08 +10:00
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 " ?
*/
2012-04-05 14:53:08 +10:00
off_t pushed ;
2008-02-28 15:21:33 +01:00
} ;
2009-04-08 22:39:55 +02:00
static char * cli_pull_print ( struct tevent_req * req , TALLOC_CTX * mem_ctx )
2008-02-28 15:21:33 +01:00
{
2009-04-08 22:39:55 +02:00
struct cli_pull_state * state = tevent_req_data (
req , struct cli_pull_state ) ;
2008-02-28 15:21:33 +01:00
char * result ;
2010-05-09 00:42:54 +02:00
result = tevent_req_default_print ( req , mem_ctx ) ;
2008-02-28 15:21:33 +01:00
if ( result = = NULL ) {
return NULL ;
}
return talloc_asprintf_append_buffer (
result , " num_reqs=%d, top_req=%d " ,
state - > num_reqs , state - > top_req ) ;
}
2009-03-15 09:54:23 +01:00
static void cli_pull_read_done ( struct tevent_req * read_req ) ;
2008-02-28 15:21:33 +01:00
/*
* Prepare an async pull request
*/
2009-04-08 22:39:55 +02:00
struct tevent_req * cli_pull_send ( TALLOC_CTX * mem_ctx ,
struct event_context * ev ,
struct cli_state * cli ,
uint16_t fnum , off_t start_offset ,
2012-04-05 14:53:08 +10:00
off_t size , size_t window_size ,
2009-04-08 22:39:55 +02:00
NTSTATUS ( * sink ) ( char * buf , size_t n ,
void * priv ) ,
void * priv )
2008-02-28 15:21:33 +01:00
{
2009-04-08 22:39:55 +02:00
struct tevent_req * req ;
2008-02-28 15:21:33 +01:00
struct cli_pull_state * state ;
int i ;
2011-09-12 02:48:25 +02:00
size_t page_size = 1024 ;
2008-02-28 15:21:33 +01:00
2009-04-08 22:39:55 +02:00
req = tevent_req_create ( mem_ctx , & state , struct cli_pull_state ) ;
if ( req = = NULL ) {
2009-01-18 16:38:30 +01:00
return NULL ;
2008-02-28 15:21:33 +01:00
}
2009-04-08 22:39:55 +02:00
tevent_req_set_print_fn ( req , cli_pull_print ) ;
state - > req = req ;
2008-02-28 15:21:33 +01:00
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 ) {
2009-04-08 22:39:55 +02:00
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
2008-03-18 13:20:10 +01:00
}
2008-02-28 15:21:33 +01:00
state - > chunk_size = cli_read_max_bufsize ( cli ) ;
2011-09-12 02:48:25 +02:00
if ( state - > chunk_size > page_size ) {
state - > chunk_size & = ~ ( page_size - 1 ) ;
}
2008-02-28 15:21:33 +01:00
2011-09-13 16:30:30 +02:00
state - > max_reqs = cli_state_max_requests ( cli ) ;
2008-02-28 15:21:33 +01:00
state - > num_reqs = MAX ( window_size / state - > chunk_size , 1 ) ;
2011-09-13 16:30:30 +02:00
state - > num_reqs = MIN ( state - > num_reqs , state - > max_reqs ) ;
2008-02-28 15:21:33 +01:00
2011-06-07 11:58:39 +10:00
state - > reqs = talloc_zero_array ( state , struct cli_pull_subreq ,
2008-02-28 15:21:33 +01:00
state - > num_reqs ) ;
if ( state - > reqs = = NULL ) {
goto failed ;
}
state - > requested = 0 ;
for ( i = 0 ; i < state - > num_reqs ; i + + ) {
2009-03-15 09:54:23 +01:00
struct cli_pull_subreq * subreq = & state - > reqs [ i ] ;
2012-04-05 14:53:08 +10:00
off_t size_left ;
2008-03-21 13:39:48 +01:00
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 ) ;
2009-09-14 03:21:19 +02:00
subreq - > req = cli_readall_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 ) ;
2009-03-15 09:54:23 +01:00
if ( subreq - > req = = NULL ) {
2008-02-28 15:21:33 +01:00
goto failed ;
}
2009-04-08 22:39:55 +02:00
tevent_req_set_callback ( subreq - > req , cli_pull_read_done , req ) ;
2008-02-28 15:21:33 +01:00
state - > requested + = request_thistime ;
}
2009-04-08 22:39:55 +02:00
return req ;
2008-02-28 15:21:33 +01:00
failed :
2009-04-08 22:39:55 +02:00
TALLOC_FREE ( req ) ;
2008-02-28 15:21:33 +01:00
return NULL ;
}
/*
* Handle incoming read replies , push the data into sink and send out new
* requests if necessary .
*/
2009-03-15 09:54:23 +01:00
static void cli_pull_read_done ( struct tevent_req * subreq )
2008-02-28 15:21:33 +01:00
{
2009-04-08 22:39:55 +02:00
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct cli_pull_state * state = tevent_req_data (
req , struct cli_pull_state ) ;
2009-03-15 09:54:23 +01:00
struct cli_pull_subreq * pull_subreq = NULL ;
2008-02-28 15:21:33 +01:00
NTSTATUS status ;
2009-03-15 09:54:23 +01:00
int i ;
2008-02-28 15:21:33 +01:00
2009-03-15 09:54:23 +01:00
for ( i = 0 ; i < state - > num_reqs ; i + + ) {
pull_subreq = & state - > reqs [ i ] ;
if ( subreq = = pull_subreq - > req ) {
break ;
}
}
if ( i = = state - > num_reqs ) {
/* Huh -- received something we did not send?? */
2009-04-08 22:39:55 +02:00
tevent_req_nterror ( req , NT_STATUS_INTERNAL_ERROR ) ;
2009-03-15 09:54:23 +01:00
return ;
}
2009-09-14 03:21:19 +02:00
status = cli_readall_recv ( subreq , & pull_subreq - > received ,
& pull_subreq - > buf ) ;
2008-02-28 15:21:33 +01:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2009-04-08 22:39:55 +02:00
tevent_req_nterror ( state - > req , status ) ;
2008-02-28 15:21:33 +01:00
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 .
*/
2009-03-15 09:54:23 +01:00
while ( state - > reqs [ state - > top_req ] . req ! = NULL ) {
struct cli_pull_subreq * top_subreq ;
2008-02-28 15:21:33 +01:00
DEBUG ( 11 , ( " cli_pull_read_done: top_req = %d \n " ,
state - > top_req ) ) ;
2009-03-15 09:54:23 +01:00
top_subreq = & state - > reqs [ state - > top_req ] ;
if ( tevent_req_is_in_progress ( top_subreq - > req ) ) {
2008-02-28 15:21:33 +01:00
DEBUG ( 11 , ( " cli_pull_read_done: top request not yet "
" done \n " ) ) ;
return ;
}
DEBUG ( 10 , ( " cli_pull_read_done: Pushing %d bytes, %d already "
2009-03-15 09:54:23 +01:00
" pushed \n " , ( int ) top_subreq - > received ,
2008-02-28 15:21:33 +01:00
( int ) state - > pushed ) ) ;
2009-03-15 09:54:23 +01:00
status = state - > sink ( ( char * ) top_subreq - > buf ,
top_subreq - > received , state - > priv ) ;
2011-04-02 16:02:23 +02:00
if ( tevent_req_nterror ( state - > req , status ) ) {
2008-02-28 15:21:33 +01:00
return ;
}
2009-03-15 09:54:23 +01:00
state - > pushed + = top_subreq - > received ;
2008-02-28 15:21:33 +01:00
2009-03-15 09:54:23 +01:00
TALLOC_FREE ( state - > reqs [ state - > top_req ] . req ) ;
2008-02-28 15:21:33 +01:00
if ( state - > requested < state - > size ) {
2009-03-15 09:54:23 +01:00
struct tevent_req * new_req ;
2012-04-05 14:53:08 +10:00
off_t size_left ;
2008-03-21 13:39:48 +01:00
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 ) ) ;
2009-09-14 03:21:19 +02:00
new_req = cli_readall_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 ) ;
2009-04-08 22:39:55 +02:00
if ( tevent_req_nomem ( new_req , state - > req ) ) {
2008-02-28 15:21:33 +01:00
return ;
}
2009-03-15 09:54:23 +01:00
tevent_req_set_callback ( new_req , cli_pull_read_done ,
req ) ;
2008-02-28 15:21:33 +01:00
2009-03-15 09:54:23 +01:00
state - > reqs [ state - > top_req ] . req = new_req ;
2008-02-28 15:21:33 +01:00
state - > requested + = request_thistime ;
}
state - > top_req = ( state - > top_req + 1 ) % state - > num_reqs ;
}
2009-04-08 22:39:55 +02:00
tevent_req_done ( req ) ;
2008-02-28 15:21:33 +01:00
}
2012-04-05 14:53:08 +10:00
NTSTATUS cli_pull_recv ( struct tevent_req * req , off_t * received )
2008-02-28 15:21:33 +01:00
{
2009-04-08 22:39:55 +02:00
struct cli_pull_state * state = tevent_req_data (
req , struct cli_pull_state ) ;
2008-12-01 08:23:35 +01:00
NTSTATUS status ;
2008-02-28 15:21:33 +01:00
2009-04-08 22:39:55 +02:00
if ( tevent_req_is_nterror ( req , & status ) ) {
2008-12-01 08:23:35 +01:00
return status ;
2008-02-28 15:21:33 +01:00
}
* received = state - > pushed ;
return NT_STATUS_OK ;
}
NTSTATUS cli_pull ( struct cli_state * cli , uint16_t fnum ,
2012-04-05 14:53:08 +10:00
off_t start_offset , off_t size , size_t window_size ,
2008-02-28 15:21:33 +01:00
NTSTATUS ( * sink ) ( char * buf , size_t n , void * priv ) ,
2012-04-05 14:53:08 +10:00
void * priv , 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 ;
2009-04-08 22:39:55 +02:00
struct tevent_req * req ;
2009-04-05 06:33:17 +02:00
NTSTATUS status = NT_STATUS_OK ;
2008-02-28 15:21:33 +01:00
2009-04-05 06:33:17 +02:00
if ( cli_has_async_calls ( cli ) ) {
2008-08-24 14:17:43 +02:00
/*
* Can ' t use sync call while an async call is in flight
*/
2009-04-05 06:33:17 +02:00
status = NT_STATUS_INVALID_PARAMETER ;
goto fail ;
2008-08-24 14:17:43 +02:00
}
ev = event_context_init ( frame ) ;
if ( ev = = NULL ) {
2009-04-05 06:33:17 +02:00
status = NT_STATUS_NO_MEMORY ;
goto fail ;
2008-02-28 15:21:33 +01:00
}
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 ) {
2009-04-05 06:33:17 +02:00
status = NT_STATUS_NO_MEMORY ;
goto fail ;
2008-02-28 15:21:33 +01:00
}
2009-04-08 22:39:55 +02:00
if ( ! tevent_req_poll ( req , ev ) ) {
status = map_nt_error_from_unix ( errno ) ;
goto fail ;
2008-02-28 15:21:33 +01:00
}
2009-04-05 06:33:17 +02:00
status = cli_pull_recv ( req , received ) ;
fail :
2008-02-28 15:21:33 +01:00
TALLOC_FREE ( frame ) ;
2009-04-05 06:33:17 +02:00
return status ;
2008-02-28 15:21:33 +01:00
}
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
}
2011-07-19 11:11:27 +02: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 14:53:08 +10:00
off_t ret ;
2011-07-19 11:11:27 +02: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 14:04:06 +00:00
/****************************************************************************
write to a file using a SMBwrite and not bypassing 0 byte writes
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-06-29 00:22:22 +00:00
2011-03-27 17:04:56 +02: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 14:04:06 +00:00
{
2011-03-27 17:04:56 +02:00
uint8_t * bytes ;
2000-04-25 14:04:06 +00:00
ssize_t total = 0 ;
2011-03-27 17:04:56 +02:00
/*
* 3 bytes prefix
*/
2007-11-02 12:21:34 -07:00
2011-06-07 11:30:12 +10:00
bytes = talloc_array ( talloc_tos ( ) , uint8_t , 3 ) ;
2011-03-27 17:04:56 +02:00
if ( bytes = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
bytes [ 0 ] = 1 ;
2001-06-29 00:22:22 +00:00
2011-03-27 17:04:56 +02:00
do {
2011-09-13 13:10:33 +02:00
uint32_t usable_space = cli_state_available_size ( cli , 48 ) ;
size_t size = MIN ( size1 , usable_space ) ;
2011-03-27 17:04:56 +02: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 11:10:15 +10:00
bytes = talloc_realloc ( talloc_tos ( ) , bytes , uint8_t ,
2011-03-27 17:04:56 +02:00
size + 3 ) ;
if ( bytes = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
SSVAL ( bytes , 1 , size ) ;
memcpy ( bytes + 3 , buf + total , size ) ;
2007-11-02 12:21:34 -07:00
2011-03-27 17:04:56 +02: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 14:04:06 +00:00
2011-03-27 17:04:56 +02:00
size = SVAL ( ret_vwv + 0 , 0 ) ;
TALLOC_FREE ( req ) ;
if ( size = = 0 ) {
2001-06-29 00:22:22 +00:00
break ;
2011-03-27 17:04:56 +02:00
}
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 ) ;
2011-03-27 17:04:56 +02:00
TALLOC_FREE ( bytes ) ;
if ( ptotal ! = NULL ) {
* ptotal = total ;
}
return NT_STATUS_OK ;
2000-04-25 14:04:06 +00:00
}
2008-12-19 22:58:34 +01:00
/*
* Send a write & x request
*/
2009-03-29 13:28:18 +02: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 ,
struct event_context * ev ,
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-19 22:58:34 +01:00
{
2009-03-29 13:28:18 +02:00
struct tevent_req * req , * subreq ;
struct cli_write_andx_state * state ;
2011-08-01 16:42:23 +02:00
bool bigoffset = ( ( cli_state_capabilities ( cli ) & CAP_LARGE_FILES ) ! = 0 ) ;
2008-12-19 22:58:34 +01:00
uint8_t wct = bigoffset ? 14 : 12 ;
2011-06-08 19:01:13 +02:00
size_t max_write = cli_write_max_bufsize ( cli , mode , wct ) ;
2009-03-29 13:28:18 +02:00
uint16_t * vwv ;
req = tevent_req_create ( mem_ctx , & state , struct cli_write_andx_state ) ;
if ( req = = NULL ) {
return NULL ;
}
2008-12-19 22:58:34 +01:00
2011-11-08 08:25:16 +01:00
state - > size = MIN ( size , max_write ) ;
2008-12-19 22:58:34 +01:00
2009-03-29 13:28:18 +02:00
vwv = state - > vwv ;
2008-12-19 22:58:34 +01: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 08:25:16 +01:00
SSVAL ( vwv + 9 , 0 , ( state - > size > > 16 ) ) ;
SSVAL ( vwv + 10 , 0 , state - > size ) ;
2008-12-19 22:58:34 +01:00
SSVAL ( vwv + 11 , 0 ,
2009-03-29 13:28:18 +02:00
cli_smb_wct_ofs ( reqs_before , num_reqs_before )
2008-12-19 22:58:34 +01: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 13:28:18 +02:00
state - > pad = 0 ;
2009-05-12 11:45:37 -07:00
state - > iov [ 0 ] . iov_base = ( void * ) & state - > pad ;
2009-03-29 13:28:18 +02:00
state - > iov [ 0 ] . iov_len = 1 ;
2011-05-05 13:42:05 -07:00
state - > iov [ 1 ] . iov_base = discard_const_p ( void , buf ) ;
2011-11-09 08:09:17 +01:00
state - > iov [ 1 ] . iov_len = state - > size ;
2009-03-29 13:28:18 +02: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-19 22:58:34 +01:00
}
2009-03-29 13:28:18 +02:00
struct tevent_req * cli_write_andx_send ( TALLOC_CTX * mem_ctx ,
struct event_context * ev ,
struct cli_state * cli , uint16_t fnum ,
uint16_t mode , const uint8_t * buf ,
off_t offset , size_t size )
2008-12-19 22:58:34 +01:00
{
2009-03-29 13:28:18 +02:00
struct tevent_req * req , * subreq ;
2009-05-14 10:13:12 +08:00
NTSTATUS status ;
2009-03-29 13:28:18 +02:00
req = cli_write_andx_create ( mem_ctx , ev , cli , fnum , mode , buf , offset ,
size , NULL , 0 , & subreq ) ;
2009-05-14 10:13:12 +08:00
if ( req = = NULL ) {
2009-03-29 13:28:18 +02:00
return NULL ;
}
2009-05-14 10:13:12 +08:00
status = cli_smb_req_send ( subreq ) ;
2011-04-02 16:02:23 +02:00
if ( tevent_req_nterror ( req , status ) ) {
2009-05-14 10:13:12 +08:00
return tevent_req_post ( req , ev ) ;
}
2009-03-29 13:28:18 +02: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-19 22:58:34 +01:00
uint8_t wct ;
uint16_t * vwv ;
2010-02-20 15:27:22 +01:00
uint8_t * inbuf ;
2008-12-19 22:58:34 +01:00
NTSTATUS status ;
2010-02-20 15:27:22 +01:00
status = cli_smb_recv ( subreq , state , & inbuf , 6 , & wct , & vwv ,
NULL , NULL ) ;
TALLOC_FREE ( subreq ) ;
2009-03-29 13:28:18 +02:00
if ( NT_STATUS_IS_ERR ( status ) ) {
tevent_req_nterror ( req , status ) ;
return ;
2008-12-19 22:58:34 +01:00
}
2009-03-29 13:28:18 +02:00
state - > written = SVAL ( vwv + 2 , 0 ) ;
2011-11-08 08:25:16 +01: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 13:28:18 +02:00
tevent_req_done ( req ) ;
}
2008-12-19 22:58:34 +01:00
2009-03-29 13:28:18 +02: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-19 22:58:34 +01:00
2009-03-29 13:28:18 +02:00
if ( tevent_req_is_nterror ( req , & status ) ) {
2008-12-19 22:58:34 +01:00
return status ;
}
2011-04-30 10:58:35 +02:00
if ( pwritten ! = 0 ) {
* pwritten = state - > written ;
}
2008-12-19 22:58:34 +01:00
return NT_STATUS_OK ;
}
struct cli_writeall_state {
struct event_context * ev ;
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 13:28:18 +02:00
static void cli_writeall_written ( struct tevent_req * req ) ;
2008-12-19 22:58:34 +01:00
2009-04-08 22:54:28 +02:00
static struct tevent_req * cli_writeall_send ( TALLOC_CTX * mem_ctx ,
struct event_context * ev ,
struct cli_state * cli ,
uint16_t fnum ,
uint16_t mode ,
const uint8_t * buf ,
off_t offset , size_t size )
2008-12-19 22:58:34 +01:00
{
2009-04-08 22:54:28 +02:00
struct tevent_req * req , * subreq ;
2008-12-19 22:58:34 +01:00
struct cli_writeall_state * state ;
2009-04-08 22:54:28 +02:00
req = tevent_req_create ( mem_ctx , & state , struct cli_writeall_state ) ;
if ( req = = NULL ) {
2009-01-18 16:38:30 +01:00
return NULL ;
2008-12-19 22:58:34 +01: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-08 22:54:28 +02:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
2008-12-19 22:58:34 +01:00
}
2009-04-08 22:54:28 +02:00
tevent_req_set_callback ( subreq , cli_writeall_written , req ) ;
return req ;
2008-12-19 22:58:34 +01:00
}
2009-03-29 13:28:18 +02:00
static void cli_writeall_written ( struct tevent_req * subreq )
2008-12-19 22:58:34 +01:00
{
2009-04-08 22:54:28 +02: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-19 22:58:34 +01:00
NTSTATUS status ;
size_t written , to_write ;
status = cli_write_andx_recv ( subreq , & written ) ;
TALLOC_FREE ( subreq ) ;
2011-04-02 16:02:23 +02:00
if ( tevent_req_nterror ( req , status ) ) {
2008-12-19 22:58:34 +01:00
return ;
}
state - > written + = written ;
if ( state - > written > state - > size ) {
2009-04-08 22:54:28 +02:00
tevent_req_nterror ( req , NT_STATUS_INVALID_NETWORK_RESPONSE ) ;
2008-12-19 22:58:34 +01:00
return ;
}
to_write = state - > size - state - > written ;
if ( to_write = = 0 ) {
2009-04-08 22:54:28 +02:00
tevent_req_done ( req ) ;
2008-12-19 22:58:34 +01: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-08 22:54:28 +02:00
if ( tevent_req_nomem ( subreq , req ) ) {
2008-12-19 22:58:34 +01:00
return ;
}
2009-03-29 13:28:18 +02:00
tevent_req_set_callback ( subreq , cli_writeall_written , req ) ;
2008-12-19 22:58:34 +01:00
}
2011-04-26 10:52:39 -07:00
static NTSTATUS cli_writeall_recv ( struct tevent_req * req ,
size_t * pwritten )
2008-12-19 22:58:34 +01:00
{
2011-04-26 10:52:39 -07: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 ( ) ;
struct event_context * ev ;
struct tevent_req * req ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
if ( cli_has_async_calls ( cli ) ) {
/*
* Can ' t use sync call while an async call is in flight
*/
status = NT_STATUS_INVALID_PARAMETER ;
goto fail ;
}
ev = event_context_init ( frame ) ;
if ( ev = = NULL ) {
goto fail ;
}
req = cli_writeall_send ( frame , ev , cli , fnum , mode , buf , offset , size ) ;
if ( req = = NULL ) {
goto fail ;
}
if ( ! tevent_req_poll ( req , ev ) ) {
status = map_nt_error_from_unix ( errno ) ;
goto fail ;
}
status = cli_writeall_recv ( req , pwritten ) ;
fail :
TALLOC_FREE ( frame ) ;
return status ;
2008-12-19 22:58:34 +01:00
}
2009-03-10 10:56:33 +01:00
struct cli_push_write_state {
2009-04-08 22:54:28 +02:00
struct tevent_req * req ; /* This is the main request! Not the subreq */
2009-03-10 10:56:33 +01:00
uint32_t idx ;
off_t ofs ;
uint8_t * buf ;
size_t size ;
} ;
2008-12-19 22:58:34 +01:00
2009-03-10 10:56:33 +01:00
struct cli_push_state {
2008-12-19 22:58:34 +01:00
struct event_context * ev ;
struct cli_state * cli ;
uint16_t fnum ;
uint16_t mode ;
off_t start_offset ;
size_t window_size ;
2009-03-12 09:02:02 +01:00
size_t ( * source ) ( uint8_t * buf , size_t n , void * priv ) ;
2008-12-19 22:58:34 +01:00
void * priv ;
bool eof ;
2009-03-10 10:56:33 +01:00
size_t chunk_size ;
off_t next_offset ;
2008-12-19 22:58:34 +01:00
/*
* Outstanding requests
*/
2009-03-10 10:56:33 +01:00
uint32_t pending ;
2011-09-13 16:30:56 +02:00
uint16_t max_reqs ;
2009-03-10 10:56:33 +01:00
uint32_t num_reqs ;
struct cli_push_write_state * * reqs ;
2008-12-19 22:58:34 +01:00
} ;
2009-04-08 22:54:28 +02:00
static void cli_push_written ( struct tevent_req * req ) ;
2008-12-19 22:58:34 +01:00
2009-04-08 22:54:28 +02:00
static bool cli_push_write_setup ( struct tevent_req * req ,
2009-03-10 10:56:33 +01:00
struct cli_push_state * state ,
uint32_t idx )
{
struct cli_push_write_state * substate ;
2009-04-08 22:54:28 +02:00
struct tevent_req * subreq ;
2009-03-10 10:56:33 +01:00
substate = talloc ( state - > reqs , struct cli_push_write_state ) ;
if ( ! substate ) {
return false ;
}
substate - > req = req ;
substate - > idx = idx ;
substate - > ofs = state - > next_offset ;
2009-03-12 09:02:02 +01:00
substate - > buf = talloc_array ( substate , uint8_t , state - > chunk_size ) ;
if ( ! substate - > buf ) {
talloc_free ( substate ) ;
return false ;
2009-03-10 10:56:33 +01:00
}
substate - > size = state - > source ( substate - > buf ,
state - > chunk_size ,
state - > priv ) ;
if ( substate - > size = = 0 ) {
2009-03-10 12:32:48 +01:00
state - > eof = true ;
2009-03-10 10:56:33 +01:00
/* nothing to send */
talloc_free ( substate ) ;
return true ;
}
subreq = cli_writeall_send ( substate ,
state - > ev , state - > cli ,
state - > fnum , state - > mode ,
substate - > buf ,
substate - > ofs ,
substate - > size ) ;
if ( ! subreq ) {
talloc_free ( substate ) ;
return false ;
}
2009-04-08 22:54:28 +02:00
tevent_req_set_callback ( subreq , cli_push_written , substate ) ;
2009-03-10 10:56:33 +01:00
state - > reqs [ idx ] = substate ;
state - > pending + = 1 ;
state - > next_offset + = substate - > size ;
return true ;
}
2009-04-08 22:54:28 +02:00
struct tevent_req * cli_push_send ( TALLOC_CTX * mem_ctx , struct event_context * ev ,
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-19 22:58:34 +01:00
{
2009-04-08 22:54:28 +02:00
struct tevent_req * req ;
2008-12-19 22:58:34 +01:00
struct cli_push_state * state ;
2009-03-10 10:56:33 +01:00
uint32_t i ;
2011-09-12 02:48:25 +02:00
size_t page_size = 1024 ;
2008-12-19 22:58:34 +01:00
2009-04-08 22:54:28 +02:00
req = tevent_req_create ( mem_ctx , & state , struct cli_push_state ) ;
if ( req = = NULL ) {
2009-01-18 16:38:30 +01:00
return NULL ;
2008-12-19 22:58:34 +01:00
}
state - > cli = cli ;
state - > ev = ev ;
state - > fnum = fnum ;
state - > start_offset = start_offset ;
state - > mode = mode ;
state - > source = source ;
state - > priv = priv ;
state - > eof = false ;
state - > pending = 0 ;
2009-03-10 10:56:33 +01:00
state - > next_offset = start_offset ;
2008-12-19 22:58:34 +01:00
2011-06-08 19:01:13 +02:00
state - > chunk_size = cli_write_max_bufsize ( cli , mode , 14 ) ;
2011-09-12 02:48:25 +02:00
if ( state - > chunk_size > page_size ) {
state - > chunk_size & = ~ ( page_size - 1 ) ;
}
2008-12-19 22:58:34 +01:00
2011-09-13 16:30:56 +02:00
state - > max_reqs = cli_state_max_requests ( cli ) ;
2009-03-10 10:56:33 +01:00
if ( window_size = = 0 ) {
2011-09-13 16:30:56 +02:00
window_size = state - > max_reqs * state - > chunk_size ;
2009-03-10 10:56:33 +01:00
}
state - > num_reqs = window_size / state - > chunk_size ;
if ( ( window_size % state - > chunk_size ) > 0 ) {
state - > num_reqs + = 1 ;
}
2011-09-13 16:30:56 +02:00
state - > num_reqs = MIN ( state - > num_reqs , state - > max_reqs ) ;
2009-03-10 10:56:33 +01:00
state - > num_reqs = MAX ( state - > num_reqs , 1 ) ;
2008-12-19 22:58:34 +01:00
2011-06-07 11:58:39 +10:00
state - > reqs = talloc_zero_array ( state , struct cli_push_write_state * ,
2008-12-19 22:58:34 +01:00
state - > num_reqs ) ;
if ( state - > reqs = = NULL ) {
goto failed ;
}
for ( i = 0 ; i < state - > num_reqs ; i + + ) {
2009-03-10 10:56:33 +01:00
if ( ! cli_push_write_setup ( req , state , i ) ) {
2008-12-19 22:58:34 +01:00
goto failed ;
}
2009-03-10 10:56:33 +01:00
if ( state - > eof ) {
break ;
}
2008-12-19 22:58:34 +01:00
}
2009-03-10 10:56:33 +01:00
if ( state - > pending = = 0 ) {
2009-04-08 22:54:28 +02:00
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
2008-12-19 22:58:34 +01:00
}
2009-03-10 10:56:33 +01:00
return req ;
2008-12-19 22:58:34 +01:00
failed :
2009-04-08 22:54:28 +02:00
tevent_req_nterror ( req , NT_STATUS_NO_MEMORY ) ;
return tevent_req_post ( req , ev ) ;
2008-12-19 22:58:34 +01:00
}
2009-04-08 22:54:28 +02:00
static void cli_push_written ( struct tevent_req * subreq )
2008-12-19 22:58:34 +01:00
{
2009-04-08 22:54:28 +02:00
struct cli_push_write_state * substate = tevent_req_callback_data (
subreq , struct cli_push_write_state ) ;
struct tevent_req * req = substate - > req ;
struct cli_push_state * state = tevent_req_data (
req , struct cli_push_state ) ;
2008-12-19 22:58:34 +01:00
NTSTATUS status ;
2009-03-10 10:56:33 +01:00
uint32_t idx = substate - > idx ;
2008-12-19 22:58:34 +01:00
2009-03-10 10:56:33 +01:00
state - > reqs [ idx ] = NULL ;
2008-12-19 22:58:34 +01:00
state - > pending - = 1 ;
2011-04-26 10:52:39 -07:00
status = cli_writeall_recv ( subreq , NULL ) ;
2009-03-10 10:56:33 +01:00
TALLOC_FREE ( subreq ) ;
TALLOC_FREE ( substate ) ;
2011-04-02 16:02:23 +02:00
if ( tevent_req_nterror ( req , status ) ) {
2008-12-19 22:58:34 +01:00
return ;
}
2009-03-10 10:56:33 +01:00
if ( ! state - > eof ) {
if ( ! cli_push_write_setup ( req , state , idx ) ) {
2009-04-08 22:54:28 +02:00
tevent_req_nterror ( req , NT_STATUS_NO_MEMORY ) ;
2009-03-10 10:56:33 +01:00
return ;
}
2008-12-19 22:58:34 +01:00
}
2009-03-10 10:56:33 +01:00
if ( state - > pending = = 0 ) {
2009-04-08 22:54:28 +02:00
tevent_req_done ( req ) ;
2008-12-19 22:58:34 +01:00
return ;
}
}
2009-04-08 22:54:28 +02:00
NTSTATUS cli_push_recv ( struct tevent_req * req )
2008-12-19 22:58:34 +01:00
{
2009-04-08 22:54:28 +02:00
return tevent_req_simple_recv_ntstatus ( req ) ;
2008-12-19 22:58:34 +01: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 09:02:02 +01:00
size_t ( * source ) ( uint8_t * buf , size_t n , void * priv ) ,
2008-12-19 22:58:34 +01:00
void * priv )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct event_context * ev ;
2009-04-08 22:54:28 +02:00
struct tevent_req * req ;
2009-04-05 06:33:24 +02:00
NTSTATUS status = NT_STATUS_OK ;
2008-12-19 22:58:34 +01:00
2009-04-05 06:33:24 +02:00
if ( cli_has_async_calls ( cli ) ) {
2008-12-19 22:58:34 +01:00
/*
* Can ' t use sync call while an async call is in flight
*/
2009-04-05 06:33:24 +02:00
status = NT_STATUS_INVALID_PARAMETER ;
goto fail ;
2008-12-19 22:58:34 +01:00
}
ev = event_context_init ( frame ) ;
if ( ev = = NULL ) {
2009-04-05 06:33:24 +02:00
status = NT_STATUS_NO_MEMORY ;
goto fail ;
2008-12-19 22:58:34 +01:00
}
req = cli_push_send ( frame , ev , cli , fnum , mode , start_offset ,
2009-03-12 09:02:02 +01:00
window_size , source , priv ) ;
2008-12-19 22:58:34 +01:00
if ( req = = NULL ) {
2009-04-05 06:33:24 +02:00
status = NT_STATUS_NO_MEMORY ;
goto fail ;
2008-12-19 22:58:34 +01:00
}
2009-04-08 22:54:28 +02:00
if ( ! tevent_req_poll ( req , ev ) ) {
status = map_nt_error_from_unix ( errno ) ;
goto fail ;
2008-12-19 22:58:34 +01:00
}
2009-04-05 06:33:24 +02:00
status = cli_push_recv ( req ) ;
fail :
2008-12-19 22:58:34 +01:00
TALLOC_FREE ( frame ) ;
2009-04-05 06:33:24 +02:00
return status ;
2008-12-19 22:58:34 +01:00
}