2011-09-23 00:23:08 +04:00
/*
Unix SMB / CIFS implementation .
test suite for SMB2 ioctl operations
Copyright ( C ) David Disseldorp 2011
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
# include "librpc/gen_ndr/security.h"
# include "libcli/smb2/smb2.h"
# include "libcli/smb2/smb2_calls.h"
# include "torture/torture.h"
# include "torture/smb2/proto.h"
2011-09-27 18:40:20 +04:00
# include "librpc/gen_ndr/ndr_ioctl.h"
2011-09-23 00:23:08 +04:00
2011-09-27 18:40:20 +04:00
# define FNAME "testfsctl.dat"
# define FNAME2 "testfsctl2.dat"
2011-09-23 00:23:08 +04:00
/*
basic testing of SMB2 shadow copy calls
*/
static bool test_ioctl_get_shadow_copy ( struct torture_context * torture ,
struct smb2_tree * tree )
{
struct smb2_handle h ;
uint8_t buf [ 100 ] ;
NTSTATUS status ;
union smb_ioctl ioctl ;
TALLOC_CTX * tmp_ctx = talloc_new ( tree ) ;
smb2_util_unlink ( tree , FNAME ) ;
status = torture_smb2_testfile ( tree , FNAME , & h ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
printf ( " create write \n " ) ;
return false ;
}
ZERO_ARRAY ( buf ) ;
status = smb2_util_write ( tree , h , buf , 0 , ARRAY_SIZE ( buf ) ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
printf ( " failed write \n " ) ;
return false ;
}
ZERO_STRUCT ( ioctl ) ;
ioctl . smb2 . level = RAW_IOCTL_SMB2 ;
ioctl . smb2 . in . file . handle = h ;
2011-10-18 15:55:22 +04:00
ioctl . smb2 . in . function = FSCTL_SRV_ENUM_SNAPS ;
2011-09-23 00:23:08 +04:00
ioctl . smb2 . in . max_response_size = 16 ;
2011-09-26 15:38:16 +04:00
ioctl . smb2 . in . flags = SMB2_IOCTL_FLAG_IS_FSCTL ;
2011-09-23 00:23:08 +04:00
status = smb2_ioctl ( tree , tmp_ctx , & ioctl . smb2 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2011-10-18 15:55:22 +04:00
printf ( " FSCTL_SRV_ENUM_SNAPS failed \n " ) ;
2011-09-23 00:23:08 +04:00
return false ;
}
return true ;
}
2011-09-27 18:40:20 +04:00
/*
basic testing of the SMB2 server side copy ioctls
*/
static bool test_ioctl_req_resume_key ( struct torture_context * torture ,
struct smb2_tree * tree )
{
struct smb2_handle h ;
uint8_t buf [ 100 ] ;
NTSTATUS status ;
union smb_ioctl ioctl ;
TALLOC_CTX * tmp_ctx = talloc_new ( tree ) ;
struct req_resume_key_rsp res_key ;
enum ndr_err_code ndr_ret ;
smb2_util_unlink ( tree , FNAME ) ;
status = torture_smb2_testfile ( tree , FNAME , & h ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
printf ( " create write \n " ) ;
return false ;
}
ZERO_ARRAY ( buf ) ;
status = smb2_util_write ( tree , h , buf , 0 , ARRAY_SIZE ( buf ) ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
printf ( " failed write \n " ) ;
return false ;
}
ZERO_STRUCT ( ioctl ) ;
ioctl . smb2 . level = RAW_IOCTL_SMB2 ;
ioctl . smb2 . in . file . handle = h ;
ioctl . smb2 . in . function = FSCTL_SRV_REQUEST_RESUME_KEY ;
ioctl . smb2 . in . max_response_size = 32 ;
ioctl . smb2 . in . flags = SMB2_IOCTL_FLAG_IS_FSCTL ;
status = smb2_ioctl ( tree , tmp_ctx , & ioctl . smb2 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
printf ( " FSCTL_SRV_REQUEST_RESUME_KEY failed \n " ) ;
return false ;
}
ndr_ret = ndr_pull_struct_blob ( & ioctl . smb2 . out . out , tmp_ctx , & res_key ,
( ndr_pull_flags_fn_t ) ndr_pull_req_resume_key_rsp ) ;
if ( ndr_ret ! = NDR_ERR_SUCCESS ) {
return false ;
}
ndr_print_debug ( ( ndr_print_fn_t ) ndr_print_req_resume_key_rsp , " yo " , & res_key ) ;
talloc_free ( tmp_ctx ) ;
return true ;
}
2011-09-30 14:50:36 +04:00
static bool test_setup_copy_chunk ( struct smb2_tree * tree , TALLOC_CTX * mem_ctx ,
uint32_t nchunks ,
struct smb2_handle * src_h ,
uint64_t src_size ,
struct smb2_handle * dest_h ,
uint64_t dest_size ,
struct srv_copychunk_copy * cc_copy ,
union smb_ioctl * ioctl )
2011-09-27 18:40:20 +04:00
{
struct req_resume_key_rsp res_key ;
2011-09-30 14:50:36 +04:00
NTSTATUS status ;
2011-09-27 18:40:20 +04:00
enum ndr_err_code ndr_ret ;
2011-09-30 14:50:36 +04:00
uint8_t * buf = talloc_zero_size ( mem_ctx , MAX ( src_size , dest_size ) ) ;
if ( buf = = NULL ) {
printf ( " no mem for file data buffer \n " ) ;
return false ;
}
2011-09-27 18:40:20 +04:00
smb2_util_unlink ( tree , FNAME ) ;
smb2_util_unlink ( tree , FNAME2 ) ;
2011-09-30 14:50:36 +04:00
status = torture_smb2_testfile ( tree , FNAME , src_h ) ;
2011-09-27 18:40:20 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
printf ( " create write \n " ) ;
return false ;
}
2011-09-30 14:50:36 +04:00
if ( src_size > 0 ) {
status = smb2_util_write ( tree , * src_h , buf , 0 , src_size ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
printf ( " failed src write \n " ) ;
return false ;
}
}
status = torture_smb2_testfile ( tree , FNAME2 , dest_h ) ;
2011-09-27 18:40:20 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2011-09-30 14:50:36 +04:00
printf ( " create write \n " ) ;
2011-09-27 18:40:20 +04:00
return false ;
}
2011-09-30 14:50:36 +04:00
if ( dest_size > 0 ) {
status = smb2_util_write ( tree , * dest_h , buf , 0 , dest_size ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
printf ( " failed dest write \n " ) ;
return false ;
}
}
ZERO_STRUCTPN ( ioctl ) ;
ioctl - > smb2 . level = RAW_IOCTL_SMB2 ;
ioctl - > smb2 . in . file . handle = * src_h ;
ioctl - > smb2 . in . function = FSCTL_SRV_REQUEST_RESUME_KEY ;
2011-09-27 18:40:20 +04:00
/* Allow for Key + ContextLength + Context */
2011-09-30 14:50:36 +04:00
ioctl - > smb2 . in . max_response_size = 32 ;
ioctl - > smb2 . in . flags = SMB2_IOCTL_FLAG_IS_FSCTL ;
2011-09-27 18:40:20 +04:00
2011-09-30 14:50:36 +04:00
status = smb2_ioctl ( tree , mem_ctx , & ioctl - > smb2 ) ;
2011-09-27 18:40:20 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
printf ( " FSCTL_SRV_REQUEST_RESUME_KEY failed \n " ) ;
return false ;
}
2011-09-30 14:50:36 +04:00
ndr_ret = ndr_pull_struct_blob ( & ioctl - > smb2 . out . out , mem_ctx , & res_key ,
2011-09-27 18:40:20 +04:00
( ndr_pull_flags_fn_t ) ndr_pull_req_resume_key_rsp ) ;
if ( ndr_ret ! = NDR_ERR_SUCCESS ) {
return false ;
}
2011-09-30 14:50:36 +04:00
ZERO_STRUCTPN ( ioctl ) ;
ioctl - > smb2 . level = RAW_IOCTL_SMB2 ;
ioctl - > smb2 . in . file . handle = * dest_h ;
ioctl - > smb2 . in . function = FSCTL_SRV_COPYCHUNK ;
ioctl - > smb2 . in . max_response_size = sizeof ( struct srv_copychunk_rsp ) ;
ioctl - > smb2 . in . flags = SMB2_IOCTL_FLAG_IS_FSCTL ;
ZERO_STRUCTPN ( cc_copy ) ;
memcpy ( cc_copy - > source_key , res_key . resume_key , ARRAY_SIZE ( cc_copy - > source_key ) ) ;
cc_copy - > chunk_count = nchunks ;
cc_copy - > chunks = talloc_zero_array ( mem_ctx , struct srv_copychunk , nchunks ) ;
if ( cc_copy - > chunks = = NULL ) {
printf ( " not enough memory to allocate %u chunks \n " , nchunks ) ;
2011-09-27 18:40:20 +04:00
return false ;
}
2011-09-30 14:50:36 +04:00
return true ;
}
2011-09-27 18:40:20 +04:00
2011-09-30 14:50:36 +04:00
static bool check_copy_chunk_rsp ( struct srv_copychunk_rsp * cc_rsp ,
uint32_t ex_chunks_written ,
uint32_t ex_chunk_bytes_written ,
uint32_t ex_total_bytes_written )
{
if ( cc_rsp - > chunks_written ! = ex_chunks_written ) {
printf ( " expected %u chunks, got %u \n " ,
ex_chunks_written , cc_rsp - > chunks_written ) ;
return false ;
}
if ( cc_rsp - > chunk_bytes_written ! = ex_chunk_bytes_written ) {
printf ( " expected %u chunk bytes remaining, got %u \n " ,
ex_chunk_bytes_written , cc_rsp - > chunk_bytes_written ) ;
return false ;
}
if ( cc_rsp - > total_bytes_written ! = ex_total_bytes_written ) {
printf ( " expected %u total bytes, got %u \n " ,
ex_total_bytes_written , cc_rsp - > total_bytes_written ) ;
return false ;
}
return true ;
}
static bool test_ioctl_copy_chunk ( struct torture_context * torture ,
struct smb2_tree * tree )
{
struct smb2_handle src_h ;
struct smb2_handle dest_h ;
NTSTATUS status ;
union smb_ioctl ioctl ;
TALLOC_CTX * tmp_ctx = talloc_new ( tree ) ;
struct srv_copychunk_copy cc_copy ;
struct srv_copychunk_rsp cc_rsp ;
enum ndr_err_code ndr_ret ;
bool ok ;
ok = test_setup_copy_chunk ( tree , tmp_ctx ,
1 , /* 1 chunk */
& src_h , 100 , /* fill 100 byte src file */
& dest_h , 0 , /* 0 byte dest file */
& cc_copy ,
& ioctl ) ;
if ( ! ok ) {
return false ;
}
/* copy all src file data (via a single chunk desc) */
cc_copy . chunks [ 0 ] . source_off = 0 ;
cc_copy . chunks [ 0 ] . target_off = 0 ;
cc_copy . chunks [ 0 ] . length = 100 ;
2011-09-27 18:40:20 +04:00
ndr_ret = ndr_push_struct_blob ( & ioctl . smb2 . in . out , tmp_ctx ,
& cc_copy ,
( ndr_push_flags_fn_t ) ndr_push_srv_copychunk_copy ) ;
if ( ndr_ret ! = NDR_ERR_SUCCESS ) {
return false ;
}
status = smb2_ioctl ( tree , tmp_ctx , & ioctl . smb2 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
printf ( " FSCTL_SRV_COPYCHUNK failed \n " ) ;
return false ;
}
ndr_ret = ndr_pull_struct_blob ( & ioctl . smb2 . out . out , tmp_ctx ,
& cc_rsp ,
( ndr_pull_flags_fn_t ) ndr_pull_srv_copychunk_rsp ) ;
if ( ndr_ret ! = NDR_ERR_SUCCESS ) {
return false ;
}
2011-09-30 14:50:36 +04:00
ok = check_copy_chunk_rsp ( & cc_rsp ,
1 , /* chunks written */
0 , /* chunk bytes unsuccessfully written */
100 ) ; /* total bytes written */
if ( ! ok ) {
2011-09-27 18:40:20 +04:00
return false ;
}
talloc_free ( tmp_ctx ) ;
return true ;
}
2011-09-23 00:23:08 +04:00
/*
basic testing of SMB2 ioctls
*/
struct torture_suite * torture_smb2_ioctl_init ( void )
{
struct torture_suite * suite = torture_suite_create ( talloc_autofree_context ( ) , " ioctl " ) ;
torture_suite_add_1smb2_test ( suite , " shadow_copy " , test_ioctl_get_shadow_copy ) ;
2011-09-27 18:40:20 +04:00
torture_suite_add_1smb2_test ( suite , " req_resume_key " , test_ioctl_req_resume_key ) ;
torture_suite_add_1smb2_test ( suite , " copy_chunk " , test_ioctl_copy_chunk ) ;
2011-09-23 00:23:08 +04:00
suite - > description = talloc_strdup ( suite , " SMB2-IOCTL tests " ) ;
return suite ;
}