2000-04-25 18:04:06 +04:00
/*
Unix SMB / Netbios implementation .
Version 3.0
client directory list routines
Copyright ( C ) Andrew Tridgell 1994 - 1998
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 .
*/
# define NO_SYSLOG
# include "includes.h"
/****************************************************************************
interpret a long filename structure - this is mostly guesses at the moment
The length of the structure is returned
The structure of a long filename depends on the info level . 260 is used
by NT and 2 is used by OS / 2
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-02-20 15:22:30 +03:00
static int interpret_long_filename ( struct cli_state * cli ,
int level , char * p , file_info * finfo )
2000-04-25 18:04:06 +04:00
{
extern file_info def_finfo ;
2001-07-08 01:23:32 +04:00
file_info finfo2 ;
int len ;
char * base = p ;
2000-04-25 18:04:06 +04:00
2001-07-08 01:23:32 +04:00
if ( ! finfo ) finfo = & finfo2 ;
memcpy ( finfo , & def_finfo , sizeof ( * finfo ) ) ;
2000-04-25 18:04:06 +04:00
switch ( level )
{
case 1 : /* OS/2 understands this */
2001-07-08 01:23:32 +04:00
/* these dates are converted to GMT by
make_unix_date */
finfo - > ctime = make_unix_date2 ( p + 4 ) ;
finfo - > atime = make_unix_date2 ( p + 8 ) ;
finfo - > mtime = make_unix_date2 ( p + 12 ) ;
finfo - > size = IVAL ( p , 16 ) ;
finfo - > mode = CVAL ( p , 24 ) ;
len = CVAL ( p , 26 ) ;
p + = 27 ;
p + = clistr_align_in ( cli , p , 0 ) ;
p + = clistr_pull ( cli , finfo - > name , p ,
sizeof ( finfo - > name ) ,
len ,
STR_TERMINATE ) ;
return PTR_DIFF ( p , base ) ;
2000-04-25 18:04:06 +04:00
case 2 : /* this is what OS/2 uses mostly */
2001-07-08 01:23:32 +04:00
/* these dates are converted to GMT by
make_unix_date */
finfo - > ctime = make_unix_date2 ( p + 4 ) ;
finfo - > atime = make_unix_date2 ( p + 8 ) ;
finfo - > mtime = make_unix_date2 ( p + 12 ) ;
finfo - > size = IVAL ( p , 16 ) ;
finfo - > mode = CVAL ( p , 24 ) ;
len = CVAL ( p , 30 ) ;
p + = 31 ;
2001-08-24 08:53:39 +04:00
/* check for unisys! */
2001-07-08 01:23:32 +04:00
p + = clistr_pull ( cli , finfo - > name , p ,
sizeof ( finfo - > name ) ,
len ,
STR_NOALIGN ) ;
return PTR_DIFF ( p , base ) + 1 ;
2000-04-25 18:04:06 +04:00
case 260 : /* NT uses this, but also accepts 2 */
2001-07-08 01:23:32 +04:00
{
int namelen , slen ;
p + = 4 ; /* next entry offset */
p + = 4 ; /* fileindex */
2000-04-25 18:04:06 +04:00
2001-07-08 01:23:32 +04:00
/* these dates appear to arrive in a
weird way . It seems to be localtime
plus the serverzone given in the
initial connect . This is GMT when
DST is not in effect and one hour
from GMT otherwise . Can this really
be right ? ?
I suppose this could be called
kludge - GMT . Is is the GMT you get
by using the current DST setting on
a different localtime . It will be
cheap to calculate , I suppose , as
no DST tables will be needed */
finfo - > ctime = interpret_long_date ( p ) ; p + = 8 ;
finfo - > atime = interpret_long_date ( p ) ; p + = 8 ;
finfo - > mtime = interpret_long_date ( p ) ; p + = 8 ; p + = 8 ;
finfo - > size = IVAL ( p , 0 ) ; p + = 8 ;
p + = 8 ; /* alloc size */
finfo - > mode = CVAL ( p , 0 ) ; p + = 4 ;
namelen = IVAL ( p , 0 ) ; p + = 4 ;
p + = 4 ; /* EA size */
slen = SVAL ( p , 0 ) ;
p + = 2 ;
{
/* stupid NT bugs. grr */
int flags = 0 ;
if ( p [ 1 ] = = 0 & & namelen > 1 ) flags | = STR_UNICODE ;
clistr_pull ( cli , finfo - > short_name , p ,
sizeof ( finfo - > short_name ) ,
2001-08-24 08:53:39 +04:00
slen , flags ) ;
2000-04-25 18:04:06 +04:00
}
2001-07-08 01:23:32 +04:00
p + = 24 ; /* short name? */
clistr_pull ( cli , finfo - > name , p ,
sizeof ( finfo - > name ) ,
namelen , 0 ) ;
return SVAL ( base , 0 ) ;
}
2000-04-25 18:04:06 +04:00
}
DEBUG ( 1 , ( " Unknown long filename format %d \n " , level ) ) ;
return ( SVAL ( p , 0 ) ) ;
}
/****************************************************************************
do a directory listing , calling fn on each file found
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-02-26 08:10:44 +03:00
int cli_list_new ( struct cli_state * cli , const char * Mask , uint16 attribute ,
void ( * fn ) ( file_info * , const char * , void * ) , void * state )
2000-04-25 18:04:06 +04:00
{
int max_matches = 512 ;
2001-02-26 09:53:42 +03:00
int info_level ;
2000-04-25 18:04:06 +04:00
char * p , * p2 ;
pstring mask ;
file_info finfo ;
int i ;
2001-08-12 21:30:01 +04:00
char * tdl , * dirlist = NULL ;
2000-04-25 18:04:06 +04:00
int dirlist_len = 0 ;
int total_received = - 1 ;
BOOL First = True ;
int ff_searchcount = 0 ;
int ff_eos = 0 ;
int ff_lastname = 0 ;
int ff_dir_handle = 0 ;
int loop_count = 0 ;
char * rparam = NULL , * rdata = NULL ;
2000-04-30 16:34:26 +04:00
int param_len , data_len ;
2000-04-25 18:04:06 +04:00
uint16 setup ;
pstring param ;
2001-02-26 02:46:28 +03:00
2001-02-26 09:53:42 +03:00
/* NT uses 260, OS/2 uses 2. Both accept 1. */
info_level = ( cli - > capabilities & CAP_NT_SMBS ) ? 260 : 1 ;
2000-04-25 18:04:06 +04:00
pstrcpy ( mask , Mask ) ;
while ( ff_eos = = 0 ) {
loop_count + + ;
if ( loop_count > 200 ) {
DEBUG ( 0 , ( " Error: Looping in FIND_NEXT?? \n " ) ) ;
break ;
}
if ( First ) {
setup = TRANSACT2_FINDFIRST ;
SSVAL ( param , 0 , attribute ) ; /* attribute */
SSVAL ( param , 2 , max_matches ) ; /* max count */
SSVAL ( param , 4 , 4 + 2 ) ; /* resume required + close on end */
SSVAL ( param , 6 , info_level ) ;
SIVAL ( param , 8 , 0 ) ;
2001-03-14 15:42:43 +03:00
p = param + 12 ;
p + = clistr_push ( cli , param + 12 , mask , - 1 ,
2001-07-04 11:15:53 +04:00
STR_TERMINATE ) ;
2000-04-25 18:04:06 +04:00
} else {
setup = TRANSACT2_FINDNEXT ;
SSVAL ( param , 0 , ff_dir_handle ) ;
SSVAL ( param , 2 , max_matches ) ; /* max count */
SSVAL ( param , 4 , info_level ) ;
SIVAL ( param , 6 , 0 ) ; /* ff_resume_key */
SSVAL ( param , 10 , 8 + 4 + 2 ) ; /* continue + resume required + close on end */
2001-03-14 15:42:43 +03:00
p = param + 12 ;
p + = clistr_push ( cli , param + 12 , mask , - 1 ,
2001-07-04 11:15:53 +04:00
STR_TERMINATE ) ;
2000-04-25 18:04:06 +04:00
}
2001-03-14 15:42:43 +03:00
param_len = PTR_DIFF ( p , param ) ;
2000-04-25 18:04:06 +04:00
if ( ! cli_send_trans ( cli , SMBtrans2 ,
2001-02-20 13:11:40 +03:00
NULL , /* Name */
2000-04-25 18:04:06 +04:00
- 1 , 0 , /* fid, flags */
& setup , 1 , 0 , /* setup, length, max */
param , param_len , 10 , /* param, length, max */
NULL , 0 ,
cli - > max_xmit /* data, length, max */
) ) {
break ;
}
if ( ! cli_receive_trans ( cli , SMBtrans2 ,
& rparam , & param_len ,
2001-08-10 10:00:33 +04:00
& rdata , & data_len ) & &
cli_is_dos_error ( cli ) ) {
2000-04-25 18:04:06 +04:00
/* we need to work around a Win95 bug - sometimes
it gives ERRSRV / ERRerror temprarily */
uint8 eclass ;
uint32 ecode ;
2001-08-10 10:00:33 +04:00
cli_dos_error ( cli , & eclass , & ecode ) ;
2000-04-25 18:04:06 +04:00
if ( eclass ! = ERRSRV | | ecode ! = ERRerror ) break ;
msleep ( 100 ) ;
continue ;
}
2001-08-20 09:15:26 +04:00
if ( cli_is_error ( cli ) | | ! rdata | | ! rparam )
2001-08-22 06:47:38 +04:00
break ;
2001-08-13 03:53:26 +04:00
2000-04-25 18:04:06 +04:00
if ( total_received = = - 1 ) total_received = 0 ;
/* parse out some important return info */
p = rparam ;
if ( First ) {
ff_dir_handle = SVAL ( p , 0 ) ;
ff_searchcount = SVAL ( p , 2 ) ;
ff_eos = SVAL ( p , 4 ) ;
ff_lastname = SVAL ( p , 8 ) ;
} else {
ff_searchcount = SVAL ( p , 0 ) ;
ff_eos = SVAL ( p , 2 ) ;
ff_lastname = SVAL ( p , 6 ) ;
}
if ( ff_searchcount = = 0 )
break ;
/* point to the data bytes */
p = rdata ;
/* we might need the lastname for continuations */
if ( ff_lastname > 0 ) {
switch ( info_level )
{
case 260 :
2001-02-20 15:22:30 +03:00
clistr_pull ( cli , mask , p + ff_lastname ,
sizeof ( mask ) ,
data_len - ff_lastname ,
2001-07-04 11:15:53 +04:00
STR_TERMINATE ) ;
2000-04-25 18:04:06 +04:00
break ;
case 1 :
2001-02-20 15:22:30 +03:00
clistr_pull ( cli , mask , p + ff_lastname + 1 ,
sizeof ( mask ) ,
- 1 ,
2001-07-04 11:15:53 +04:00
STR_TERMINATE ) ;
2000-04-25 18:04:06 +04:00
break ;
}
} else {
pstrcpy ( mask , " " ) ;
}
/* and add them to the dirlist pool */
2001-08-12 21:30:01 +04:00
tdl = Realloc ( dirlist , dirlist_len + data_len ) ;
2000-04-25 18:04:06 +04:00
2001-08-12 21:30:01 +04:00
if ( ! tdl ) {
DEBUG ( 0 , ( " cli_list_new: Failed to expand dirlist \n " ) ) ;
2000-04-25 18:04:06 +04:00
break ;
}
2001-08-12 21:30:01 +04:00
else dirlist = tdl ;
2000-04-25 18:04:06 +04:00
/* put in a length for the last entry, to ensure we can chain entries
into the next packet */
for ( p2 = p , i = 0 ; i < ( ff_searchcount - 1 ) ; i + + )
2001-02-20 15:22:30 +03:00
p2 + = interpret_long_filename ( cli , info_level , p2 , NULL ) ;
2000-04-25 18:04:06 +04:00
SSVAL ( p2 , 0 , data_len - PTR_DIFF ( p2 , p ) ) ;
/* grab the data for later use */
memcpy ( dirlist + dirlist_len , p , data_len ) ;
dirlist_len + = data_len ;
total_received + = ff_searchcount ;
2001-09-17 07:33:37 +04:00
SAFE_FREE ( rdata ) ;
SAFE_FREE ( rparam ) ;
2000-04-25 18:04:06 +04:00
DEBUG ( 3 , ( " received %d entries (eos=%d) \n " ,
ff_searchcount , ff_eos ) ) ;
if ( ff_searchcount > 0 ) loop_count = 0 ;
First = False ;
}
for ( p = dirlist , i = 0 ; i < total_received ; i + + ) {
2001-02-20 15:22:30 +03:00
p + = interpret_long_filename ( cli , info_level , p , & finfo ) ;
2001-01-05 16:11:29 +03:00
fn ( & finfo , Mask , state ) ;
2000-04-25 18:04:06 +04:00
}
/* free up the dirlist buffer */
2001-09-17 07:33:37 +04:00
SAFE_FREE ( dirlist ) ;
2000-04-25 18:04:06 +04:00
return ( total_received ) ;
}
2000-04-30 16:34:26 +04:00
/****************************************************************************
interpret a short filename structure
The length of the structure is returned
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-03-16 06:25:13 +03:00
static int interpret_short_filename ( struct cli_state * cli , char * p , file_info * finfo )
2000-04-30 16:34:26 +04:00
{
extern file_info def_finfo ;
* finfo = def_finfo ;
finfo - > mode = CVAL ( p , 21 ) ;
/* this date is converted to GMT by make_unix_date */
finfo - > ctime = make_unix_date ( p + 22 ) ;
finfo - > mtime = finfo - > atime = finfo - > ctime ;
finfo - > size = IVAL ( p , 26 ) ;
2001-07-04 11:15:53 +04:00
clistr_pull ( cli , finfo - > name , p + 30 , sizeof ( finfo - > name ) , 12 , STR_ASCII ) ;
2000-04-30 16:34:26 +04:00
if ( strcmp ( finfo - > name , " .. " ) & & strcmp ( finfo - > name , " . " ) )
fstrcpy ( finfo - > short_name , finfo - > name ) ;
return ( DIR_STRUCT_SIZE ) ;
}
/****************************************************************************
do a directory listing , calling fn on each file found
this uses the old SMBsearch interface . It is needed for testing Samba ,
but should otherwise not be used
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int cli_list_old ( struct cli_state * cli , const char * Mask , uint16 attribute ,
2001-02-21 06:40:20 +03:00
void ( * fn ) ( file_info * , const char * , void * ) , void * state )
2000-04-30 16:34:26 +04:00
{
char * p ;
int received = 0 ;
BOOL first = True ;
char status [ 21 ] ;
int num_asked = ( cli - > max_xmit - 100 ) / DIR_STRUCT_SIZE ;
int num_received = 0 ;
int i ;
2001-08-12 21:30:01 +04:00
char * tdl , * dirlist = NULL ;
2000-04-30 16:34:26 +04:00
pstring mask ;
ZERO_ARRAY ( status ) ;
pstrcpy ( mask , Mask ) ;
while ( 1 ) {
memset ( cli - > outbuf , ' \0 ' , smb_size ) ;
memset ( cli - > inbuf , ' \0 ' , smb_size ) ;
2001-02-21 06:40:20 +03:00
set_message ( cli - > outbuf , 2 , 0 , True ) ;
2000-04-30 16:34:26 +04:00
2001-02-21 06:40:20 +03:00
CVAL ( cli - > outbuf , smb_com ) = SMBsearch ;
2000-04-30 16:34:26 +04:00
SSVAL ( cli - > outbuf , smb_tid , cli - > cnum ) ;
cli_setup_packet ( cli ) ;
SSVAL ( cli - > outbuf , smb_vwv0 , num_asked ) ;
SSVAL ( cli - > outbuf , smb_vwv1 , attribute ) ;
p = smb_buf ( cli - > outbuf ) ;
* p + + = 4 ;
2001-07-04 11:15:53 +04:00
p + = clistr_push ( cli , p , first ? mask : " " , - 1 , STR_TERMINATE ) ;
2000-04-30 16:34:26 +04:00
* p + + = 5 ;
if ( first ) {
SSVAL ( p , 0 , 0 ) ;
2001-02-21 06:40:20 +03:00
p + = 2 ;
2000-04-30 16:34:26 +04:00
} else {
SSVAL ( p , 0 , 21 ) ;
p + = 2 ;
memcpy ( p , status , 21 ) ;
2001-02-21 06:40:20 +03:00
p + = 21 ;
2000-04-30 16:34:26 +04:00
}
2001-02-21 06:40:20 +03:00
cli_setup_bcc ( cli , p ) ;
2000-04-30 16:34:26 +04:00
cli_send_smb ( cli ) ;
if ( ! cli_receive_smb ( cli ) ) break ;
received = SVAL ( cli - > inbuf , smb_vwv0 ) ;
if ( received < = 0 ) break ;
first = False ;
2001-08-12 21:30:01 +04:00
tdl = Realloc ( dirlist , ( num_received + received ) * DIR_STRUCT_SIZE ) ;
2000-04-30 16:34:26 +04:00
2001-08-12 21:30:01 +04:00
if ( ! tdl ) {
DEBUG ( 0 , ( " cli_list_old: failed to expand dirlist " ) ) ;
2001-09-17 07:33:37 +04:00
SAFE_FREE ( dirlist ) ;
2000-04-30 16:34:26 +04:00
return 0 ;
2001-08-12 21:30:01 +04:00
}
else dirlist = tdl ;
2000-04-30 16:34:26 +04:00
p = smb_buf ( cli - > inbuf ) + 3 ;
memcpy ( dirlist + num_received * DIR_STRUCT_SIZE ,
p , received * DIR_STRUCT_SIZE ) ;
memcpy ( status , p + ( ( received - 1 ) * DIR_STRUCT_SIZE ) , 21 ) ;
num_received + = received ;
2001-09-05 15:32:59 +04:00
if ( cli_is_error ( cli ) ) break ;
2000-04-30 16:34:26 +04:00
}
if ( ! first ) {
memset ( cli - > outbuf , ' \0 ' , smb_size ) ;
memset ( cli - > inbuf , ' \0 ' , smb_size ) ;
2001-02-21 06:40:20 +03:00
set_message ( cli - > outbuf , 2 , 0 , True ) ;
2000-04-30 16:34:26 +04:00
CVAL ( cli - > outbuf , smb_com ) = SMBfclose ;
SSVAL ( cli - > outbuf , smb_tid , cli - > cnum ) ;
cli_setup_packet ( cli ) ;
2000-04-30 18:26:59 +04:00
SSVAL ( cli - > outbuf , smb_vwv0 , 0 ) ; /* find count? */
SSVAL ( cli - > outbuf , smb_vwv1 , attribute ) ;
2000-04-30 16:34:26 +04:00
p = smb_buf ( cli - > outbuf ) ;
* p + + = 4 ;
2000-04-30 18:26:59 +04:00
fstrcpy ( p , " " ) ;
2000-04-30 16:34:26 +04:00
p + = strlen ( p ) + 1 ;
* p + + = 5 ;
2000-04-30 18:26:59 +04:00
SSVAL ( p , 0 , 21 ) ;
2000-04-30 16:34:26 +04:00
p + = 2 ;
memcpy ( p , status , 21 ) ;
2001-02-21 06:40:20 +03:00
p + = 21 ;
2000-04-30 16:34:26 +04:00
2001-02-21 06:40:20 +03:00
cli_setup_bcc ( cli , p ) ;
2000-04-30 16:34:26 +04:00
cli_send_smb ( cli ) ;
if ( ! cli_receive_smb ( cli ) ) {
2001-09-05 15:32:59 +04:00
DEBUG ( 0 , ( " Error closing search: %s \n " , cli_errstr ( cli ) ) ) ;
2000-04-30 16:34:26 +04:00
}
}
for ( p = dirlist , i = 0 ; i < num_received ; i + + ) {
file_info finfo ;
2001-03-16 06:25:13 +03:00
p + = interpret_short_filename ( cli , p , & finfo ) ;
2001-02-21 06:40:20 +03:00
fn ( & finfo , Mask , state ) ;
2000-04-30 16:34:26 +04:00
}
2001-09-17 07:33:37 +04:00
SAFE_FREE ( dirlist ) ;
2000-04-30 16:34:26 +04:00
return ( num_received ) ;
}
2001-02-26 08:10:44 +03:00
/****************************************************************************
do a directory listing , calling fn on each file found
this auto - switches between old and new style
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int cli_list ( struct cli_state * cli , const char * Mask , uint16 attribute ,
void ( * fn ) ( file_info * , const char * , void * ) , void * state )
{
if ( cli - > protocol < = PROTOCOL_LANMAN1 ) {
return cli_list_old ( cli , Mask , attribute , fn , state ) ;
}
return cli_list_new ( cli , Mask , attribute , fn , state ) ;
}