/*
Unix SMB / Netbios implementation .
Version 2.0
SMB wrapper directory functions
Copyright ( C ) Andrew Tridgell 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 .
*/
# include "includes.h"
# include "realcalls.h"
extern pstring smbw_cwd ;
extern fstring smbw_prefix ;
static struct smbw_dir * smbw_dirs ;
extern struct bitmap * smbw_file_bmap ;
extern int DEBUGLEVEL ;
extern int smbw_busy ;
/*****************************************************
map a fd to a smbw_dir structure
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
struct smbw_dir * smbw_dir ( int fd )
{
struct smbw_dir * dir ;
for ( dir = smbw_dirs ; dir ; dir = dir - > next ) {
if ( dir - > fd = = fd ) return dir ;
}
return NULL ;
}
/*****************************************************
check if a DIR * is one of ours
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int smbw_dirp ( DIR * dirp )
{
struct smbw_dir * d = ( struct smbw_dir * ) dirp ;
struct smbw_dir * dir ;
for ( dir = smbw_dirs ; dir ; dir = dir - > next ) {
if ( dir = = d ) return 1 ;
}
return 0 ;
}
/*****************************************************
free a smbw_dir structure and all entries
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void free_dir ( struct smbw_dir * dir )
{
if ( dir - > list ) {
free ( dir - > list ) ;
}
if ( dir - > path ) free ( dir - > path ) ;
ZERO_STRUCTP ( dir ) ;
free ( dir ) ;
}
static struct smbw_dir * cur_dir ;
/*****************************************************
add a entry to a directory listing
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void smbw_dir_add ( struct file_info * finfo )
{
DEBUG ( 5 , ( " %s \n " , finfo - > name ) ) ;
if ( cur_dir - > malloced = = cur_dir - > count ) {
cur_dir - > list = ( struct file_info * ) Realloc ( cur_dir - > list ,
sizeof ( cur_dir - > list [ 0 ] ) *
( cur_dir - > count + 100 ) ) ;
if ( ! cur_dir - > list ) {
/* oops */
return ;
}
cur_dir - > malloced + = 100 ;
}
cur_dir - > list [ cur_dir - > count ] = * finfo ;
cur_dir - > count + + ;
}
/*****************************************************
add a entry to a directory listing
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void smbw_share_add ( const char * share , uint32 type , const char * comment )
{
struct file_info finfo ;
if ( strcmp ( share , " IPC$ " ) = = 0 ) return ;
ZERO_STRUCT ( finfo ) ;
pstrcpy ( finfo . name , share ) ;
finfo . mode = aRONLY | aDIR ;
smbw_dir_add ( & finfo ) ;
}
/*****************************************************
add a server to a directory listing
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void smbw_server_add ( const char * name , uint32 type ,
const char * comment )
{
struct file_info finfo ;
ZERO_STRUCT ( finfo ) ;
pstrcpy ( finfo . name , name ) ;
finfo . mode = aRONLY | aDIR ;
smbw_dir_add ( & finfo ) ;
}
/*****************************************************
add a entry to a directory listing
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void smbw_printjob_add ( struct print_job_info * job )
{
struct file_info finfo ;
ZERO_STRUCT ( finfo ) ;
pstrcpy ( finfo . name , job - > name ) ;
finfo . mode = aRONLY | aDIR ;
finfo . mtime = job - > t ;
finfo . atime = job - > t ;
finfo . ctime = job - > t ;
finfo . uid = nametouid ( job - > user ) ;
finfo . mode = aRONLY ;
finfo . size = job - > size ;
smbw_dir_add ( & finfo ) ;
}
/*****************************************************
open a directory on the server
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int smbw_dir_open ( const char * fname )
{
fstring server , share ;
pstring path ;
struct smbw_server * srv = NULL ;
struct smbw_dir * dir = NULL ;
pstring mask ;
int fd ;
char * s , * p ;
if ( ! fname ) {
errno = EINVAL ;
return - 1 ;
}
smbw_init ( ) ;
/* work out what server they are after */
s = smbw_parse_path ( fname , server , share , path ) ;
DEBUG ( 4 , ( " dir_open share=%s \n " , share ) ) ;
/* get a connection to the server */
srv = smbw_server ( server , share ) ;
if ( ! srv ) {
/* smbw_server sets errno */
goto failed ;
}
dir = ( struct smbw_dir * ) malloc ( sizeof ( * dir ) ) ;
if ( ! dir ) {
errno = ENOMEM ;
goto failed ;
}
ZERO_STRUCTP ( dir ) ;
cur_dir = dir ;
slprintf ( mask , sizeof ( mask ) - 1 , " %s \\ * " , path ) ;
string_sub ( mask , " \\ \\ " , " \\ " ) ;
if ( ( p = strstr ( srv - > server_name , " #1D " ) ) ) {
DEBUG ( 4 , ( " doing NetServerEnum \n " ) ) ;
* p = 0 ;
cli_NetServerEnum ( & srv - > cli , srv - > server_name , SV_TYPE_ALL ,
smbw_server_add ) ;
* p = ' # ' ;
} else if ( strcmp ( srv - > cli . dev , " IPC " ) = = 0 ) {
DEBUG ( 4 , ( " doing NetShareEnum \n " ) ) ;
if ( cli_RNetShareEnum ( & srv - > cli , smbw_share_add ) < 0 ) {
errno = smbw_errno ( & srv - > cli ) ;
goto failed ;
}
} else if ( strncmp ( srv - > cli . dev , " LPT " , 3 ) = = 0 ) {
if ( cli_print_queue ( & srv - > cli , smbw_printjob_add ) < 0 ) {
errno = smbw_errno ( & srv - > cli ) ;
goto failed ;
}
} else {
if ( cli_list ( & srv - > cli , mask , aHIDDEN | aSYSTEM | aDIR ,
smbw_dir_add ) < 0 ) {
errno = smbw_errno ( & srv - > cli ) ;
goto failed ;
}
}
cur_dir = NULL ;
fd = open ( SMBW_DUMMY , O_WRONLY ) ;
if ( fd = = - 1 ) {
errno = EMFILE ;
goto failed ;
}
if ( bitmap_query ( smbw_file_bmap , fd ) ) {
DEBUG ( 0 , ( " ERROR: fd used in smbw_dir_open \n " ) ) ;
errno = EIO ;
goto failed ;
}
DLIST_ADD ( smbw_dirs , dir ) ;
bitmap_set ( smbw_file_bmap , fd ) ;
dir - > fd = fd ;
dir - > srv = srv ;
dir - > path = strdup ( s ) ;
DEBUG ( 4 , ( " -> %d \n " , dir - > count ) ) ;
return dir - > fd ;
failed :
if ( dir ) {
free_dir ( dir ) ;
}
return - 1 ;
}
/*****************************************************
a wrapper for fstat ( ) on a directory
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int smbw_dir_fstat ( int fd , struct stat * st )
{
struct smbw_dir * dir ;
dir = smbw_dir ( fd ) ;
if ( ! dir ) {
errno = EBADF ;
return - 1 ;
}
ZERO_STRUCTP ( st ) ;
smbw_setup_stat ( st , " " , dir - > count * DIRP_SIZE , aDIR ) ;
st - > st_dev = dir - > srv - > dev ;
return 0 ;
}
/*****************************************************
close a directory handle
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int smbw_dir_close ( int fd )
{
struct smbw_dir * dir ;
dir = smbw_dir ( fd ) ;
if ( ! dir ) {
errno = EBADF ;
return - 1 ;
}
bitmap_clear ( smbw_file_bmap , dir - > fd ) ;
close ( dir - > fd ) ;
DLIST_REMOVE ( smbw_dirs , dir ) ;
free_dir ( dir ) ;
return 0 ;
}
/*****************************************************
a wrapper for getdents ( )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int smbw_getdents ( unsigned int fd , struct dirent * dirp , int count )
{
struct smbw_dir * dir ;
int n = 0 ;
smbw_busy + + ;
dir = smbw_dir ( fd ) ;
if ( ! dir ) {
errno = EBADF ;
smbw_busy - - ;
return - 1 ;
}
while ( count > = DIRP_SIZE & & ( dir - > offset < dir - > count ) ) {
# if HAVE_DIRENT_D_OFF
dirp - > d_off = ( dir - > offset + 1 ) * DIRP_SIZE ;
# endif
dirp - > d_reclen = DIRP_SIZE ;
fstrcpy ( & dirp - > d_name [ 0 ] , dir - > list [ dir - > offset ] . name ) ;
dirp - > d_ino = smbw_inode ( dir - > list [ dir - > offset ] . name ) ;
dir - > offset + + ;
count - = dirp - > d_reclen ;
# if HAVE_DIRENT_D_OFF
if ( dir - > offset = = dir - > count ) {
dirp - > d_off = - 1 ;
}
# endif
dirp = ( struct dirent * ) ( ( ( char * ) dirp ) + DIRP_SIZE ) ;
n + + ;
}
smbw_busy - - ;
return n * DIRP_SIZE ;
}
/*****************************************************
a wrapper for chdir ( )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int smbw_chdir ( const char * name )
{
struct smbw_server * srv ;
fstring server , share ;
pstring path ;
uint32 mode = aDIR ;
char * cwd ;
int len ;
smbw_init ( ) ;
len = strlen ( smbw_prefix ) ;
if ( smbw_busy ) return real_chdir ( name ) ;
smbw_busy + + ;
if ( ! name ) {
errno = EINVAL ;
goto failed ;
}
DEBUG ( 4 , ( " smbw_chdir(%s) \n " , name ) ) ;
/* work out what server they are after */
cwd = smbw_parse_path ( name , server , share , path ) ;
/* a special case - accept cd to /smb */
if ( strncmp ( cwd , smbw_prefix , len - 1 ) = = 0 & &
cwd [ len - 1 ] = = 0 ) {
goto success1 ;
}
if ( strncmp ( cwd , smbw_prefix , strlen ( smbw_prefix ) ) ) {
if ( real_chdir ( cwd ) = = 0 ) {
goto success2 ;
}
goto failed ;
}
/* get a connection to the server */
srv = smbw_server ( server , share ) ;
if ( ! srv ) {
/* smbw_server sets errno */
goto failed ;
}
if ( strncmp ( srv - > cli . dev , " IPC " , 3 ) & &
strncmp ( srv - > cli . dev , " LPT " , 3 ) & &
! smbw_getatr ( srv , path ,
& mode , NULL , NULL , NULL , NULL ) ) {
errno = smbw_errno ( & srv - > cli ) ;
goto failed ;
}
if ( ! ( mode & aDIR ) ) {
errno = ENOTDIR ;
goto failed ;
}
success1 :
/* we don't want the old directory to be busy */
real_chdir ( " / " ) ;
success2 :
DEBUG ( 4 , ( " set SMBW_CWD to %s \n " , cwd ) ) ;
pstrcpy ( smbw_cwd , cwd ) ;
if ( setenv ( SMBW_PWD_ENV , smbw_cwd , 1 ) ) {
DEBUG ( 4 , ( " setenv failed \n " ) ) ;
}
smbw_busy - - ;
return 0 ;
failed :
smbw_busy - - ;
return - 1 ;
}
/*****************************************************
a wrapper for lseek ( ) on directories
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
off_t smbw_dir_lseek ( int fd , off_t offset , int whence )
{
struct smbw_dir * dir ;
off_t ret ;
dir = smbw_dir ( fd ) ;
if ( ! dir ) {
errno = EBADF ;
return - 1 ;
}
switch ( whence ) {
case SEEK_SET :
dir - > offset = offset / DIRP_SIZE ;
break ;
case SEEK_CUR :
dir - > offset + = offset / DIRP_SIZE ;
break ;
case SEEK_END :
dir - > offset = ( dir - > count * DIRP_SIZE ) + offset ;
dir - > offset / = DIRP_SIZE ;
break ;
}
ret = dir - > offset * DIRP_SIZE ;
DEBUG ( 4 , ( " -> %d \n " , ( int ) ret ) ) ;
return ret ;
}
/*****************************************************
a wrapper for mkdir ( )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int smbw_mkdir ( const char * fname , mode_t mode )
{
struct smbw_server * srv ;
fstring server , share ;
pstring path ;
if ( ! fname ) {
errno = EINVAL ;
return - 1 ;
}
smbw_init ( ) ;
smbw_busy + + ;
/* work out what server they are after */
smbw_parse_path ( fname , server , share , path ) ;
/* get a connection to the server */
srv = smbw_server ( server , share ) ;
if ( ! srv ) {
/* smbw_server sets errno */
goto failed ;
}
if ( ! cli_mkdir ( & srv - > cli , path ) ) {
errno = smbw_errno ( & srv - > cli ) ;
goto failed ;
}
smbw_busy - - ;
return 0 ;
failed :
smbw_busy - - ;
return - 1 ;
}
/*****************************************************
a wrapper for rmdir ( )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int smbw_rmdir ( const char * fname )
{
struct smbw_server * srv ;
fstring server , share ;
pstring path ;
if ( ! fname ) {
errno = EINVAL ;
return - 1 ;
}
smbw_init ( ) ;
smbw_busy + + ;
/* work out what server they are after */
smbw_parse_path ( fname , server , share , path ) ;
/* get a connection to the server */
srv = smbw_server ( server , share ) ;
if ( ! srv ) {
/* smbw_server sets errno */
goto failed ;
}
if ( ! cli_rmdir ( & srv - > cli , path ) ) {
errno = smbw_errno ( & srv - > cli ) ;
goto failed ;
}
smbw_busy - - ;
return 0 ;
failed :
smbw_busy - - ;
return - 1 ;
}
/*****************************************************
a wrapper for getcwd ( )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
char * smbw_getcwd ( char * buf , size_t size )
{
smbw_init ( ) ;
if ( smbw_busy ) {
return ( char * ) real_getcwd ( buf , size ) ;
}
smbw_busy + + ;
if ( ! buf ) {
if ( size < = 0 ) size = strlen ( smbw_cwd ) + 1 ;
buf = ( char * ) malloc ( size ) ;
if ( ! buf ) {
errno = ENOMEM ;
smbw_busy - - ;
return NULL ;
}
}
if ( strlen ( smbw_cwd ) > size - 1 ) {
errno = ERANGE ;
smbw_busy - - ;
return NULL ;
}
safe_strcpy ( buf , smbw_cwd , size ) ;
smbw_busy - - ;
return buf ;
}
/*****************************************************
a wrapper for fchdir ( )
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int smbw_fchdir ( unsigned int fd )
{
struct smbw_dir * dir ;
smbw_busy + + ;
dir = smbw_dir ( fd ) ;
if ( ! dir ) {
errno = EBADF ;
smbw_busy - - ;
return - 1 ;
}
smbw_busy - - ;
return chdir ( dir - > path ) ;
}
/*****************************************************
open a directory on the server
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
DIR * smbw_opendir ( const char * fname )
{
int fd ;
smbw_busy + + ;
fd = smbw_dir_open ( fname ) ;
if ( fd = = - 1 ) {
smbw_busy - - ;
return NULL ;
}
smbw_busy - - ;
return ( DIR * ) smbw_dir ( fd ) ;
}
/*****************************************************
read one entry from a directory
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
struct dirent * smbw_readdir ( DIR * dirp )
{
struct smbw_dir * d = ( struct smbw_dir * ) dirp ;
static union {
char buf [ DIRP_SIZE ] ;
struct dirent de ;
} dbuf ;
if ( smbw_getdents ( d - > fd , & dbuf . de , DIRP_SIZE ) > 0 )
return & dbuf . de ;
return NULL ;
}
/*****************************************************
close a DIR *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int smbw_closedir ( DIR * dirp )
{
struct smbw_dir * d = ( struct smbw_dir * ) dirp ;
return smbw_close ( d - > fd ) ;
}
/*****************************************************
seek in a directory
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void smbw_seekdir ( DIR * dirp , off_t offset )
{
struct smbw_dir * d = ( struct smbw_dir * ) dirp ;
smbw_dir_lseek ( d - > fd , offset , SEEK_SET ) ;
}
/*****************************************************
current loc in a directory
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
off_t smbw_telldir ( DIR * dirp )
{
struct smbw_dir * d = ( struct smbw_dir * ) dirp ;
return smbw_dir_lseek ( d - > fd , 0 , SEEK_CUR ) ;
}