2003-08-13 05:53:07 +04:00
/*
Unix SMB / CIFS implementation .
generic testing tool
Copyright ( C ) Andrew Tridgell 2003
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 2 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 , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include "includes.h"
# define NSERVERS 2
# define NINSTANCES 2
/* global options */
static struct gentest_options {
BOOL showall ;
BOOL analyze ;
BOOL analyze_always ;
BOOL analyze_continuous ;
uint_t max_open_handles ;
uint_t seed ;
uint_t numops ;
BOOL use_oplocks ;
char * * ignore_patterns ;
const char * seeds_file ;
BOOL use_preset_seeds ;
BOOL fast_reconnect ;
} options ;
/* mapping between open handles on the server and local handles */
static struct {
BOOL active ;
uint_t instance ;
uint_t server_fnum [ NSERVERS ] ;
const char * name ;
} * open_handles ;
static uint_t num_open_handles ;
/* state information for the servers. We open NINSTANCES connections to
each server */
static struct {
struct cli_state * cli [ NINSTANCES ] ;
char * server_name ;
char * share_name ;
char * username ;
char * password ;
} servers [ NSERVERS ] ;
/* the seeds and flags for each operation */
static struct {
uint_t seed ;
BOOL disabled ;
} * op_parms ;
/* oplock break info */
static struct {
BOOL got_break ;
uint16 fnum ;
uint16 handle ;
uint8 level ;
BOOL do_close ;
} oplocks [ NSERVERS ] [ NINSTANCES ] ;
/* change notify reply info */
static struct {
int notify_count ;
NTSTATUS status ;
struct smb_notify notify ;
} notifies [ NSERVERS ] [ NINSTANCES ] ;
/* info relevant to the current operation */
static struct {
const char * name ;
uint_t seed ;
NTSTATUS status ;
uint_t opnum ;
TALLOC_CTX * mem_ctx ;
} current_op ;
# define BAD_HANDLE 0xFFFE
static BOOL oplock_handler ( struct cli_transport * transport , uint16 tid , uint16 fnum , uint8 level , void * private ) ;
static void idle_func ( struct cli_transport * transport , void * private ) ;
/*
check if a string should be ignored . This is used as the basis
for all error ignore settings
*/
static BOOL ignore_pattern ( const char * str )
{
int i ;
if ( ! options . ignore_patterns ) return False ;
for ( i = 0 ; options . ignore_patterns [ i ] ; i + + ) {
if ( strcmp ( options . ignore_patterns [ i ] , str ) = = 0 | |
gen_fnmatch ( options . ignore_patterns [ i ] , str ) = = 0 ) {
DEBUG ( 2 , ( " Ignoring '%s' \n " , str ) ) ;
return True ;
}
}
return False ;
}
/*****************************************************
connect to the servers
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL connect_servers_fast ( void )
{
int h , i ;
/* close all open files */
for ( h = 0 ; h < options . max_open_handles ; h + + ) {
if ( ! open_handles [ h ] . active ) continue ;
for ( i = 0 ; i < NSERVERS ; i + + ) {
if ( ! cli_close ( servers [ i ] . cli [ open_handles [ h ] . instance ] ,
open_handles [ h ] . server_fnum [ i ] ) ) {
return False ;
}
open_handles [ h ] . active = False ;
}
}
return True ;
}
/*****************************************************
connect to the servers
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL connect_servers ( void )
{
int i , j ;
if ( options . fast_reconnect & & servers [ 0 ] . cli [ 0 ] ) {
if ( connect_servers_fast ( ) ) {
return True ;
}
}
/* close any existing connections */
for ( i = 0 ; i < NSERVERS ; i + + ) {
for ( j = 0 ; j < NINSTANCES ; j + + ) {
if ( servers [ i ] . cli [ j ] ) {
cli_tdis ( servers [ i ] . cli [ j ] ) ;
cli_shutdown ( servers [ i ] . cli [ j ] ) ;
servers [ i ] . cli [ j ] = NULL ;
}
}
}
for ( i = 0 ; i < NSERVERS ; i + + ) {
for ( j = 0 ; j < NINSTANCES ; j + + ) {
NTSTATUS status ;
printf ( " Connecting to \\ \\ %s \\ %s as %s - instance %d \n " ,
servers [ i ] . server_name , servers [ i ] . share_name ,
servers [ i ] . username , j ) ;
status = cli_full_connection ( & servers [ i ] . cli [ j ] ,
" gentest " ,
servers [ i ] . server_name , NULL ,
servers [ i ] . share_name , " ????? " ,
2003-08-13 06:03:16 +04:00
servers [ i ] . username , lp_workgroup ( ) ,
2003-08-13 05:53:07 +04:00
servers [ i ] . password , 0 , NULL ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
printf ( " Failed to connect to \\ \\ %s \\ %s - %s \n " ,
servers [ i ] . server_name , servers [ i ] . share_name ,
nt_errstr ( status ) ) ;
return False ;
}
cli_oplock_handler ( servers [ i ] . cli [ j ] - > transport , oplock_handler , NULL ) ;
cli_transport_idle_handler ( servers [ i ] . cli [ j ] - > transport , idle_func , 10 , NULL ) ;
}
}
return True ;
}
/*
work out the time skew between the servers - be conservative
*/
static uint_t time_skew ( void )
{
uint_t ret ;
ret = ABS ( servers [ 0 ] . cli [ 0 ] - > transport - > negotiate . server_time -
servers [ 1 ] . cli [ 0 ] - > transport - > negotiate . server_time ) ;
return ret + 300 ;
}
/*
turn an fnum for an instance into a handle
*/
static uint_t fnum_to_handle ( int server , int instance , uint16 fnum )
{
uint_t i ;
for ( i = 0 ; i < options . max_open_handles ; i + + ) {
if ( ! open_handles [ i ] . active | |
instance ! = open_handles [ i ] . instance ) continue ;
if ( open_handles [ i ] . server_fnum [ server ] = = fnum ) {
return i ;
}
}
printf ( " Invalid fnum %d in fnum_to_handle on server %d instance %d \n " ,
fnum , server , instance ) ;
return BAD_HANDLE ;
}
/*
add some newly opened handles
*/
static void gen_add_handle ( int instance , const char * name , uint16 fnums [ NSERVERS ] )
{
int i , h ;
for ( h = 0 ; h < options . max_open_handles ; h + + ) {
if ( ! open_handles [ h ] . active ) break ;
}
if ( h = = options . max_open_handles ) {
/* we have to force close a random handle */
h = random ( ) % options . max_open_handles ;
for ( i = 0 ; i < NSERVERS ; i + + ) {
if ( ! cli_close ( servers [ i ] . cli [ open_handles [ h ] . instance ] ,
open_handles [ h ] . server_fnum [ i ] ) ) {
printf ( " INTERNAL ERROR: Close failed when recovering handle! - %s \n " ,
cli_errstr ( servers [ i ] . cli [ open_handles [ h ] . instance ] ) ) ;
}
}
printf ( " Recovered handle %d \n " , h ) ;
num_open_handles - - ;
}
for ( i = 0 ; i < NSERVERS ; i + + ) {
open_handles [ h ] . server_fnum [ i ] = fnums [ i ] ;
open_handles [ h ] . instance = instance ;
open_handles [ h ] . active = True ;
open_handles [ h ] . name = name ;
}
num_open_handles + + ;
printf ( " OPEN num_open_handles=%d h=%d s1=0x%x s2=0x%x (%s) \n " ,
num_open_handles , h ,
open_handles [ h ] . server_fnum [ 0 ] , open_handles [ h ] . server_fnum [ 1 ] ,
name ) ;
}
/*
remove a closed handle
*/
static void gen_remove_handle ( int instance , uint16 fnums [ NSERVERS ] )
{
int h ;
for ( h = 0 ; h < options . max_open_handles ; h + + ) {
if ( instance = = open_handles [ h ] . instance & &
open_handles [ h ] . server_fnum [ 0 ] = = fnums [ 0 ] ) {
open_handles [ h ] . active = False ;
num_open_handles - - ;
printf ( " CLOSE num_open_handles=%d h=%d s1=0x%x s2=0x%x (%s) \n " ,
num_open_handles , h ,
open_handles [ h ] . server_fnum [ 0 ] , open_handles [ h ] . server_fnum [ 1 ] ,
open_handles [ h ] . name ) ;
return ;
}
}
printf ( " Removing invalid handle!? \n " ) ;
exit ( 1 ) ;
}
/*
return True with ' chance ' probability as a percentage
*/
static BOOL gen_chance ( uint_t chance )
{
return ( ( random ( ) % 100 ) < = chance ) ;
}
/*
map an internal handle number to a server fnum
*/
static uint16 gen_lookup_fnum ( int server , uint16 handle )
{
if ( handle = = BAD_HANDLE ) return handle ;
return open_handles [ handle ] . server_fnum [ server ] ;
}
/*
return a file handle
*/
static uint16 gen_fnum ( int instance )
{
uint16 h ;
int count = 0 ;
if ( gen_chance ( 20 ) ) return BAD_HANDLE ;
while ( num_open_handles > 0 & & count + + < 10 * options . max_open_handles ) {
h = random ( ) % options . max_open_handles ;
if ( open_handles [ h ] . active & &
open_handles [ h ] . instance = = instance ) {
return h ;
}
}
return BAD_HANDLE ;
}
/*
return a file handle , but skewed so we don ' t close the last
couple of handles too readily
*/
static uint16 gen_fnum_close ( int instance )
{
if ( num_open_handles < 3 ) {
if ( gen_chance ( 80 ) ) return BAD_HANDLE ;
}
return gen_fnum ( instance ) ;
}
/*
generate an integer in a specified range
*/
static int gen_int_range ( uint_t min , uint_t max )
{
uint_t r = random ( ) ;
return min + ( r % ( 1 + max - min ) ) ;
}
/*
return a fnum for use as a root fid
be careful to call GEN_SET_FNUM ( ) when you use this !
*/
static uint16 gen_root_fid ( int instance )
{
if ( gen_chance ( 5 ) ) return gen_fnum ( instance ) ;
return 0 ;
}
/*
generate a file offset
*/
static int gen_offset ( void )
{
if ( gen_chance ( 20 ) ) return 0 ;
return gen_int_range ( 0 , 1024 * 1024 ) ;
}
/*
generate a io count
*/
static int gen_io_count ( void )
{
if ( gen_chance ( 20 ) ) return 0 ;
return gen_int_range ( 0 , 4096 ) ;
}
/*
generate a filename
*/
static const char * gen_fname ( void )
{
const char * names [ ] = { " \\ gentest \\ gentest.dat " ,
" \\ gentest \\ foo " ,
" \\ gentest \\ foo2.sym " ,
" \\ gentest \\ foo3.dll " ,
" \\ gentest \\ foo4 " ,
" \\ gentest \\ foo4:teststream1 " ,
" \\ gentest \\ foo4:teststream2 " ,
" \\ gentest \\ foo5.exe " ,
" \\ gentest \\ foo5.exe:teststream3 " ,
" \\ gentest \\ foo5.exe:teststream4 " ,
" \\ gentest \\ foo6.com " ,
" \\ gentest \\ blah " ,
" \\ gentest \\ blah \\ blergh.txt " ,
" \\ gentest \\ blah \\ blergh2 " ,
" \\ gentest \\ blah \\ blergh3.txt " ,
" \\ gentest \\ blah \\ blergh4 " ,
" \\ gentest \\ blah \\ blergh5.txt " ,
" \\ gentest \\ blah \\ blergh5 " ,
" \\ gentest \\ blah \\ . " ,
#if 0
/* this causes problem with w2k3 */
" \\ gentest \\ blah \\ .. " ,
# endif
" \\ gentest \\ a_very_long_name.bin " ,
" \\ gentest \\ x.y " ,
" \\ gentest \\ blah " } ;
int i ;
do {
i = gen_int_range ( 0 , ARRAY_SIZE ( names ) - 1 ) ;
} while ( ignore_pattern ( names [ i ] ) ) ;
return names [ i ] ;
}
/*
generate a filename with a higher chance of choosing an already
open file
*/
static const char * gen_fname_open ( int instance )
{
uint16 h ;
h = gen_fnum ( instance ) ;
if ( h = = BAD_HANDLE ) {
return gen_fname ( ) ;
}
return open_handles [ h ] . name ;
}
/*
generate a wildcard pattern
*/
static const char * gen_pattern ( void )
{
int i ;
const char * names [ ] = { " \\ gentest \\ *.dat " ,
" \\ gentest \\ * " ,
" \\ gentest \\ *.* " ,
" \\ gentest \\ blah \\ *.* " ,
" \\ gentest \\ blah \\ * " ,
" \\ gentest \\ ? " } ;
if ( gen_chance ( 50 ) ) return gen_fname ( ) ;
do {
i = gen_int_range ( 0 , ARRAY_SIZE ( names ) - 1 ) ;
} while ( ignore_pattern ( names [ i ] ) ) ;
return names [ i ] ;
}
/*
generate a bitmask
*/
static uint32 gen_bits_mask ( uint_t mask )
{
uint_t ret = random ( ) ;
return ret & mask ;
}
/*
generate a bitmask with high probability of the first mask
and low of the second
*/
static uint32 gen_bits_mask2 ( uint32 mask1 , uint32 mask2 )
{
if ( gen_chance ( 10 ) ) return gen_bits_mask ( mask2 ) ;
return gen_bits_mask ( mask1 ) ;
}
/*
generate a boolean
*/
static BOOL gen_bool ( void )
{
return gen_bits_mask2 ( 0x1 , 0xFF ) ;
}
2003-08-13 20:04:21 +04:00
/*
generate ntrename flags
*/
static uint16 gen_rename_flags ( void )
{
if ( gen_chance ( 30 ) ) return RENAME_FLAG_RENAME ;
if ( gen_chance ( 30 ) ) return RENAME_FLAG_HARD_LINK ;
if ( gen_chance ( 30 ) ) return RENAME_FLAG_COPY ;
return gen_bits_mask ( 0xFFFF ) ;
}
2003-08-13 05:53:07 +04:00
/*
return a lockingx lock mode
*/
static uint16 gen_lock_mode ( void )
{
if ( gen_chance ( 5 ) ) return gen_bits_mask ( 0xFFFF ) ;
if ( gen_chance ( 20 ) ) return gen_bits_mask ( 0x1F ) ;
return gen_bits_mask ( LOCKING_ANDX_SHARED_LOCK | LOCKING_ANDX_LARGE_FILES ) ;
}
/*
generate a pid
*/
static uint16 gen_pid ( void )
{
if ( gen_chance ( 10 ) ) return gen_bits_mask ( 0xFFFF ) ;
return getpid ( ) ;
}
/*
generate a lock count
*/
static SMB_OFF_T gen_lock_count ( void )
{
return gen_int_range ( 0 , 3 ) ;
}
/*
generate a ntcreatex flags field
*/
static uint32 gen_ntcreatex_flags ( void )
{
if ( gen_chance ( 70 ) ) return NTCREATEX_FLAGS_EXTENDED ;
return gen_bits_mask2 ( 0x1F , 0xFFFFFFFF ) ;
}
/*
generate a NT access mask
*/
static uint32 gen_access_mask ( void )
{
if ( gen_chance ( 50 ) ) return SEC_RIGHT_MAXIMUM_ALLOWED ;
if ( gen_chance ( 20 ) ) return GENERIC_RIGHTS_FILE_ALL_ACCESS ;
return gen_bits_mask ( 0xFFFFFFFF ) ;
}
/*
generate a ntcreatex create options bitfield
*/
static uint32 gen_create_options ( void )
{
if ( gen_chance ( 20 ) ) return gen_bits_mask ( 0xFFFFFFFF ) ;
if ( gen_chance ( 50 ) ) return 0 ;
return gen_bits_mask ( NTCREATEX_OPTIONS_DELETE_ON_CLOSE | NTCREATEX_OPTIONS_DIRECTORY ) ;
}
/*
generate a ntcreatex open disposition
*/
static uint32 gen_open_disp ( void )
{
if ( gen_chance ( 10 ) ) return gen_bits_mask ( 0xFFFFFFFF ) ;
return gen_int_range ( 0 , 5 ) ;
}
/*
generate an openx open mode
*/
static uint16 gen_openx_mode ( void )
{
if ( gen_chance ( 20 ) ) return gen_bits_mask ( 0xFFFF ) ;
if ( gen_chance ( 20 ) ) return gen_bits_mask ( 0xFF ) ;
return OPENX_MODE_DENY_NONE | gen_bits_mask ( 0x3 ) ;
}
/*
generate an openx flags field
*/
static uint16 gen_openx_flags ( void )
{
if ( gen_chance ( 20 ) ) return gen_bits_mask ( 0xFFFF ) ;
return gen_bits_mask ( 0x7 ) ;
}
/*
generate an openx open function
*/
static uint16 gen_openx_func ( void )
{
if ( gen_chance ( 20 ) ) return gen_bits_mask ( 0xFFFF ) ;
return gen_bits_mask ( 0x13 ) ;
}
/*
generate a file attrib combination
*/
static uint32 gen_attrib ( void )
{
if ( gen_chance ( 20 ) ) return gen_bits_mask ( 0xFFFFFFFF ) ;
return gen_bits_mask ( FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY ) ;
}
/*
generate a unix timestamp
*/
static time_t gen_timet ( void )
{
if ( gen_chance ( 30 ) ) return 0 ;
return ( time_t ) random ( ) ;
}
/*
generate a unix timestamp
*/
static NTTIME gen_nttime ( void )
{
NTTIME ret ;
unix_to_nt_time ( & ret , gen_timet ( ) ) ;
return ret ;
}
/*
generate a milliseconds protocol timeout
*/
static uint32 gen_timeout ( void )
{
if ( gen_chance ( 98 ) ) return 0 ;
return random ( ) % 50 ;
}
/*
generate a file allocation size
*/
static uint_t gen_alloc_size ( void )
{
uint_t ret ;
if ( gen_chance ( 30 ) ) return 0 ;
ret = random ( ) % 4 * 1024 * 1024 ;
/* give a high chance of a round number */
if ( gen_chance ( 60 ) ) {
ret & = ~ ( 1024 * 1024 - 1 ) ;
}
return ret ;
}
/*
generate an ea_struct
*/
struct ea_struct gen_ea_struct ( void )
{
struct ea_struct ea ;
const char * names [ ] = { " EAONE " ,
" " ,
" FOO! " ,
" WITH SPACES " ,
" . " ,
" AVERYLONGATTRIBUTENAME " } ;
const char * values [ ] = { " VALUE1 " ,
" " ,
" NOT MUCH FOO " ,
" LEADING SPACES " ,
" : " ,
" ASOMEWHATLONGERATTRIBUTEVALUE " } ;
int i ;
do {
i = gen_int_range ( 0 , ARRAY_SIZE ( names ) - 1 ) ;
} while ( ignore_pattern ( names [ i ] ) ) ;
ea . name . s = names [ i ] ;
do {
i = gen_int_range ( 0 , ARRAY_SIZE ( values ) - 1 ) ;
} while ( ignore_pattern ( values [ i ] ) ) ;
ea . value = data_blob ( values [ i ] , strlen ( values [ i ] ) ) ;
if ( gen_chance ( 10 ) ) ea . flags = gen_bits_mask ( 0xFF ) ;
ea . flags = 0 ;
return ea ;
}
/*
this is called when a change notify reply comes in
*/
static void async_notify ( struct cli_request * req )
{
struct smb_notify notify ;
NTSTATUS status ;
int i , j ;
uint16 tid ;
struct cli_transport * transport = req - > transport ;
tid = SVAL ( req - > in . hdr , HDR_TID ) ;
status = smb_raw_changenotify_recv ( req , current_op . mem_ctx , & notify ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
printf ( " notify tid=%d num_changes=%d action=%d name=%s \n " ,
tid ,
notify . out . num_changes ,
notify . out . changes [ 0 ] . action ,
notify . out . changes [ 0 ] . name . s ) ;
}
for ( i = 0 ; i < NSERVERS ; i + + ) {
for ( j = 0 ; j < NINSTANCES ; j + + ) {
if ( transport = = servers [ i ] . cli [ j ] - > transport & &
tid = = servers [ i ] . cli [ j ] - > tree - > tid ) {
notifies [ i ] [ j ] . notify_count + + ;
notifies [ i ] [ j ] . status = status ;
notifies [ i ] [ j ] . notify = notify ;
}
}
}
}
/*
the oplock handler will either ack the break or close the file
*/
static BOOL oplock_handler ( struct cli_transport * transport , uint16 tid , uint16 fnum , uint8 level , void * private )
{
union smb_close io ;
NTSTATUS status ;
int i , j ;
BOOL do_close ;
struct cli_tree * tree = NULL ;
srandom ( current_op . seed ) ;
do_close = gen_chance ( 50 ) ;
for ( i = 0 ; i < NSERVERS ; i + + ) {
for ( j = 0 ; j < NINSTANCES ; j + + ) {
if ( transport = = servers [ i ] . cli [ j ] - > transport & &
tid = = servers [ i ] . cli [ j ] - > tree - > tid ) {
oplocks [ i ] [ j ] . got_break = True ;
oplocks [ i ] [ j ] . fnum = fnum ;
oplocks [ i ] [ j ] . handle = fnum_to_handle ( i , j , fnum ) ;
oplocks [ i ] [ j ] . level = level ;
oplocks [ i ] [ j ] . do_close = do_close ;
tree = servers [ i ] . cli [ j ] - > tree ;
}
}
}
if ( ! tree ) {
printf ( " Oplock break not for one of our trees!? \n " ) ;
return False ;
}
if ( ! do_close ) {
printf ( " oplock ack fnum=%d \n " , fnum ) ;
return cli_oplock_ack ( tree , fnum , level = = 1 ? 0x102 : 2 ) ;
}
printf ( " oplock close fnum=%d \n " , fnum ) ;
io . close . level = RAW_CLOSE_CLOSE ;
io . close . in . fnum = fnum ;
io . close . in . write_time = 0 ;
status = smb_raw_close ( tree , & io ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
printf ( " WARNING: close failed in oplock_handler_close - %s \n " , nt_errstr ( status ) ) ;
}
return True ;
}
/*
the idle function tries to cope with getting an oplock break on a connection , and
an operation on another connection blocking until that break is acked
we check for operations on all transports in the idle function
*/
static void idle_func ( struct cli_transport * transport , void * private )
{
int i , j ;
for ( i = 0 ; i < NSERVERS ; i + + ) {
for ( j = 0 ; j < NINSTANCES ; j + + ) {
if ( servers [ i ] . cli [ j ] & &
transport ! = servers [ i ] . cli [ j ] - > transport & &
cli_transport_pending ( servers [ i ] . cli [ j ] - > transport ) ) {
if ( ! cli_request_receive_next ( servers [ i ] . cli [ j ] - > transport ) ) {
printf ( " Connection to server %d instance %d died! \n " ,
i , j ) ;
exit ( 1 ) ;
}
}
}
}
}
/*
compare NTSTATUS , using checking ignored patterns
*/
static BOOL compare_status ( NTSTATUS status1 , NTSTATUS status2 )
{
if ( NT_STATUS_EQUAL ( status1 , status2 ) ) return True ;
/* one code being an error and the other OK is always an error */
if ( NT_STATUS_IS_OK ( status1 ) | | NT_STATUS_IS_OK ( status2 ) ) return False ;
/* if we are ignoring one of the status codes then consider this a match */
if ( ignore_pattern ( nt_errstr ( status1 ) ) | |
ignore_pattern ( nt_errstr ( status2 ) ) ) {
return True ;
}
return False ;
}
/*
check for pending packets on all connections
*/
static void check_pending ( void )
{
int i , j ;
msleep ( 20 ) ;
for ( j = 0 ; j < NINSTANCES ; j + + ) {
for ( i = 0 ; i < NSERVERS ; i + + ) {
if ( cli_transport_pending ( servers [ i ] . cli [ j ] - > transport ) ) {
if ( ! cli_request_receive_next ( servers [ i ] . cli [ j ] - > transport ) ) {
printf ( " Connection to server %d instance %d died! \n " ,
i , j ) ;
exit ( 1 ) ;
}
}
}
}
}
/*
check that the same oplock breaks have been received by all instances
*/
static BOOL check_oplocks ( const char * call )
{
int i , j ;
int tries = 0 ;
again :
check_pending ( ) ;
for ( j = 0 ; j < NINSTANCES ; j + + ) {
for ( i = 1 ; i < NSERVERS ; i + + ) {
if ( oplocks [ 0 ] [ j ] . got_break ! = oplocks [ i ] [ j ] . got_break | |
oplocks [ 0 ] [ j ] . handle ! = oplocks [ i ] [ j ] . handle | |
oplocks [ 0 ] [ j ] . level ! = oplocks [ i ] [ j ] . level ) {
if ( tries + + < 10 ) goto again ;
printf ( " oplock break inconsistent - %d/%d/%d vs %d/%d/%d \n " ,
oplocks [ 0 ] [ j ] . got_break ,
oplocks [ 0 ] [ j ] . handle ,
oplocks [ 0 ] [ j ] . level ,
oplocks [ i ] [ j ] . got_break ,
oplocks [ i ] [ j ] . handle ,
oplocks [ i ] [ j ] . level ) ;
return False ;
}
}
}
/* if we got a break and closed then remove the handle */
for ( j = 0 ; j < NINSTANCES ; j + + ) {
if ( oplocks [ 0 ] [ j ] . got_break & &
oplocks [ 0 ] [ j ] . do_close ) {
uint16 fnums [ NSERVERS ] ;
for ( i = 0 ; i < NSERVERS ; i + + ) {
fnums [ i ] = oplocks [ i ] [ j ] . fnum ;
}
gen_remove_handle ( j , fnums ) ;
break ;
}
}
return True ;
}
/*
check that the same change notify info has been received by all instances
*/
static BOOL check_notifies ( const char * call )
{
int i , j ;
int tries = 0 ;
again :
check_pending ( ) ;
for ( j = 0 ; j < NINSTANCES ; j + + ) {
for ( i = 1 ; i < NSERVERS ; i + + ) {
int n ;
struct smb_notify not1 , not2 ;
if ( notifies [ 0 ] [ j ] . notify_count ! = notifies [ i ] [ j ] . notify_count ) {
if ( tries + + < 10 ) goto again ;
printf ( " Notify count inconsistent %d %d \n " ,
notifies [ 0 ] [ j ] . notify_count ,
notifies [ i ] [ j ] . notify_count ) ;
return False ;
}
if ( notifies [ 0 ] [ j ] . notify_count = = 0 ) continue ;
if ( ! NT_STATUS_EQUAL ( notifies [ 0 ] [ j ] . status ,
notifies [ i ] [ j ] . status ) ) {
printf ( " Notify status mismatch - %s - %s \n " ,
nt_errstr ( notifies [ 0 ] [ j ] . status ) ,
nt_errstr ( notifies [ i ] [ j ] . status ) ) ;
return False ;
}
if ( ! NT_STATUS_IS_OK ( notifies [ 0 ] [ j ] . status ) ) {
continue ;
}
not1 = notifies [ 0 ] [ j ] . notify ;
not2 = notifies [ i ] [ j ] . notify ;
for ( n = 0 ; n < not1 . out . num_changes ; n + + ) {
if ( not1 . out . changes [ n ] . action ! =
not2 . out . changes [ n ] . action ) {
printf ( " Notify action %d inconsistent %d %d \n " , n ,
not1 . out . changes [ n ] . action ,
not2 . out . changes [ n ] . action ) ;
return False ;
}
if ( strcmp ( not1 . out . changes [ n ] . name . s ,
not2 . out . changes [ n ] . name . s ) ) {
printf ( " Notify name %d inconsistent %s %s \n " , n ,
not1 . out . changes [ n ] . name . s ,
not2 . out . changes [ n ] . name . s ) ;
return False ;
}
if ( not1 . out . changes [ n ] . name . private_length ! =
not2 . out . changes [ n ] . name . private_length ) {
printf ( " Notify name length %d inconsistent %d %d \n " , n ,
not1 . out . changes [ n ] . name . private_length ,
not2 . out . changes [ n ] . name . private_length ) ;
return False ;
}
}
}
}
ZERO_STRUCT ( notifies ) ;
return True ;
}
# define GEN_COPY_PARM do { \
int i ; \
for ( i = 1 ; i < NSERVERS ; i + + ) { \
parm [ i ] = parm [ 0 ] ; \
} \
} while ( 0 )
# define GEN_CALL(call) do { \
int i ; \
ZERO_STRUCT ( oplocks ) ; \
ZERO_STRUCT ( notifies ) ; \
for ( i = 0 ; i < NSERVERS ; i + + ) { \
struct cli_tree * tree = servers [ i ] . cli [ instance ] - > tree ; \
status [ i ] = call ; \
} \
current_op . status = status [ 0 ] ; \
for ( i = 1 ; i < NSERVERS ; i + + ) { \
if ( ! compare_status ( status [ i ] , status [ 0 ] ) ) { \
printf ( " status different in %s - %s %s \n " , # call , \
nt_errstr ( status [ 0 ] ) , nt_errstr ( status [ i ] ) ) ; \
return False ; \
} \
} \
if ( ! check_oplocks ( # call ) ) return False ; \
if ( ! check_notifies ( # call ) ) return False ; \
if ( ! NT_STATUS_IS_OK ( status [ 0 ] ) ) { \
return True ; \
} \
} while ( 0 )
# define ADD_HANDLE(name, field) do { \
uint16 fnums [ NSERVERS ] ; \
int i ; \
for ( i = 0 ; i < NSERVERS ; i + + ) { \
fnums [ i ] = parm [ i ] . field ; \
} \
gen_add_handle ( instance , name , fnums ) ; \
} while ( 0 )
# define REMOVE_HANDLE(field) do { \
uint16 fnums [ NSERVERS ] ; \
int i ; \
for ( i = 0 ; i < NSERVERS ; i + + ) { \
fnums [ i ] = parm [ i ] . field ; \
} \
gen_remove_handle ( instance , fnums ) ; \
} while ( 0 )
# define GEN_SET_FNUM(field) do { \
int i ; \
for ( i = 0 ; i < NSERVERS ; i + + ) { \
parm [ i ] . field = gen_lookup_fnum ( i , parm [ i ] . field ) ; \
} \
} while ( 0 )
# define CHECK_EQUAL(field) do { \
if ( parm [ 0 ] . field ! = parm [ 1 ] . field & & ! ignore_pattern ( # field ) ) { \
printf ( " Mismatch in %s - 0x%x 0x%x \n " , # field , \
( int ) parm [ 0 ] . field , ( int ) parm [ 1 ] . field ) ; \
return False ; \
} \
} while ( 0 )
# define CHECK_WSTR_EQUAL(field) do { \
2003-08-13 06:03:16 +04:00
if ( ( ! parm [ 0 ] . field . s & & parm [ 1 ] . field . s ) | | ( parm [ 0 ] . field . s & & ! parm [ 1 ] . field . s ) ) { \
printf ( " %s is NULL! \n " , # field ) ; \
return False ; \
} \
if ( parm [ 0 ] . field . s & & strcmp ( parm [ 0 ] . field . s , parm [ 1 ] . field . s ) ! = 0 & & ! ignore_pattern ( # field ) ) { \
2003-08-13 05:53:07 +04:00
printf ( " Mismatch in %s - %s %s \n " , # field , \
parm [ 0 ] . field . s , parm [ 1 ] . field . s ) ; \
return False ; \
} \
CHECK_EQUAL ( field . private_length ) ; \
} while ( 0 )
# define CHECK_BLOB_EQUAL(field) do { \
if ( memcmp ( parm [ 0 ] . field . data , parm [ 1 ] . field . data , parm [ 0 ] . field . length ) ! = 0 & & ! ignore_pattern ( # field ) ) { \
printf ( " Mismatch in %s \n " , # field ) ; \
return False ; \
} \
CHECK_EQUAL ( field . length ) ; \
} while ( 0 )
# define CHECK_TIMES_EQUAL(field) do { \
if ( ABS ( parm [ 0 ] . field - parm [ 1 ] . field ) > time_skew ( ) & & \
! ignore_pattern ( # field ) ) { \
printf ( " Mismatch in %s - 0x%x 0x%x \n " , # field , \
( int ) parm [ 0 ] . field , ( int ) parm [ 1 ] . field ) ; \
return False ; \
} \
} while ( 0 )
# define CHECK_NTTIMES_EQUAL(field) do { \
if ( ABS ( nt_time_to_unix ( & parm [ 0 ] . field ) - \
nt_time_to_unix ( & parm [ 1 ] . field ) ) > time_skew ( ) & & \
! ignore_pattern ( # field ) ) { \
printf ( " Mismatch in %s - 0x%x 0x%x \n " , # field , \
( int ) nt_time_to_unix ( & parm [ 0 ] . field ) , \
( int ) nt_time_to_unix ( & parm [ 1 ] . field ) ) ; \
return False ; \
} \
} while ( 0 )
/*
generate openx operations
*/
static BOOL handler_openx ( int instance )
{
union smb_open parm [ NSERVERS ] ;
NTSTATUS status [ NSERVERS ] ;
parm [ 0 ] . openx . level = RAW_OPEN_OPENX ;
parm [ 0 ] . openx . in . flags = gen_openx_flags ( ) ;
parm [ 0 ] . openx . in . open_mode = gen_openx_mode ( ) ;
parm [ 0 ] . openx . in . search_attrs = gen_attrib ( ) ;
parm [ 0 ] . openx . in . file_attrs = gen_attrib ( ) ;
parm [ 0 ] . openx . in . write_time = gen_timet ( ) ;
parm [ 0 ] . openx . in . open_func = gen_openx_func ( ) ;
parm [ 0 ] . openx . in . size = gen_io_count ( ) ;
parm [ 0 ] . openx . in . timeout = gen_timeout ( ) ;
parm [ 0 ] . openx . in . fname = gen_fname_open ( instance ) ;
if ( ! options . use_oplocks ) {
/* mask out oplocks */
parm [ 0 ] . openx . in . flags & = ~ ( OPENX_FLAGS_REQUEST_OPLOCK |
OPENX_FLAGS_REQUEST_BATCH_OPLOCK ) ;
}
GEN_COPY_PARM ;
GEN_CALL ( smb_raw_open ( tree , current_op . mem_ctx , & parm [ i ] ) ) ;
CHECK_EQUAL ( openx . out . attrib ) ;
CHECK_EQUAL ( openx . out . size ) ;
CHECK_EQUAL ( openx . out . access ) ;
CHECK_EQUAL ( openx . out . ftype ) ;
CHECK_EQUAL ( openx . out . devstate ) ;
CHECK_EQUAL ( openx . out . action ) ;
CHECK_EQUAL ( openx . out . access_mask ) ;
CHECK_EQUAL ( openx . out . unknown ) ;
CHECK_TIMES_EQUAL ( openx . out . write_time ) ;
/* open creates a new file handle */
ADD_HANDLE ( parm [ 0 ] . openx . in . fname , openx . out . fnum ) ;
return True ;
}
2003-08-14 05:21:05 +04:00
/*
generate open operations
*/
static BOOL handler_open ( int instance )
{
union smb_open parm [ NSERVERS ] ;
NTSTATUS status [ NSERVERS ] ;
parm [ 0 ] . open . level = RAW_OPEN_OPEN ;
parm [ 0 ] . open . in . flags = gen_bits_mask2 ( 0xF , 0xFFFF ) ;
parm [ 0 ] . open . in . search_attrs = gen_attrib ( ) ;
parm [ 0 ] . open . in . fname = gen_fname_open ( instance ) ;
if ( ! options . use_oplocks ) {
/* mask out oplocks */
parm [ 0 ] . open . in . flags & = ~ ( OPENX_FLAGS_REQUEST_OPLOCK |
OPENX_FLAGS_REQUEST_BATCH_OPLOCK ) ;
}
GEN_COPY_PARM ;
GEN_CALL ( smb_raw_open ( tree , current_op . mem_ctx , & parm [ i ] ) ) ;
CHECK_EQUAL ( open . out . attrib ) ;
CHECK_TIMES_EQUAL ( open . out . write_time ) ;
CHECK_EQUAL ( open . out . size ) ;
CHECK_EQUAL ( open . out . rmode ) ;
/* open creates a new file handle */
ADD_HANDLE ( parm [ 0 ] . open . in . fname , open . out . fnum ) ;
return True ;
}
2003-08-13 05:53:07 +04:00
/*
generate ntcreatex operations
*/
static BOOL handler_ntcreatex ( int instance )
{
union smb_open parm [ NSERVERS ] ;
NTSTATUS status [ NSERVERS ] ;
parm [ 0 ] . ntcreatex . level = RAW_OPEN_NTCREATEX ;
parm [ 0 ] . ntcreatex . in . flags = gen_ntcreatex_flags ( ) ;
parm [ 0 ] . ntcreatex . in . root_fid = gen_root_fid ( instance ) ;
parm [ 0 ] . ntcreatex . in . access_mask = gen_access_mask ( ) ;
parm [ 0 ] . ntcreatex . in . alloc_size = gen_alloc_size ( ) ;
parm [ 0 ] . ntcreatex . in . file_attr = gen_attrib ( ) ;
parm [ 0 ] . ntcreatex . in . share_access = gen_bits_mask2 ( 0x7 , 0xFFFFFFFF ) ;
parm [ 0 ] . ntcreatex . in . open_disposition = gen_open_disp ( ) ;
parm [ 0 ] . ntcreatex . in . create_options = gen_create_options ( ) ;
parm [ 0 ] . ntcreatex . in . impersonation = gen_bits_mask2 ( 0 , 0xFFFFFFFF ) ;
parm [ 0 ] . ntcreatex . in . security_flags = gen_bits_mask2 ( 0 , 0xFF ) ;
parm [ 0 ] . ntcreatex . in . fname = gen_fname_open ( instance ) ;
if ( ! options . use_oplocks ) {
/* mask out oplocks */
parm [ 0 ] . ntcreatex . in . flags & = ~ ( NTCREATEX_FLAGS_REQUEST_OPLOCK |
NTCREATEX_FLAGS_REQUEST_BATCH_OPLOCK ) ;
}
GEN_COPY_PARM ;
if ( parm [ 0 ] . ntcreatex . in . root_fid ! = 0 ) {
GEN_SET_FNUM ( ntcreatex . in . root_fid ) ;
}
GEN_CALL ( smb_raw_open ( tree , current_op . mem_ctx , & parm [ i ] ) ) ;
CHECK_EQUAL ( ntcreatex . out . oplock_level ) ;
CHECK_EQUAL ( ntcreatex . out . create_action ) ;
CHECK_NTTIMES_EQUAL ( ntcreatex . out . create_time ) ;
CHECK_NTTIMES_EQUAL ( ntcreatex . out . access_time ) ;
CHECK_NTTIMES_EQUAL ( ntcreatex . out . write_time ) ;
CHECK_NTTIMES_EQUAL ( ntcreatex . out . change_time ) ;
CHECK_EQUAL ( ntcreatex . out . attrib ) ;
CHECK_EQUAL ( ntcreatex . out . alloc_size ) ;
CHECK_EQUAL ( ntcreatex . out . size ) ;
CHECK_EQUAL ( ntcreatex . out . file_type ) ;
CHECK_EQUAL ( ntcreatex . out . ipc_state ) ;
CHECK_EQUAL ( ntcreatex . out . is_directory ) ;
/* ntcreatex creates a new file handle */
ADD_HANDLE ( parm [ 0 ] . ntcreatex . in . fname , ntcreatex . out . fnum ) ;
return True ;
}
/*
generate close operations
*/
static BOOL handler_close ( int instance )
{
union smb_close parm [ NSERVERS ] ;
NTSTATUS status [ NSERVERS ] ;
parm [ 0 ] . close . level = RAW_CLOSE_CLOSE ;
parm [ 0 ] . close . in . fnum = gen_fnum_close ( instance ) ;
parm [ 0 ] . close . in . write_time = gen_timet ( ) ;
GEN_COPY_PARM ;
GEN_SET_FNUM ( close . in . fnum ) ;
GEN_CALL ( smb_raw_close ( tree , & parm [ i ] ) ) ;
REMOVE_HANDLE ( close . in . fnum ) ;
return True ;
}
/*
generate unlink operations
*/
static BOOL handler_unlink ( int instance )
{
struct smb_unlink parm [ NSERVERS ] ;
NTSTATUS status [ NSERVERS ] ;
parm [ 0 ] . in . pattern = gen_pattern ( ) ;
parm [ 0 ] . in . attrib = gen_attrib ( ) ;
GEN_COPY_PARM ;
GEN_CALL ( smb_raw_unlink ( tree , & parm [ i ] ) ) ;
return True ;
}
/*
generate chkpath operations
*/
static BOOL handler_chkpath ( int instance )
{
struct smb_chkpath parm [ NSERVERS ] ;
NTSTATUS status [ NSERVERS ] ;
parm [ 0 ] . in . path = gen_fname_open ( instance ) ;
GEN_COPY_PARM ;
GEN_CALL ( smb_raw_chkpath ( tree , & parm [ i ] ) ) ;
return True ;
}
/*
generate mkdir operations
*/
static BOOL handler_mkdir ( int instance )
{
union smb_mkdir parm [ NSERVERS ] ;
NTSTATUS status [ NSERVERS ] ;
parm [ 0 ] . mkdir . level = RAW_MKDIR_MKDIR ;
parm [ 0 ] . mkdir . in . path = gen_fname_open ( instance ) ;
GEN_COPY_PARM ;
GEN_CALL ( smb_raw_mkdir ( tree , & parm [ i ] ) ) ;
return True ;
}
/*
generate rmdir operations
*/
static BOOL handler_rmdir ( int instance )
{
struct smb_rmdir parm [ NSERVERS ] ;
NTSTATUS status [ NSERVERS ] ;
parm [ 0 ] . in . path = gen_fname_open ( instance ) ;
GEN_COPY_PARM ;
GEN_CALL ( smb_raw_rmdir ( tree , & parm [ i ] ) ) ;
return True ;
}
/*
generate rename operations
*/
static BOOL handler_rename ( int instance )
{
2003-08-13 20:04:21 +04:00
union smb_rename parm [ NSERVERS ] ;
2003-08-13 05:53:07 +04:00
NTSTATUS status [ NSERVERS ] ;
2003-08-13 20:04:21 +04:00
parm [ 0 ] . generic . level = RAW_RENAME_RENAME ;
parm [ 0 ] . rename . in . pattern1 = gen_pattern ( ) ;
parm [ 0 ] . rename . in . pattern2 = gen_pattern ( ) ;
parm [ 0 ] . rename . in . attrib = gen_attrib ( ) ;
GEN_COPY_PARM ;
GEN_CALL ( smb_raw_rename ( tree , & parm [ i ] ) ) ;
return True ;
}
/*
generate ntrename operations
*/
static BOOL handler_ntrename ( int instance )
{
union smb_rename parm [ NSERVERS ] ;
NTSTATUS status [ NSERVERS ] ;
parm [ 0 ] . generic . level = RAW_RENAME_NTRENAME ;
parm [ 0 ] . ntrename . in . old_name = gen_fname ( ) ;
parm [ 0 ] . ntrename . in . new_name = gen_fname ( ) ;
parm [ 0 ] . ntrename . in . attrib = gen_attrib ( ) ;
2003-08-14 02:23:18 +04:00
parm [ 0 ] . ntrename . in . unknown = gen_bits_mask2 ( 0 , 0xFFFFFFF ) ;
2003-08-13 20:04:21 +04:00
parm [ 0 ] . ntrename . in . flags = gen_rename_flags ( ) ;
2003-08-13 05:53:07 +04:00
GEN_COPY_PARM ;
GEN_CALL ( smb_raw_rename ( tree , & parm [ i ] ) ) ;
return True ;
}
2003-08-14 05:12:27 +04:00
/*
generate seek operations
*/
static BOOL handler_seek ( int instance )
{
struct smb_seek parm [ NSERVERS ] ;
NTSTATUS status [ NSERVERS ] ;
parm [ 0 ] . in . fnum = gen_fnum ( instance ) ;
parm [ 0 ] . in . mode = gen_bits_mask2 ( 0x3 , 0xFFFF ) ;
parm [ 0 ] . in . offset = gen_offset ( ) ;
GEN_COPY_PARM ;
GEN_SET_FNUM ( in . fnum ) ;
GEN_CALL ( smb_raw_seek ( tree , & parm [ i ] ) ) ;
CHECK_EQUAL ( out . offset ) ;
return True ;
}
2003-08-13 05:53:07 +04:00
/*
generate readx operations
*/
static BOOL handler_readx ( int instance )
{
union smb_read parm [ NSERVERS ] ;
NTSTATUS status [ NSERVERS ] ;
parm [ 0 ] . readx . level = RAW_READ_READX ;
parm [ 0 ] . readx . in . fnum = gen_fnum ( instance ) ;
parm [ 0 ] . readx . in . offset = gen_offset ( ) ;
parm [ 0 ] . readx . in . mincnt = gen_io_count ( ) ;
parm [ 0 ] . readx . in . maxcnt = gen_io_count ( ) ;
parm [ 0 ] . readx . in . remaining = gen_io_count ( ) ;
parm [ 0 ] . readx . out . data = talloc ( current_op . mem_ctx ,
MAX ( parm [ 0 ] . readx . in . mincnt , parm [ 0 ] . readx . in . maxcnt ) ) ;
GEN_COPY_PARM ;
GEN_SET_FNUM ( readx . in . fnum ) ;
GEN_CALL ( smb_raw_read ( tree , & parm [ i ] ) ) ;
CHECK_EQUAL ( readx . out . remaining ) ;
CHECK_EQUAL ( readx . out . compaction_mode ) ;
CHECK_EQUAL ( readx . out . nread ) ;
return True ;
}
/*
generate writex operations
*/
static BOOL handler_writex ( int instance )
{
union smb_write parm [ NSERVERS ] ;
NTSTATUS status [ NSERVERS ] ;
parm [ 0 ] . writex . level = RAW_WRITE_WRITEX ;
parm [ 0 ] . writex . in . fnum = gen_fnum ( instance ) ;
parm [ 0 ] . writex . in . offset = gen_offset ( ) ;
parm [ 0 ] . writex . in . wmode = gen_bits_mask ( 0xFFFF ) ;
parm [ 0 ] . writex . in . remaining = gen_io_count ( ) ;
parm [ 0 ] . writex . in . count = gen_io_count ( ) ;
parm [ 0 ] . writex . in . data = talloc_zero ( current_op . mem_ctx , parm [ 0 ] . writex . in . count ) ;
GEN_COPY_PARM ;
GEN_SET_FNUM ( writex . in . fnum ) ;
GEN_CALL ( smb_raw_write ( tree , & parm [ i ] ) ) ;
CHECK_EQUAL ( writex . out . nwritten ) ;
CHECK_EQUAL ( writex . out . remaining ) ;
return True ;
}
/*
generate lockingx operations
*/
static BOOL handler_lockingx ( int instance )
{
union smb_lock parm [ NSERVERS ] ;
NTSTATUS status [ NSERVERS ] ;
int n , nlocks ;
parm [ 0 ] . lockx . level = RAW_LOCK_LOCKX ;
parm [ 0 ] . lockx . in . fnum = gen_fnum ( instance ) ;
parm [ 0 ] . lockx . in . mode = gen_lock_mode ( ) ;
parm [ 0 ] . lockx . in . timeout = gen_timeout ( ) ;
do {
/* make sure we don't accidentially generate an oplock
break ack - otherwise the server can just block forever */
parm [ 0 ] . lockx . in . ulock_cnt = gen_lock_count ( ) ;
parm [ 0 ] . lockx . in . lock_cnt = gen_lock_count ( ) ;
nlocks = parm [ 0 ] . lockx . in . ulock_cnt + parm [ 0 ] . lockx . in . lock_cnt ;
} while ( nlocks = = 0 ) ;
if ( nlocks > 0 ) {
parm [ 0 ] . lockx . in . locks = talloc ( current_op . mem_ctx ,
sizeof ( parm [ 0 ] . lockx . in . locks [ 0 ] ) * nlocks ) ;
for ( n = 0 ; n < nlocks ; n + + ) {
parm [ 0 ] . lockx . in . locks [ n ] . pid = gen_pid ( ) ;
parm [ 0 ] . lockx . in . locks [ n ] . offset = gen_offset ( ) ;
parm [ 0 ] . lockx . in . locks [ n ] . count = gen_io_count ( ) ;
}
}
GEN_COPY_PARM ;
GEN_SET_FNUM ( lockx . in . fnum ) ;
GEN_CALL ( smb_raw_lock ( tree , & parm [ i ] ) ) ;
return True ;
}
/*
generate a fileinfo query structure
*/
static void gen_fileinfo ( int instance , union smb_fileinfo * info )
{
int i ;
# define LVL(v) {RAW_FILEINFO_ ## v, "RAW_FILEINFO_" #v}
struct {
enum fileinfo_level level ;
const char * name ;
} levels [ ] = {
LVL ( GETATTR ) , LVL ( GETATTRE ) , LVL ( STANDARD ) ,
LVL ( EA_SIZE ) , LVL ( ALL_EAS ) , LVL ( IS_NAME_VALID ) ,
LVL ( BASIC_INFO ) , LVL ( STANDARD_INFO ) , LVL ( EA_INFO ) ,
LVL ( NAME_INFO ) , LVL ( ALL_INFO ) , LVL ( ALT_NAME_INFO ) ,
LVL ( STREAM_INFO ) , LVL ( COMPRESSION_INFO ) , LVL ( BASIC_INFORMATION ) ,
LVL ( STANDARD_INFORMATION ) , LVL ( INTERNAL_INFORMATION ) , LVL ( EA_INFORMATION ) ,
LVL ( ACCESS_INFORMATION ) , LVL ( NAME_INFORMATION ) , LVL ( POSITION_INFORMATION ) ,
LVL ( MODE_INFORMATION ) , LVL ( ALIGNMENT_INFORMATION ) , LVL ( ALL_INFORMATION ) ,
LVL ( ALT_NAME_INFORMATION ) , LVL ( STREAM_INFORMATION ) , LVL ( COMPRESSION_INFORMATION ) ,
LVL ( NETWORK_OPEN_INFORMATION ) , LVL ( ATTRIBUTE_TAG_INFORMATION )
} ;
do {
i = gen_int_range ( 0 , ARRAY_SIZE ( levels ) - 1 ) ;
} while ( ignore_pattern ( levels [ i ] . name ) ) ;
info - > generic . level = levels [ i ] . level ;
}
/*
compare returned fileinfo structures
*/
static BOOL cmp_fileinfo ( int instance ,
union smb_fileinfo parm [ NSERVERS ] ,
NTSTATUS status [ NSERVERS ] )
{
int i ;
switch ( parm [ 0 ] . generic . level ) {
case RAW_FILEINFO_GENERIC :
return False ;
case RAW_FILEINFO_GETATTR :
CHECK_EQUAL ( getattr . out . attrib ) ;
CHECK_EQUAL ( getattr . out . size ) ;
CHECK_TIMES_EQUAL ( getattr . out . write_time ) ;
break ;
case RAW_FILEINFO_GETATTRE :
CHECK_TIMES_EQUAL ( getattre . out . create_time ) ;
CHECK_TIMES_EQUAL ( getattre . out . access_time ) ;
CHECK_TIMES_EQUAL ( getattre . out . write_time ) ;
CHECK_EQUAL ( getattre . out . size ) ;
CHECK_EQUAL ( getattre . out . alloc_size ) ;
CHECK_EQUAL ( getattre . out . attrib ) ;
break ;
case RAW_FILEINFO_STANDARD :
CHECK_TIMES_EQUAL ( standard . out . create_time ) ;
CHECK_TIMES_EQUAL ( standard . out . access_time ) ;
CHECK_TIMES_EQUAL ( standard . out . write_time ) ;
CHECK_EQUAL ( standard . out . size ) ;
CHECK_EQUAL ( standard . out . alloc_size ) ;
CHECK_EQUAL ( standard . out . attrib ) ;
break ;
case RAW_FILEINFO_EA_SIZE :
CHECK_TIMES_EQUAL ( ea_size . out . create_time ) ;
CHECK_TIMES_EQUAL ( ea_size . out . access_time ) ;
CHECK_TIMES_EQUAL ( ea_size . out . write_time ) ;
CHECK_EQUAL ( ea_size . out . size ) ;
CHECK_EQUAL ( ea_size . out . alloc_size ) ;
CHECK_EQUAL ( ea_size . out . attrib ) ;
CHECK_EQUAL ( ea_size . out . ea_size ) ;
break ;
case RAW_FILEINFO_ALL_EAS :
CHECK_EQUAL ( all_eas . out . num_eas ) ;
for ( i = 0 ; i < parm [ 0 ] . all_eas . out . num_eas ; i + + ) {
CHECK_EQUAL ( all_eas . out . eas [ i ] . flags ) ;
CHECK_WSTR_EQUAL ( all_eas . out . eas [ i ] . name ) ;
CHECK_BLOB_EQUAL ( all_eas . out . eas [ i ] . value ) ;
}
break ;
case RAW_FILEINFO_IS_NAME_VALID :
break ;
case RAW_FILEINFO_BASIC_INFO :
case RAW_FILEINFO_BASIC_INFORMATION :
CHECK_NTTIMES_EQUAL ( basic_info . out . create_time ) ;
CHECK_NTTIMES_EQUAL ( basic_info . out . access_time ) ;
CHECK_NTTIMES_EQUAL ( basic_info . out . write_time ) ;
CHECK_NTTIMES_EQUAL ( basic_info . out . change_time ) ;
CHECK_EQUAL ( basic_info . out . attrib ) ;
break ;
case RAW_FILEINFO_STANDARD_INFO :
case RAW_FILEINFO_STANDARD_INFORMATION :
CHECK_EQUAL ( standard_info . out . alloc_size ) ;
CHECK_EQUAL ( standard_info . out . size ) ;
CHECK_EQUAL ( standard_info . out . nlink ) ;
CHECK_EQUAL ( standard_info . out . delete_pending ) ;
CHECK_EQUAL ( standard_info . out . directory ) ;
break ;
case RAW_FILEINFO_EA_INFO :
case RAW_FILEINFO_EA_INFORMATION :
CHECK_EQUAL ( ea_info . out . ea_size ) ;
break ;
case RAW_FILEINFO_NAME_INFO :
case RAW_FILEINFO_NAME_INFORMATION :
CHECK_WSTR_EQUAL ( name_info . out . fname ) ;
break ;
case RAW_FILEINFO_ALL_INFO :
case RAW_FILEINFO_ALL_INFORMATION :
CHECK_NTTIMES_EQUAL ( all_info . out . create_time ) ;
CHECK_NTTIMES_EQUAL ( all_info . out . access_time ) ;
CHECK_NTTIMES_EQUAL ( all_info . out . write_time ) ;
CHECK_NTTIMES_EQUAL ( all_info . out . change_time ) ;
CHECK_EQUAL ( all_info . out . attrib ) ;
CHECK_EQUAL ( all_info . out . alloc_size ) ;
CHECK_EQUAL ( all_info . out . size ) ;
CHECK_EQUAL ( all_info . out . nlink ) ;
CHECK_EQUAL ( all_info . out . delete_pending ) ;
CHECK_EQUAL ( all_info . out . directory ) ;
CHECK_EQUAL ( all_info . out . ea_size ) ;
CHECK_WSTR_EQUAL ( all_info . out . fname ) ;
break ;
case RAW_FILEINFO_ALT_NAME_INFO :
case RAW_FILEINFO_ALT_NAME_INFORMATION :
CHECK_WSTR_EQUAL ( alt_name_info . out . fname ) ;
break ;
case RAW_FILEINFO_STREAM_INFO :
case RAW_FILEINFO_STREAM_INFORMATION :
CHECK_EQUAL ( stream_info . out . num_streams ) ;
for ( i = 0 ; i < parm [ 0 ] . stream_info . out . num_streams ; i + + ) {
CHECK_EQUAL ( stream_info . out . streams [ i ] . size ) ;
CHECK_EQUAL ( stream_info . out . streams [ i ] . alloc_size ) ;
CHECK_WSTR_EQUAL ( stream_info . out . streams [ i ] . stream_name ) ;
}
break ;
case RAW_FILEINFO_COMPRESSION_INFO :
case RAW_FILEINFO_COMPRESSION_INFORMATION :
CHECK_EQUAL ( compression_info . out . compressed_size ) ;
CHECK_EQUAL ( compression_info . out . format ) ;
CHECK_EQUAL ( compression_info . out . unit_shift ) ;
CHECK_EQUAL ( compression_info . out . chunk_shift ) ;
CHECK_EQUAL ( compression_info . out . cluster_shift ) ;
break ;
case RAW_FILEINFO_INTERNAL_INFORMATION :
CHECK_EQUAL ( internal_information . out . device ) ;
CHECK_EQUAL ( internal_information . out . inode ) ;
break ;
case RAW_FILEINFO_ACCESS_INFORMATION :
CHECK_EQUAL ( access_information . out . access_flags ) ;
break ;
case RAW_FILEINFO_POSITION_INFORMATION :
CHECK_EQUAL ( position_information . out . position ) ;
break ;
case RAW_FILEINFO_MODE_INFORMATION :
CHECK_EQUAL ( mode_information . out . mode ) ;
break ;
case RAW_FILEINFO_ALIGNMENT_INFORMATION :
CHECK_EQUAL ( alignment_information . out . alignment_requirement ) ;
break ;
case RAW_FILEINFO_NETWORK_OPEN_INFORMATION :
CHECK_NTTIMES_EQUAL ( network_open_information . out . create_time ) ;
CHECK_NTTIMES_EQUAL ( network_open_information . out . access_time ) ;
CHECK_NTTIMES_EQUAL ( network_open_information . out . write_time ) ;
CHECK_NTTIMES_EQUAL ( network_open_information . out . change_time ) ;
CHECK_EQUAL ( network_open_information . out . alloc_size ) ;
CHECK_EQUAL ( network_open_information . out . size ) ;
CHECK_EQUAL ( network_open_information . out . attrib ) ;
break ;
case RAW_FILEINFO_ATTRIBUTE_TAG_INFORMATION :
CHECK_EQUAL ( attribute_tag_information . out . attrib ) ;
CHECK_EQUAL ( attribute_tag_information . out . reparse_tag ) ;
break ;
}
return True ;
}
/*
generate qpathinfo operations
*/
static BOOL handler_qpathinfo ( int instance )
{
union smb_fileinfo parm [ NSERVERS ] ;
NTSTATUS status [ NSERVERS ] ;
parm [ 0 ] . generic . in . fname = gen_fname_open ( instance ) ;
gen_fileinfo ( instance , & parm [ 0 ] ) ;
GEN_COPY_PARM ;
GEN_CALL ( smb_raw_pathinfo ( tree , current_op . mem_ctx , & parm [ i ] ) ) ;
return cmp_fileinfo ( instance , parm , status ) ;
}
/*
generate qfileinfo operations
*/
static BOOL handler_qfileinfo ( int instance )
{
union smb_fileinfo parm [ NSERVERS ] ;
NTSTATUS status [ NSERVERS ] ;
parm [ 0 ] . generic . in . fnum = gen_fnum ( instance ) ;
gen_fileinfo ( instance , & parm [ 0 ] ) ;
GEN_COPY_PARM ;
GEN_SET_FNUM ( generic . in . fnum ) ;
GEN_CALL ( smb_raw_fileinfo ( tree , current_op . mem_ctx , & parm [ i ] ) ) ;
return cmp_fileinfo ( instance , parm , status ) ;
}
/*
generate a fileinfo query structure
*/
static void gen_setfileinfo ( int instance , union smb_setfileinfo * info )
{
int i ;
# undef LVL
# define LVL(v) {RAW_SFILEINFO_ ## v, "RAW_SFILEINFO_" #v}
struct {
enum setfileinfo_level level ;
const char * name ;
} levels [ ] = {
#if 0
/* disabled until win2003 can handle them ... */
LVL ( EA_SET ) , LVL ( BASIC_INFO ) , LVL ( DISPOSITION_INFO ) ,
LVL ( STANDARD ) , LVL ( ALLOCATION_INFO ) , LVL ( END_OF_FILE_INFO ) ,
# endif
LVL ( SETATTR ) , LVL ( SETATTRE ) , LVL ( BASIC_INFORMATION ) ,
LVL ( RENAME_INFORMATION ) , LVL ( DISPOSITION_INFORMATION ) ,
LVL ( POSITION_INFORMATION ) , LVL ( MODE_INFORMATION ) ,
LVL ( ALLOCATION_INFORMATION ) , LVL ( END_OF_FILE_INFORMATION ) ,
LVL ( 1023 ) , LVL ( 1025 ) , LVL ( 1029 ) , LVL ( 1032 ) , LVL ( 1039 ) , LVL ( 1040 )
} ;
do {
i = gen_int_range ( 0 , ARRAY_SIZE ( levels ) - 1 ) ;
} while ( ignore_pattern ( levels [ i ] . name ) ) ;
info - > generic . level = levels [ i ] . level ;
switch ( info - > generic . level ) {
case RAW_SFILEINFO_SETATTR :
info - > setattr . in . attrib = gen_attrib ( ) ;
info - > setattr . in . write_time = gen_timet ( ) ;
break ;
case RAW_SFILEINFO_SETATTRE :
info - > setattre . in . create_time = gen_timet ( ) ;
info - > setattre . in . access_time = gen_timet ( ) ;
info - > setattre . in . write_time = gen_timet ( ) ;
break ;
case RAW_SFILEINFO_STANDARD :
info - > standard . in . create_time = gen_timet ( ) ;
info - > standard . in . access_time = gen_timet ( ) ;
info - > standard . in . write_time = gen_timet ( ) ;
break ;
case RAW_SFILEINFO_EA_SET :
info - > ea_set . in . ea = gen_ea_struct ( ) ;
break ;
case RAW_SFILEINFO_BASIC_INFO :
case RAW_SFILEINFO_BASIC_INFORMATION :
info - > basic_info . in . create_time = gen_nttime ( ) ;
info - > basic_info . in . access_time = gen_nttime ( ) ;
info - > basic_info . in . write_time = gen_nttime ( ) ;
info - > basic_info . in . change_time = gen_nttime ( ) ;
info - > basic_info . in . attrib = gen_attrib ( ) ;
break ;
case RAW_SFILEINFO_DISPOSITION_INFO :
case RAW_SFILEINFO_DISPOSITION_INFORMATION :
info - > disposition_info . in . delete_on_close = gen_bool ( ) ;
break ;
case RAW_SFILEINFO_ALLOCATION_INFO :
case RAW_SFILEINFO_ALLOCATION_INFORMATION :
info - > allocation_info . in . alloc_size = gen_alloc_size ( ) ;
break ;
case RAW_SFILEINFO_END_OF_FILE_INFO :
case RAW_SFILEINFO_END_OF_FILE_INFORMATION :
info - > end_of_file_info . in . size = gen_offset ( ) ;
break ;
case RAW_SFILEINFO_RENAME_INFORMATION :
info - > rename_information . in . overwrite = gen_bool ( ) ;
info - > rename_information . in . root_fid = gen_root_fid ( instance ) ;
info - > rename_information . in . new_name = gen_fname_open ( instance ) ;
break ;
case RAW_SFILEINFO_POSITION_INFORMATION :
info - > position_information . in . position = gen_offset ( ) ;
break ;
case RAW_SFILEINFO_MODE_INFORMATION :
info - > mode_information . in . mode = gen_bits_mask ( 0xFFFFFFFF ) ;
break ;
}
}
/*
generate setpathinfo operations
*/
static BOOL handler_spathinfo ( int instance )
{
union smb_setfileinfo parm [ NSERVERS ] ;
NTSTATUS status [ NSERVERS ] ;
parm [ 0 ] . generic . file . fname = gen_fname_open ( instance ) ;
gen_setfileinfo ( instance , & parm [ 0 ] ) ;
GEN_COPY_PARM ;
/* a special case for the fid in a RENAME */
if ( parm [ 0 ] . generic . level = = RAW_SFILEINFO_RENAME_INFORMATION & &
parm [ 0 ] . rename_information . in . root_fid ! = 0 ) {
GEN_SET_FNUM ( rename_information . in . root_fid ) ;
}
GEN_CALL ( smb_raw_setpathinfo ( tree , & parm [ i ] ) ) ;
return True ;
}
/*
generate setfileinfo operations
*/
static BOOL handler_sfileinfo ( int instance )
{
union smb_setfileinfo parm [ NSERVERS ] ;
NTSTATUS status [ NSERVERS ] ;
parm [ 0 ] . generic . file . fnum = gen_fnum ( instance ) ;
gen_setfileinfo ( instance , & parm [ 0 ] ) ;
GEN_COPY_PARM ;
GEN_SET_FNUM ( generic . file . fnum ) ;
GEN_CALL ( smb_raw_setfileinfo ( tree , & parm [ i ] ) ) ;
return True ;
}
/*
generate change notify operations
*/
static BOOL handler_notify ( int instance )
{
struct smb_notify parm [ NSERVERS ] ;
int n ;
parm [ 0 ] . in . buffer_size = gen_io_count ( ) ;
parm [ 0 ] . in . completion_filter = gen_bits_mask ( 0xFF ) ;
parm [ 0 ] . in . fnum = gen_fnum ( instance ) ;
parm [ 0 ] . in . recursive = gen_bool ( ) ;
GEN_COPY_PARM ;
GEN_SET_FNUM ( in . fnum ) ;
for ( n = 0 ; n < NSERVERS ; n + + ) {
struct cli_request * req ;
req = smb_raw_changenotify_send ( servers [ n ] . cli [ instance ] - > tree , & parm [ n ] ) ;
req - > async . fn = async_notify ;
}
return True ;
}
/*
wipe any relevant files
*/
static void wipe_files ( void )
{
int i ;
for ( i = 0 ; i < NSERVERS ; i + + ) {
int n = cli_deltree ( servers [ i ] . cli [ 0 ] , " \\ gentest " ) ;
if ( n = = - 1 ) {
printf ( " Failed to wipe tree on server %d \n " , i ) ;
exit ( 1 ) ;
}
if ( ! cli_mkdir ( servers [ i ] . cli [ 0 ] , " \\ gentest " ) ) {
printf ( " Failed to create \\ gentest - %s \n " ,
cli_errstr ( servers [ i ] . cli [ 0 ] ) ) ;
exit ( 1 ) ;
}
if ( n > 0 ) {
printf ( " Deleted %d files on server %d \n " , n , i ) ;
}
}
}
/*
dump the current seeds - useful for continuing a backtrack
*/
static void dump_seeds ( void )
{
int i ;
FILE * f ;
if ( ! options . seeds_file ) {
return ;
}
f = fopen ( " seeds.tmp " , " w " ) ;
if ( ! f ) return ;
for ( i = 0 ; i < options . numops ; i + + ) {
fprintf ( f , " %u \n " , op_parms [ i ] . seed ) ;
}
fclose ( f ) ;
rename ( " seeds.tmp " , options . seeds_file ) ;
}
/*
the list of top - level operations that we will generate
*/
static struct {
const char * name ;
BOOL ( * handler ) ( int instance ) ;
int count , success_count ;
} gen_ops [ ] = {
2003-08-14 05:21:05 +04:00
{ " OPEN " , handler_open } ,
2003-08-13 05:53:07 +04:00
{ " OPENX " , handler_openx } ,
{ " NTCREATEX " , handler_ntcreatex } ,
{ " CLOSE " , handler_close } ,
{ " UNLINK " , handler_unlink } ,
{ " MKDIR " , handler_mkdir } ,
{ " RMDIR " , handler_rmdir } ,
{ " RENAME " , handler_rename } ,
2003-08-13 20:04:21 +04:00
{ " NTRENAME " , handler_ntrename } ,
2003-08-13 05:53:07 +04:00
{ " READX " , handler_readx } ,
{ " WRITEX " , handler_writex } ,
{ " CHKPATH " , handler_chkpath } ,
{ " LOCKINGX " , handler_lockingx } ,
{ " QPATHINFO " , handler_qpathinfo } ,
{ " QFILEINFO " , handler_qfileinfo } ,
{ " SPATHINFO " , handler_spathinfo } ,
{ " SFILEINFO " , handler_sfileinfo } ,
{ " NOTIFY " , handler_notify } ,
2003-08-14 05:12:27 +04:00
{ " SEEK " , handler_seek } ,
2003-08-13 05:53:07 +04:00
} ;
/*
run the test with the current set of op_parms parameters
return the number of operations that completed successfully
*/
static int run_test ( void )
{
int op , i ;
if ( ! connect_servers ( ) ) {
printf ( " Failed to connect to servers \n " ) ;
exit ( 1 ) ;
}
dump_seeds ( ) ;
/* wipe any leftover files from old runs */
wipe_files ( ) ;
/* reset the open handles array */
memset ( open_handles , 0 , options . max_open_handles * sizeof ( open_handles [ 0 ] ) ) ;
num_open_handles = 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( gen_ops ) ; i + + ) {
gen_ops [ i ] . count = 0 ;
gen_ops [ i ] . success_count = 0 ;
}
for ( op = 0 ; op < options . numops ; op + + ) {
int instance , which_op ;
BOOL ret ;
if ( op_parms [ op ] . disabled ) continue ;
srandom ( op_parms [ op ] . seed ) ;
instance = gen_int_range ( 0 , NINSTANCES - 1 ) ;
/* generate a non-ignored operation */
do {
which_op = gen_int_range ( 0 , ARRAY_SIZE ( gen_ops ) - 1 ) ;
} while ( ignore_pattern ( gen_ops [ which_op ] . name ) ) ;
DEBUG ( 3 , ( " Generating op %s on instance %d \n " ,
gen_ops [ which_op ] . name , instance ) ) ;
current_op . seed = op_parms [ op ] . seed ;
current_op . opnum = op ;
current_op . name = gen_ops [ which_op ] . name ;
current_op . status = NT_STATUS_OK ;
current_op . mem_ctx = talloc_init ( current_op . name ) ;
ret = gen_ops [ which_op ] . handler ( instance ) ;
talloc_destroy ( current_op . mem_ctx ) ;
gen_ops [ which_op ] . count + + ;
if ( NT_STATUS_IS_OK ( current_op . status ) ) {
gen_ops [ which_op ] . success_count + + ;
}
if ( ! ret ) {
printf ( " Failed at operation %d - %s \n " ,
op , gen_ops [ which_op ] . name ) ;
return op ;
}
if ( op % 100 = = 0 ) {
printf ( " %d \n " , op ) ;
}
}
for ( i = 0 ; i < ARRAY_SIZE ( gen_ops ) ; i + + ) {
printf ( " Op %-10s got %d/%d success \n " ,
gen_ops [ i ] . name ,
gen_ops [ i ] . success_count ,
gen_ops [ i ] . count ) ;
}
return op ;
}
/*
perform a backtracking analysis of the minimal set of operations
to generate an error
*/
static void backtrack_analyze ( void )
{
int chunk , ret ;
chunk = options . numops / 2 ;
do {
int base ;
for ( base = 0 ;
chunk > 0 & & base + chunk < options . numops & & options . numops > 1 ; ) {
int i , max ;
chunk = MIN ( chunk , options . numops / 2 ) ;
/* mark this range as disabled */
max = MIN ( options . numops , base + chunk ) ;
for ( i = base ; i < max ; i + + ) {
op_parms [ i ] . disabled = True ;
}
printf ( " Testing %d ops with %d-%d disabled \n " ,
options . numops , base , max - 1 ) ;
ret = run_test ( ) ;
printf ( " Completed %d of %d ops \n " , ret , options . numops ) ;
for ( i = base ; i < max ; i + + ) {
op_parms [ i ] . disabled = False ;
}
if ( ret = = options . numops ) {
/* this chunk is needed */
base + = chunk ;
} else if ( ret < base ) {
printf ( " damn - inconsistent errors! found early error \n " ) ;
options . numops = ret + 1 ;
base = 0 ;
} else {
/* it failed - this chunk isn't needed for a failure */
memmove ( & op_parms [ base ] , & op_parms [ max ] ,
sizeof ( op_parms [ 0 ] ) * ( options . numops - max ) ) ;
options . numops = ( ret + 1 ) - ( max - base ) ;
}
}
if ( chunk = = 2 ) {
chunk = 1 ;
} else {
chunk * = 0.4 ;
}
if ( options . analyze_continuous & & chunk = = 0 & & options . numops ! = 1 ) {
chunk = 1 ;
}
} while ( chunk > 0 ) ;
printf ( " Reduced to %d ops \n " , options . numops ) ;
ret = run_test ( ) ;
if ( ret ! = options . numops - 1 ) {
printf ( " Inconsistent result? ret=%d numops=%d \n " , ret , options . numops ) ;
}
}
/*
start the main gentest process
*/
static BOOL start_gentest ( void )
{
int op ;
int ret ;
/* allocate the open_handles array */
open_handles = calloc ( options . max_open_handles , sizeof ( open_handles [ 0 ] ) ) ;
srandom ( options . seed ) ;
op_parms = calloc ( options . numops , sizeof ( op_parms [ 0 ] ) ) ;
/* generate the seeds - after this everything is deterministic */
if ( options . use_preset_seeds ) {
int numops ;
char * * preset = file_lines_load ( options . seeds_file , & numops ) ;
if ( ! preset ) {
printf ( " Failed to load %s - %s \n " , options . seeds_file , strerror ( errno ) ) ;
exit ( 1 ) ;
}
if ( numops < options . numops ) {
options . numops = numops ;
}
for ( op = 0 ; op < options . numops ; op + + ) {
if ( ! preset [ op ] ) {
printf ( " Not enough seeds in %s \n " , options . seeds_file ) ;
exit ( 1 ) ;
}
op_parms [ op ] . seed = atoi ( preset [ op ] ) ;
}
printf ( " Loaded %d seeds from %s \n " , options . numops , options . seeds_file ) ;
} else {
for ( op = 0 ; op < options . numops ; op + + ) {
op_parms [ op ] . seed = random ( ) ;
}
}
ret = run_test ( ) ;
if ( ret ! = options . numops & & options . analyze ) {
options . numops = ret + 1 ;
backtrack_analyze ( ) ;
} else if ( options . analyze_always ) {
backtrack_analyze ( ) ;
} else if ( options . analyze_continuous ) {
while ( run_test ( ) = = options . numops ) ;
}
return ret = = options . numops ;
}
static void usage ( void )
{
printf (
" Usage: \n \
gentest2 / / server1 / share1 / / server2 / share2 [ options . . ] \ n \
options : \ n \
- U user % % pass ( must be specified twice ) \ n \
- s seed \ n \
- o numops \ n \
- a ( show all ops ) \ n \
- A backtrack to find minimal ops \ n \
- i FILE add a list of wildcard exclusions \ n \
- O enable oplocks \ n \
- S FILE set preset seeds file \ n \
- L use preset seeds \ n \
- F fast reconnect ( just close files ) \ n \
- C continuous analysis mode \ n \
- X analyse even when test OK \ n \
" );
}
/****************************************************************************
main program
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int main ( int argc , char * argv [ ] )
{
int opt ;
int i ;
BOOL ret ;
setlinebuf ( stdout ) ;
setup_logging ( " gentest " , DEBUG_STDOUT ) ;
if ( argc < 3 | | argv [ 1 ] [ 0 ] = = ' - ' ) {
usage ( ) ;
exit ( 1 ) ;
}
setup_logging ( argv [ 0 ] , True ) ;
for ( i = 0 ; i < NSERVERS ; i + + ) {
const char * share = argv [ 1 + i ] ;
if ( ! split_unc_name ( share , & servers [ i ] . server_name , & servers [ i ] . share_name ) ) {
printf ( " Invalid share name '%s' \n " , share ) ;
return - 1 ;
}
}
argc - = NSERVERS ;
argv + = NSERVERS ;
lp_load ( dyn_CONFIGFILE , True , False , False ) ;
load_interfaces ( ) ;
options . seed = time ( NULL ) ;
options . numops = 1000 ;
options . max_open_handles = 20 ;
options . seeds_file = " gentest_seeds.dat " ;
while ( ( opt = getopt ( argc , argv , " U:s:o:ad:i:AOhS:LFXC " ) ) ! = EOF ) {
switch ( opt ) {
case ' U ' :
i = servers [ 0 ] . username ? 1 : 0 ;
if ( ! split_username ( optarg ,
& servers [ i ] . username ,
& servers [ i ] . password ) ) {
printf ( " Must supply USER%%PASS \n " ) ;
return - 1 ;
}
break ;
case ' d ' :
DEBUGLEVEL = atoi ( optarg ) ;
setup_logging ( NULL , True ) ;
break ;
case ' s ' :
options . seed = atoi ( optarg ) ;
break ;
case ' S ' :
options . seeds_file = optarg ;
break ;
case ' L ' :
options . use_preset_seeds = True ;
break ;
case ' F ' :
options . fast_reconnect = True ;
break ;
case ' o ' :
options . numops = atoi ( optarg ) ;
break ;
case ' O ' :
options . use_oplocks = True ;
break ;
case ' a ' :
options . showall = True ;
break ;
case ' A ' :
options . analyze = True ;
break ;
case ' X ' :
options . analyze_always = True ;
break ;
case ' C ' :
options . analyze_continuous = True ;
break ;
case ' i ' :
options . ignore_patterns = file_lines_load ( optarg , NULL ) ;
break ;
case ' h ' :
usage ( ) ;
exit ( 1 ) ;
default :
printf ( " Unknown option %c (%d) \n " , ( char ) opt , opt ) ;
exit ( 1 ) ;
}
}
if ( ! servers [ 0 ] . username | | ! servers [ 1 ] . username ) {
usage ( ) ;
return - 1 ;
}
printf ( " seed=%u \n " , options . seed ) ;
ret = start_gentest ( ) ;
if ( ret ) {
printf ( " gentest completed - no errors \n " ) ;
} else {
printf ( " gentest failed \n " ) ;
}
return ret ? 0 : - 1 ;
}