2003-08-13 05:53:07 +04:00
/*
Unix SMB / CIFS implementation .
RAW_QFS_ * individual test suite
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
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2003-08-13 05:53:07 +04:00
( 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
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2003-08-13 05:53:07 +04:00
*/
# include "includes.h"
2017-11-19 20:11:28 +03:00
# include <math.h>
2006-01-03 18:40:05 +03:00
# include "libcli/libcli.h"
2006-03-17 20:59:58 +03:00
# include "torture/util.h"
2011-03-19 02:42:42 +03:00
# include "torture/basic/proto.h"
# include "torture/raw/proto.h"
2003-08-13 05:53:07 +04:00
static struct {
const char * name ;
2004-07-19 14:35:05 +04:00
enum smb_fsinfo_level level ;
2004-05-25 20:24:13 +04:00
uint32_t capability_mask ;
2003-08-13 05:53:07 +04:00
NTSTATUS status ;
union smb_fsinfo fsinfo ;
} levels [ ] = {
2018-12-13 14:43:14 +03:00
{
. name = " DSKATTR " ,
. level = RAW_QFS_DSKATTR ,
} ,
{
. name = " ALLOCATION " ,
. level = RAW_QFS_ALLOCATION ,
} ,
{
. name = " VOLUME " ,
. level = RAW_QFS_VOLUME ,
} ,
{
. name = " VOLUME_INFO " ,
. level = RAW_QFS_VOLUME_INFO ,
} ,
{
. name = " SIZE_INFO " ,
. level = RAW_QFS_SIZE_INFO ,
} ,
{
. name = " DEVICE_INFO " ,
. level = RAW_QFS_DEVICE_INFO ,
} ,
{
. name = " ATTRIBUTE_INFO " ,
. level = RAW_QFS_ATTRIBUTE_INFO ,
} ,
{
. name = " UNIX_INFO " ,
. level = RAW_QFS_UNIX_INFO ,
. capability_mask = CAP_UNIX ,
} ,
{
. name = " VOLUME_INFORMATION " ,
. level = RAW_QFS_VOLUME_INFORMATION ,
} ,
{
. name = " SIZE_INFORMATION " ,
. level = RAW_QFS_SIZE_INFORMATION ,
} ,
{
. name = " DEVICE_INFORMATION " ,
. level = RAW_QFS_DEVICE_INFORMATION ,
} ,
{
. name = " ATTRIBUTE_INFORMATION " ,
. level = RAW_QFS_ATTRIBUTE_INFORMATION ,
} ,
{
. name = " QUOTA_INFORMATION " ,
. level = RAW_QFS_QUOTA_INFORMATION ,
} ,
{
. name = " FULL_SIZE_INFORMATION " ,
. level = RAW_QFS_FULL_SIZE_INFORMATION ,
} ,
2005-07-04 09:54:09 +04:00
#if 0
/* w2k3 seems to no longer support this */
2003-08-13 05:53:07 +04:00
{ " OBJECTID_INFORMATION " , RAW_QFS_OBJECTID_INFORMATION , } ,
2005-07-04 09:54:09 +04:00
# endif
2018-12-13 14:43:14 +03:00
{ . name = NULL , } ,
2003-08-13 05:53:07 +04:00
} ;
/*
find a level in the levels [ ] table
*/
static union smb_fsinfo * find ( const char * name )
{
int i ;
for ( i = 0 ; levels [ i ] . name ; i + + ) {
2003-08-15 19:12:30 +04:00
if ( strcmp ( name , levels [ i ] . name ) = = 0 & &
NT_STATUS_IS_OK ( levels [ i ] . status ) ) {
2003-08-13 05:53:07 +04:00
return & levels [ i ] . fsinfo ;
}
}
return NULL ;
}
/* local macros to make the code below more readable */
# define VAL_EQUAL(n1, v1, n2, v2) do {if (s1->n1.out.v1 != s2->n2.out.v2) { \
printf ( " %s/%s [%u] != %s/%s [%u] at %s(%d) \n " , \
2010-01-05 20:42:54 +03:00
# n1, #v1, (unsigned int)s1->n1.out.v1, \
# n2, #v2, (unsigned int)s2->n2.out.v2, \
2003-08-13 05:53:07 +04:00
__FILE__ , __LINE__ ) ; \
2007-10-07 02:28:14 +04:00
ret = false ; \
2003-08-13 05:53:07 +04:00
} } while ( 0 )
2006-04-24 18:20:44 +04:00
# define VAL_APPROX_EQUAL(n1, v1, n2, v2) do {if (abs((int)(s1->n1.out.v1) - (int)(s2->n2.out.v2)) > 0.1*s1->n1.out.v1) { \
2005-07-15 16:25:08 +04:00
printf ( " %s/%s [%u] != %s/%s [%u] at %s(%d) \n " , \
2010-01-05 20:42:54 +03:00
# n1, #v1, (unsigned int)s1->n1.out.v1, \
# n2, #v2, (unsigned int)s2->n2.out.v2, \
2005-07-15 16:25:08 +04:00
__FILE__ , __LINE__ ) ; \
2007-10-07 02:28:14 +04:00
ret = false ; \
2005-07-15 16:25:08 +04:00
} } while ( 0 )
2005-11-10 15:29:44 +03:00
# define STR_EQUAL(n1, v1, n2, v2) do { \
if ( strcmp_safe ( s1 - > n1 . out . v1 , s2 - > n2 . out . v2 ) ) { \
2003-08-13 05:53:07 +04:00
printf ( " %s/%s [%s] != %s/%s [%s] at %s(%d) \n " , \
# n1, #v1, s1->n1.out.v1, \
# n2, #v2, s2->n2.out.v2, \
__FILE__ , __LINE__ ) ; \
2007-10-07 02:28:14 +04:00
ret = false ; \
2003-08-13 05:53:07 +04:00
} } while ( 0 )
# define STRUCT_EQUAL(n1, v1, n2, v2) do {if (memcmp(&s1->n1.out.v1,&s2->n2.out.v2,sizeof(s1->n1.out.v1))) { \
printf ( " %s/%s != %s/%s at %s(%d) \n " , \
# n1, #v1, \
# n2, #v2, \
__FILE__ , __LINE__ ) ; \
2007-10-07 02:28:14 +04:00
ret = false ; \
2003-08-13 05:53:07 +04:00
} } while ( 0 )
/* used to find hints on unknown values - and to make sure
we zero - fill */
# define VAL_UNKNOWN(n1, v1) do {if (s1->n1.out.v1 != 0) { \
printf ( " %s/%s non-zero unknown - %u (0x%x) at %s(%d) \n " , \
# n1, #v1, \
2010-01-05 20:42:54 +03:00
( unsigned int ) s1 - > n1 . out . v1 , \
( unsigned int ) s1 - > n1 . out . v1 , \
2003-08-13 05:53:07 +04:00
__FILE__ , __LINE__ ) ; \
2007-10-07 02:28:14 +04:00
ret = false ; \
2003-08-13 05:53:07 +04:00
} } while ( 0 )
/* basic testing of all RAW_QFS_* calls
for each call we test that it succeeds , and where possible test
for consistency between the calls .
Some of the consistency tests assume that the target filesystem is
quiescent , which is sometimes hard to achieve
*/
2007-08-28 16:54:27 +04:00
bool torture_raw_qfsinfo ( struct torture_context * torture ,
2007-09-08 20:46:30 +04:00
struct smbcli_state * cli )
2003-08-13 05:53:07 +04:00
{
2017-12-07 19:47:15 +03:00
size_t i ;
2007-10-07 02:28:14 +04:00
bool ret = true ;
2017-12-07 19:47:15 +03:00
size_t count ;
2003-08-13 05:53:07 +04:00
union smb_fsinfo * s1 , * s2 ;
/* scan all the levels, pulling the results */
for ( i = 0 ; levels [ i ] . name ; i + + ) {
2008-02-29 02:58:47 +03:00
torture_comment ( torture , " Running level %s \n " , levels [ i ] . name ) ;
2003-08-13 05:53:07 +04:00
levels [ i ] . fsinfo . generic . level = levels [ i ] . level ;
2007-08-28 16:54:27 +04:00
levels [ i ] . status = smb_raw_fsinfo ( cli - > tree , torture , & levels [ i ] . fsinfo ) ;
2003-08-13 05:53:07 +04:00
}
/* check for completely broken levels */
for ( count = i = 0 ; levels [ i ] . name ; i + + ) {
2004-05-25 20:24:13 +04:00
uint32_t cap = cli - > transport - > negotiate . capabilities ;
2003-08-13 05:53:07 +04:00
/* see if this server claims to support this level */
if ( ( cap & levels [ i ] . capability_mask ) ! = levels [ i ] . capability_mask ) {
continue ;
}
if ( ! NT_STATUS_IS_OK ( levels [ i ] . status ) ) {
printf ( " ERROR: level %s failed - %s \n " ,
levels [ i ] . name , nt_errstr ( levels [ i ] . status ) ) ;
count + + ;
}
}
if ( count ! = 0 ) {
2017-12-07 19:47:15 +03:00
torture_comment ( torture , " %zu levels failed \n " , count ) ;
2008-02-29 02:58:47 +03:00
torture_assert ( torture , count > 13 , " too many level failures - giving up " ) ;
2003-08-13 05:53:07 +04:00
}
2008-02-29 02:58:47 +03:00
torture_comment ( torture , " check for correct aliases \n " ) ;
2003-08-13 05:53:07 +04:00
s1 = find ( " SIZE_INFO " ) ;
s2 = find ( " SIZE_INFORMATION " ) ;
if ( s1 & & s2 ) {
VAL_EQUAL ( size_info , total_alloc_units , size_info , total_alloc_units ) ;
2005-07-15 16:25:08 +04:00
VAL_APPROX_EQUAL ( size_info , avail_alloc_units , size_info , avail_alloc_units ) ;
2003-08-13 05:53:07 +04:00
VAL_EQUAL ( size_info , sectors_per_unit , size_info , sectors_per_unit ) ;
VAL_EQUAL ( size_info , bytes_per_sector , size_info , bytes_per_sector ) ;
}
s1 = find ( " DEVICE_INFO " ) ;
s2 = find ( " DEVICE_INFORMATION " ) ;
if ( s1 & & s2 ) {
VAL_EQUAL ( device_info , device_type , device_info , device_type ) ;
VAL_EQUAL ( device_info , characteristics , device_info , characteristics ) ;
}
s1 = find ( " VOLUME_INFO " ) ;
s2 = find ( " VOLUME_INFORMATION " ) ;
if ( s1 & & s2 ) {
STRUCT_EQUAL ( volume_info , create_time , volume_info , create_time ) ;
VAL_EQUAL ( volume_info , serial_number , volume_info , serial_number ) ;
STR_EQUAL ( volume_info , volume_name . s , volume_info , volume_name . s ) ;
2008-02-29 02:58:47 +03:00
torture_comment ( torture , " volume_info.volume_name = '%s' \n " , s1 - > volume_info . out . volume_name . s ) ;
2003-08-13 05:53:07 +04:00
}
s1 = find ( " ATTRIBUTE_INFO " ) ;
s2 = find ( " ATTRIBUTE_INFORMATION " ) ;
if ( s1 & & s2 ) {
VAL_EQUAL ( attribute_info , fs_attr ,
attribute_info , fs_attr ) ;
VAL_EQUAL ( attribute_info , max_file_component_length ,
attribute_info , max_file_component_length ) ;
STR_EQUAL ( attribute_info , fs_type . s , attribute_info , fs_type . s ) ;
2008-02-29 02:58:47 +03:00
torture_comment ( torture , " attribute_info.fs_type = '%s' \n " , s1 - > attribute_info . out . fs_type . s ) ;
2003-08-13 05:53:07 +04:00
}
2008-02-29 02:58:47 +03:00
torture_comment ( torture , " check for consistent disk sizes \n " ) ;
2003-08-13 05:53:07 +04:00
s1 = find ( " DSKATTR " ) ;
s2 = find ( " ALLOCATION " ) ;
if ( s1 & & s2 ) {
double size1 , size2 ;
double scale = s1 - > dskattr . out . blocks_per_unit * s1 - > dskattr . out . block_size ;
size1 = 1.0 *
s1 - > dskattr . out . units_total *
s1 - > dskattr . out . blocks_per_unit *
s1 - > dskattr . out . block_size / scale ;
size2 = 1.0 *
s2 - > allocation . out . sectors_per_unit *
s2 - > allocation . out . total_alloc_units *
s2 - > allocation . out . bytes_per_sector / scale ;
2017-11-19 20:11:28 +03:00
if ( fabs ( size1 - size2 ) > 1 ) {
2003-08-13 05:53:07 +04:00
printf ( " Inconsistent total size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f \n " ,
size1 , size2 ) ;
2007-10-07 02:28:14 +04:00
ret = false ;
2003-08-13 05:53:07 +04:00
}
2008-02-29 02:58:47 +03:00
torture_comment ( torture , " total disk = %.0f MB \n " , size1 * scale / 1.0e6 ) ;
2003-08-13 05:53:07 +04:00
}
2008-02-29 02:58:47 +03:00
torture_comment ( torture , " check consistent free disk space \n " ) ;
2003-08-13 05:53:07 +04:00
s1 = find ( " DSKATTR " ) ;
s2 = find ( " ALLOCATION " ) ;
if ( s1 & & s2 ) {
double size1 , size2 ;
double scale = s1 - > dskattr . out . blocks_per_unit * s1 - > dskattr . out . block_size ;
size1 = 1.0 *
s1 - > dskattr . out . units_free *
s1 - > dskattr . out . blocks_per_unit *
s1 - > dskattr . out . block_size / scale ;
size2 = 1.0 *
s2 - > allocation . out . sectors_per_unit *
s2 - > allocation . out . avail_alloc_units *
s2 - > allocation . out . bytes_per_sector / scale ;
2017-11-19 20:11:28 +03:00
if ( fabs ( size1 - size2 ) > 1 ) {
2003-08-13 05:53:07 +04:00
printf ( " Inconsistent avail size in DSKATTR and ALLOCATION - size1=%.0f size2=%.0f \n " ,
size1 , size2 ) ;
2007-10-07 02:28:14 +04:00
ret = false ;
2003-08-13 05:53:07 +04:00
}
2008-02-29 02:58:47 +03:00
torture_comment ( torture , " free disk = %.0f MB \n " , size1 * scale / 1.0e6 ) ;
2003-08-13 05:53:07 +04:00
}
2008-02-29 02:58:47 +03:00
torture_comment ( torture , " volume info consistency \n " ) ;
2003-08-13 05:53:07 +04:00
s1 = find ( " VOLUME " ) ;
s2 = find ( " VOLUME_INFO " ) ;
if ( s1 & & s2 ) {
VAL_EQUAL ( volume , serial_number , volume_info , serial_number ) ;
STR_EQUAL ( volume , volume_name . s , volume_info , volume_name . s ) ;
}
/* disk size consistency - notice that 'avail_alloc_units' maps to the caller
available allocation units , not the total */
s1 = find ( " SIZE_INFO " ) ;
s2 = find ( " FULL_SIZE_INFORMATION " ) ;
if ( s1 & & s2 ) {
VAL_EQUAL ( size_info , total_alloc_units , full_size_information , total_alloc_units ) ;
2005-07-15 16:25:08 +04:00
VAL_APPROX_EQUAL ( size_info , avail_alloc_units , full_size_information , call_avail_alloc_units ) ;
2003-08-13 05:53:07 +04:00
VAL_EQUAL ( size_info , sectors_per_unit , full_size_information , sectors_per_unit ) ;
VAL_EQUAL ( size_info , bytes_per_sector , full_size_information , bytes_per_sector ) ;
}
printf ( " check for non-zero unknown fields \n " ) ;
s1 = find ( " QUOTA_INFORMATION " ) ;
if ( s1 ) {
VAL_UNKNOWN ( quota_information , unknown [ 0 ] ) ;
VAL_UNKNOWN ( quota_information , unknown [ 1 ] ) ;
VAL_UNKNOWN ( quota_information , unknown [ 2 ] ) ;
}
s1 = find ( " OBJECTID_INFORMATION " ) ;
if ( s1 ) {
VAL_UNKNOWN ( objectid_information , unknown [ 0 ] ) ;
VAL_UNKNOWN ( objectid_information , unknown [ 1 ] ) ;
VAL_UNKNOWN ( objectid_information , unknown [ 2 ] ) ;
VAL_UNKNOWN ( objectid_information , unknown [ 3 ] ) ;
VAL_UNKNOWN ( objectid_information , unknown [ 4 ] ) ;
VAL_UNKNOWN ( objectid_information , unknown [ 5 ] ) ;
}
# define STR_CHECK(sname, stype, field, flags) do { \
s1 = find ( sname ) ; \
if ( s1 ) { \
2007-05-14 09:53:26 +04:00
if ( s1 - > stype . out . field . s & & wire_bad_flags ( & s1 - > stype . out . field , flags , cli - > transport ) ) { \
2003-08-13 05:53:07 +04:00
printf ( " (%d) incorrect string termination in %s/%s \n " , \
__LINE__ , # stype , # field ) ; \
2007-10-07 02:28:14 +04:00
ret = false ; \
2003-08-13 05:53:07 +04:00
} \
} } while ( 0 )
2008-02-29 02:58:47 +03:00
torture_comment ( torture , " check for correct termination \n " ) ;
2005-11-10 15:29:44 +03:00
2003-08-13 05:53:07 +04:00
STR_CHECK ( " VOLUME " , volume , volume_name , 0 ) ;
STR_CHECK ( " VOLUME_INFO " , volume_info , volume_name , STR_UNICODE ) ;
STR_CHECK ( " VOLUME_INFORMATION " , volume_info , volume_name , STR_UNICODE ) ;
STR_CHECK ( " ATTRIBUTE_INFO " , attribute_info , fs_type , STR_UNICODE ) ;
STR_CHECK ( " ATTRIBUTE_INFORMATION " , attribute_info , fs_type , STR_UNICODE ) ;
return ret ;
}