2009-02-18 11:14:26 +03:00
/*
Unix SMB / CIFS implementation .
Copyright ( C ) Stefan Metzmacher 2009
2009-12-15 14:56:44 +03:00
* * NOTE ! The following LGPL license applies to the tsocket
2009-02-18 11:14:26 +03:00
* * library . This does NOT imply that all of Samba is released
* * under the LGPL
This library is free software ; you can redistribute it and / or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation ; either
version 3 of the License , or ( at your option ) any later version .
This library 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
Lesser General Public License for more details .
You should have received a copy of the GNU Lesser General Public
License along with this library ; if not , see < http : //www.gnu.org/licenses/>.
*/
# include "replace.h"
2009-04-03 19:17:15 +04:00
# include "system/filesys.h"
2009-02-18 11:14:26 +03:00
# include "tsocket.h"
# include "tsocket_internal.h"
2009-04-03 19:52:15 +04:00
struct tdgram_sendto_queue_state {
/* this structs are owned by the caller */
struct {
struct tevent_context * ev ;
struct tdgram_context * dgram ;
const uint8_t * buf ;
size_t len ;
const struct tsocket_address * dst ;
} caller ;
ssize_t ret ;
} ;
static void tdgram_sendto_queue_trigger ( struct tevent_req * req ,
void * private_data ) ;
static void tdgram_sendto_queue_done ( struct tevent_req * subreq ) ;
struct tevent_req * tdgram_sendto_queue_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct tdgram_context * dgram ,
struct tevent_queue * queue ,
const uint8_t * buf ,
size_t len ,
struct tsocket_address * dst )
{
struct tevent_req * req ;
struct tdgram_sendto_queue_state * state ;
2011-07-28 12:37:51 +04:00
struct tevent_queue_entry * e ;
2009-04-03 19:52:15 +04:00
req = tevent_req_create ( mem_ctx , & state ,
struct tdgram_sendto_queue_state ) ;
if ( ! req ) {
return NULL ;
}
state - > caller . ev = ev ;
state - > caller . dgram = dgram ;
state - > caller . buf = buf ;
state - > caller . len = len ;
state - > caller . dst = dst ;
state - > ret = - 1 ;
2011-07-28 12:37:51 +04:00
/*
* we use tevent_queue_add_optimize_empty ( ) with allow_direct
* in order to optimize for the empty queue case .
*/
e = tevent_queue_add_optimize_empty (
queue ,
ev ,
req ,
tdgram_sendto_queue_trigger ,
NULL ) ;
if ( tevent_req_nomem ( e , req ) ) {
return tevent_req_post ( req , ev ) ;
}
if ( ! tevent_req_is_in_progress ( req ) ) {
return tevent_req_post ( req , ev ) ;
2009-04-03 19:52:15 +04:00
}
return req ;
}
static void tdgram_sendto_queue_trigger ( struct tevent_req * req ,
void * private_data )
{
struct tdgram_sendto_queue_state * state = tevent_req_data ( req ,
struct tdgram_sendto_queue_state ) ;
struct tevent_req * subreq ;
subreq = tdgram_sendto_send ( state ,
state - > caller . ev ,
state - > caller . dgram ,
state - > caller . buf ,
state - > caller . len ,
state - > caller . dst ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , tdgram_sendto_queue_done , req ) ;
}
static void tdgram_sendto_queue_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct tdgram_sendto_queue_state * state = tevent_req_data ( req ,
struct tdgram_sendto_queue_state ) ;
ssize_t ret ;
int sys_errno ;
ret = tdgram_sendto_recv ( subreq , & sys_errno ) ;
talloc_free ( subreq ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , sys_errno ) ;
return ;
}
state - > ret = ret ;
tevent_req_done ( req ) ;
}
ssize_t tdgram_sendto_queue_recv ( struct tevent_req * req , int * perrno )
{
struct tdgram_sendto_queue_state * state = tevent_req_data ( req ,
struct tdgram_sendto_queue_state ) ;
ssize_t ret ;
ret = tsocket_simple_int_recv ( req , perrno ) ;
if ( ret = = 0 ) {
ret = state - > ret ;
}
tevent_req_received ( req ) ;
return ret ;
}
2009-04-03 19:17:15 +04:00
struct tstream_readv_pdu_state {
/* this structs are owned by the caller */
struct {
struct tevent_context * ev ;
struct tstream_context * stream ;
tstream_readv_pdu_next_vector_t next_vector_fn ;
void * next_vector_private ;
} caller ;
/*
* Each call to the callback resets iov and count
* the callback allocated the iov as child of our state ,
* that means we are allowed to modify and free it .
*
* we should call the callback every time we filled the given
* vector and ask for a new vector . We return if the callback
* ask for 0 bytes .
*/
struct iovec * vector ;
size_t count ;
/*
* the total number of bytes we read ,
* the return value of the _recv function
*/
int total_read ;
} ;
static void tstream_readv_pdu_ask_for_next_vector ( struct tevent_req * req ) ;
static void tstream_readv_pdu_readv_done ( struct tevent_req * subreq ) ;
struct tevent_req * tstream_readv_pdu_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct tstream_context * stream ,
tstream_readv_pdu_next_vector_t next_vector_fn ,
void * next_vector_private )
{
struct tevent_req * req ;
struct tstream_readv_pdu_state * state ;
req = tevent_req_create ( mem_ctx , & state ,
struct tstream_readv_pdu_state ) ;
if ( ! req ) {
return NULL ;
}
state - > caller . ev = ev ;
state - > caller . stream = stream ;
state - > caller . next_vector_fn = next_vector_fn ;
state - > caller . next_vector_private = next_vector_private ;
state - > vector = NULL ;
state - > count = 0 ;
state - > total_read = 0 ;
tstream_readv_pdu_ask_for_next_vector ( req ) ;
if ( ! tevent_req_is_in_progress ( req ) ) {
goto post ;
}
return req ;
post :
return tevent_req_post ( req , ev ) ;
}
static void tstream_readv_pdu_ask_for_next_vector ( struct tevent_req * req )
{
struct tstream_readv_pdu_state * state = tevent_req_data ( req ,
struct tstream_readv_pdu_state ) ;
int ret ;
size_t to_read = 0 ;
size_t i ;
struct tevent_req * subreq ;
2012-11-02 16:56:53 +04:00
bool optimize = false ;
bool save_optimize = false ;
if ( state - > count > 0 ) {
/*
* This is not the first time we asked for a vector ,
* which means parts of the pdu already arrived .
*
* In this case it make sense to enable
* a syscall / performance optimization if the
* low level tstream implementation supports it .
*/
optimize = true ;
}
2009-04-03 19:17:15 +04:00
TALLOC_FREE ( state - > vector ) ;
state - > count = 0 ;
ret = state - > caller . next_vector_fn ( state - > caller . stream ,
state - > caller . next_vector_private ,
state , & state - > vector , & state - > count ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , errno ) ;
return ;
}
if ( state - > count = = 0 ) {
tevent_req_done ( req ) ;
return ;
}
for ( i = 0 ; i < state - > count ; i + + ) {
size_t tmp = to_read ;
tmp + = state - > vector [ i ] . iov_len ;
if ( tmp < to_read ) {
tevent_req_error ( req , EMSGSIZE ) ;
return ;
}
to_read = tmp ;
}
/*
* this is invalid the next vector function should have
* reported count = = 0.
*/
if ( to_read = = 0 ) {
tevent_req_error ( req , EINVAL ) ;
return ;
}
if ( state - > total_read + to_read < state - > total_read ) {
tevent_req_error ( req , EMSGSIZE ) ;
return ;
}
2012-11-02 16:56:53 +04:00
if ( optimize ) {
/*
* If the low level stream is a bsd socket
* we will get syscall optimization .
*
* If it is not a bsd socket
* tstream_bsd_optimize_readv ( ) just returns .
*/
save_optimize = tstream_bsd_optimize_readv ( state - > caller . stream ,
true ) ;
}
2009-04-03 19:17:15 +04:00
subreq = tstream_readv_send ( state ,
state - > caller . ev ,
state - > caller . stream ,
state - > vector ,
state - > count ) ;
2012-11-02 16:56:53 +04:00
if ( optimize ) {
tstream_bsd_optimize_readv ( state - > caller . stream ,
save_optimize ) ;
}
2009-04-03 19:17:15 +04:00
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , tstream_readv_pdu_readv_done , req ) ;
}
static void tstream_readv_pdu_readv_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct tstream_readv_pdu_state * state = tevent_req_data ( req ,
struct tstream_readv_pdu_state ) ;
int ret ;
int sys_errno ;
ret = tstream_readv_recv ( subreq , & sys_errno ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , sys_errno ) ;
return ;
}
state - > total_read + = ret ;
/* ask the callback for a new vector we should fill */
tstream_readv_pdu_ask_for_next_vector ( req ) ;
}
int tstream_readv_pdu_recv ( struct tevent_req * req , int * perrno )
{
struct tstream_readv_pdu_state * state = tevent_req_data ( req ,
struct tstream_readv_pdu_state ) ;
int ret ;
ret = tsocket_simple_int_recv ( req , perrno ) ;
if ( ret = = 0 ) {
ret = state - > total_read ;
}
tevent_req_received ( req ) ;
return ret ;
}
2009-04-03 20:49:24 +04:00
struct tstream_readv_pdu_queue_state {
/* this structs are owned by the caller */
struct {
struct tevent_context * ev ;
struct tstream_context * stream ;
tstream_readv_pdu_next_vector_t next_vector_fn ;
void * next_vector_private ;
} caller ;
int ret ;
} ;
static void tstream_readv_pdu_queue_trigger ( struct tevent_req * req ,
void * private_data ) ;
static void tstream_readv_pdu_queue_done ( struct tevent_req * subreq ) ;
struct tevent_req * tstream_readv_pdu_queue_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct tstream_context * stream ,
struct tevent_queue * queue ,
tstream_readv_pdu_next_vector_t next_vector_fn ,
void * next_vector_private )
{
struct tevent_req * req ;
struct tstream_readv_pdu_queue_state * state ;
2011-07-28 12:37:51 +04:00
struct tevent_queue_entry * e ;
2009-04-03 20:49:24 +04:00
req = tevent_req_create ( mem_ctx , & state ,
struct tstream_readv_pdu_queue_state ) ;
if ( ! req ) {
return NULL ;
}
state - > caller . ev = ev ;
state - > caller . stream = stream ;
state - > caller . next_vector_fn = next_vector_fn ;
state - > caller . next_vector_private = next_vector_private ;
state - > ret = - 1 ;
2011-07-28 12:37:51 +04:00
/*
* we use tevent_queue_add_optimize_empty ( ) with allow_direct
* in order to optimize for the empty queue case .
*/
e = tevent_queue_add_optimize_empty (
queue ,
ev ,
req ,
tstream_readv_pdu_queue_trigger ,
NULL ) ;
if ( tevent_req_nomem ( e , req ) ) {
return tevent_req_post ( req , ev ) ;
}
if ( ! tevent_req_is_in_progress ( req ) ) {
return tevent_req_post ( req , ev ) ;
2009-04-03 20:49:24 +04:00
}
return req ;
}
static void tstream_readv_pdu_queue_trigger ( struct tevent_req * req ,
void * private_data )
{
struct tstream_readv_pdu_queue_state * state = tevent_req_data ( req ,
struct tstream_readv_pdu_queue_state ) ;
struct tevent_req * subreq ;
subreq = tstream_readv_pdu_send ( state ,
state - > caller . ev ,
state - > caller . stream ,
state - > caller . next_vector_fn ,
state - > caller . next_vector_private ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , tstream_readv_pdu_queue_done , req ) ;
}
static void tstream_readv_pdu_queue_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct tstream_readv_pdu_queue_state * state = tevent_req_data ( req ,
struct tstream_readv_pdu_queue_state ) ;
int ret ;
int sys_errno ;
ret = tstream_readv_pdu_recv ( subreq , & sys_errno ) ;
talloc_free ( subreq ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , sys_errno ) ;
return ;
}
state - > ret = ret ;
tevent_req_done ( req ) ;
}
int tstream_readv_pdu_queue_recv ( struct tevent_req * req , int * perrno )
{
struct tstream_readv_pdu_queue_state * state = tevent_req_data ( req ,
struct tstream_readv_pdu_queue_state ) ;
int ret ;
ret = tsocket_simple_int_recv ( req , perrno ) ;
if ( ret = = 0 ) {
ret = state - > ret ;
}
tevent_req_received ( req ) ;
return ret ;
}
2009-04-03 20:18:51 +04:00
struct tstream_writev_queue_state {
/* this structs are owned by the caller */
struct {
struct tevent_context * ev ;
struct tstream_context * stream ;
const struct iovec * vector ;
size_t count ;
} caller ;
int ret ;
} ;
static void tstream_writev_queue_trigger ( struct tevent_req * req ,
void * private_data ) ;
static void tstream_writev_queue_done ( struct tevent_req * subreq ) ;
struct tevent_req * tstream_writev_queue_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct tstream_context * stream ,
struct tevent_queue * queue ,
const struct iovec * vector ,
size_t count )
{
struct tevent_req * req ;
struct tstream_writev_queue_state * state ;
2011-07-28 12:37:51 +04:00
struct tevent_queue_entry * e ;
2009-04-03 20:18:51 +04:00
req = tevent_req_create ( mem_ctx , & state ,
struct tstream_writev_queue_state ) ;
if ( ! req ) {
return NULL ;
}
state - > caller . ev = ev ;
state - > caller . stream = stream ;
state - > caller . vector = vector ;
state - > caller . count = count ;
state - > ret = - 1 ;
2011-07-28 12:37:51 +04:00
/*
* we use tevent_queue_add_optimize_empty ( ) with allow_direct
* in order to optimize for the empty queue case .
*/
e = tevent_queue_add_optimize_empty (
queue ,
ev ,
req ,
tstream_writev_queue_trigger ,
NULL ) ;
if ( tevent_req_nomem ( e , req ) ) {
return tevent_req_post ( req , ev ) ;
}
if ( ! tevent_req_is_in_progress ( req ) ) {
return tevent_req_post ( req , ev ) ;
2009-04-03 20:18:51 +04:00
}
return req ;
}
static void tstream_writev_queue_trigger ( struct tevent_req * req ,
void * private_data )
{
struct tstream_writev_queue_state * state = tevent_req_data ( req ,
struct tstream_writev_queue_state ) ;
struct tevent_req * subreq ;
subreq = tstream_writev_send ( state ,
state - > caller . ev ,
state - > caller . stream ,
state - > caller . vector ,
state - > caller . count ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq , tstream_writev_queue_done , req ) ;
}
static void tstream_writev_queue_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data ( subreq ,
struct tevent_req ) ;
struct tstream_writev_queue_state * state = tevent_req_data ( req ,
struct tstream_writev_queue_state ) ;
int ret ;
int sys_errno ;
ret = tstream_writev_recv ( subreq , & sys_errno ) ;
talloc_free ( subreq ) ;
if ( ret = = - 1 ) {
tevent_req_error ( req , sys_errno ) ;
return ;
}
state - > ret = ret ;
tevent_req_done ( req ) ;
}
int tstream_writev_queue_recv ( struct tevent_req * req , int * perrno )
{
struct tstream_writev_queue_state * state = tevent_req_data ( req ,
struct tstream_writev_queue_state ) ;
int ret ;
ret = tsocket_simple_int_recv ( req , perrno ) ;
if ( ret = = 0 ) {
ret = state - > ret ;
}
tevent_req_received ( req ) ;
return ret ;
}