2000-05-10 10:41:59 +00:00
# define OLD_NTDOMAIN 1
1998-08-17 07:40:06 +00:00
/*
Unix SMB / Netbios implementation .
Version 1.9 .
filename handling routines
Copyright ( C ) Andrew Tridgell 1992 - 1998
2000-01-26 21:25:35 +00:00
Copyright ( C ) Jeremy Allison 1999 - 200
Copyright ( C ) Ying Chen 2000
1998-08-17 07:40:06 +00:00
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 .
*/
2000-01-26 21:25:35 +00:00
/*
* New hash table stat cache code added by Ying Chen .
*/
1998-08-17 07:40:06 +00:00
# include "includes.h"
extern int DEBUGLEVEL ;
extern BOOL case_sensitive ;
extern BOOL case_preserve ;
extern BOOL short_case_preserve ;
extern fstring remote_machine ;
extern BOOL use_mangled_map ;
1998-09-05 05:07:05 +00:00
static BOOL scan_directory ( char * path , char * name , connection_struct * conn , BOOL docache ) ;
1998-08-17 07:40:06 +00:00
/****************************************************************************
1998-08-27 20:38:53 +00:00
Check if two filenames are equal .
This needs to be careful about whether we are case sensitive .
1998-08-17 07:40:06 +00:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-09-05 05:07:05 +00:00
static BOOL fname_equal ( char * name1 , char * name2 )
1998-08-17 07:40:06 +00:00
{
int l1 = strlen ( name1 ) ;
int l2 = strlen ( name2 ) ;
/* handle filenames ending in a single dot */
if ( l1 - l2 = = 1 & & name1 [ l1 - 1 ] = = ' . ' & & lp_strip_dot ( ) )
{
BOOL ret ;
name1 [ l1 - 1 ] = 0 ;
ret = fname_equal ( name1 , name2 ) ;
name1 [ l1 - 1 ] = ' . ' ;
return ( ret ) ;
}
if ( l2 - l1 = = 1 & & name2 [ l2 - 1 ] = = ' . ' & & lp_strip_dot ( ) )
{
BOOL ret ;
name2 [ l2 - 1 ] = 0 ;
ret = fname_equal ( name1 , name2 ) ;
name2 [ l2 - 1 ] = ' . ' ;
return ( ret ) ;
}
/* now normal filename handling */
if ( case_sensitive )
return ( strcmp ( name1 , name2 ) = = 0 ) ;
return ( strequal ( name1 , name2 ) ) ;
}
/****************************************************************************
1998-08-27 20:38:53 +00:00
Mangle the 2 nd name and check if it is then equal to the first name .
1998-08-17 07:40:06 +00:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-09-05 05:07:05 +00:00
static BOOL mangled_equal ( char * name1 , char * name2 )
1998-08-17 07:40:06 +00:00
{
pstring tmpname ;
if ( is_8_3 ( name2 , True ) )
return ( False ) ;
pstrcpy ( tmpname , name2 ) ;
1998-09-03 03:14:31 +00:00
mangle_name_83 ( tmpname ) ;
1998-08-17 07:40:06 +00:00
return ( strequal ( name1 , tmpname ) ) ;
}
1998-08-27 20:38:53 +00:00
1998-08-17 07:40:06 +00:00
/****************************************************************************
This routine is called to convert names from the dos namespace to unix
namespace . It needs to handle any case conversions , mangling , format
changes etc .
We assume that we have already done a chdir ( ) to the right " root " directory
for this service .
The function will return False if some part of the name except for the last
part cannot be resolved
If the saved_last_component ! = 0 , then the unmodified last component
of the pathname is returned there . This is used in an exceptional
case in reply_mv ( so far ) . If saved_last_component = = 0 then nothing
is returned there .
The bad_path arg is set to True if the filename walk failed . This is
used to pick the correct error code to return between ENOENT and ENOTDIR
as Windows applications depend on ERRbadpath being returned if a component
of a pathname does not exist .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1999-12-13 13:27:58 +00:00
BOOL unix_convert ( char * name , connection_struct * conn , char * saved_last_component ,
BOOL * bad_path , SMB_STRUCT_STAT * pst )
1998-08-17 07:40:06 +00:00
{
1998-09-01 20:11:54 +00:00
SMB_STRUCT_STAT st ;
1998-09-28 21:43:48 +00:00
char * start , * end ;
1998-08-17 07:40:06 +00:00
pstring dirpath ;
1998-08-27 20:38:53 +00:00
pstring orig_path ;
BOOL component_was_mangled = False ;
BOOL name_has_wildcard = False ;
1998-09-08 19:21:04 +00:00
#if 0
/* Andrew's conservative code... JRA. */
1998-09-05 15:31:10 +00:00
extern char magic_char ;
1998-09-08 19:21:04 +00:00
# endif
1998-08-17 07:40:06 +00:00
2000-04-10 13:05:23 +00:00
if ( conn - > printer ) {
/* we don't ever use the filenames on a printer share as a
filename - so don ' t convert them */
return True ;
}
1998-10-09 06:47:22 +00:00
DEBUG ( 5 , ( " unix_convert called on file \" %s \" \n " , name ) ) ;
1998-08-17 07:40:06 +00:00
* dirpath = 0 ;
* bad_path = False ;
1998-09-05 13:24:20 +00:00
if ( pst ) {
2000-01-26 21:25:35 +00:00
ZERO_STRUCTP ( pst ) ;
1998-09-05 13:24:20 +00:00
}
1998-08-17 07:40:06 +00:00
if ( saved_last_component )
* saved_last_component = 0 ;
1998-08-19 01:49:34 +00:00
/*
* Convert to basic unix format - removing \ chars and cleaning it up .
*/
1998-08-17 07:40:06 +00:00
unix_format ( name ) ;
unix_clean_name ( name ) ;
1998-08-19 01:49:34 +00:00
/*
* Names must be relative to the root of the service - trim any leading / .
* also trim trailing / ' s .
*/
1998-08-17 07:40:06 +00:00
trim_string ( name , " / " , " / " ) ;
1998-10-09 10:03:19 +00:00
/*
* If we trimmed down to a single ' \0 ' character
* then we should use the " . " directory to avoid
* searching the cache , but not if we are in a
* printing share .
*/
if ( ! * name & & ( ! conn - > printer ) ) {
name [ 0 ] = ' . ' ;
name [ 1 ] = ' \0 ' ;
}
1998-08-17 07:40:06 +00:00
/*
* Ensure saved_last_component is valid even if file exists .
*/
1998-08-19 01:49:34 +00:00
1998-08-17 07:40:06 +00:00
if ( saved_last_component ) {
end = strrchr ( name , ' / ' ) ;
if ( end )
pstrcpy ( saved_last_component , end + 1 ) ;
else
pstrcpy ( saved_last_component , name ) ;
}
if ( ! case_sensitive & &
( ! case_preserve | | ( is_8_3 ( name , False ) & & ! short_case_preserve ) ) )
strnorm ( name ) ;
1998-09-28 21:43:48 +00:00
/*
* If we trimmed down to a single ' \0 ' character
* then we will be using the " . " directory .
* As we know this is valid we can return true here .
*/
if ( ! * name )
return ( True ) ;
1998-08-27 20:38:53 +00:00
start = name ;
while ( strncmp ( start , " ./ " , 2 ) = = 0 )
start + = 2 ;
pstrcpy ( orig_path , name ) ;
2000-02-03 05:17:25 +00:00
if ( stat_cache_lookup ( conn , name , dirpath , & start , & st ) ) {
1998-08-27 20:38:53 +00:00
if ( pst )
* pst = st ;
return True ;
}
1998-08-19 01:49:34 +00:00
/*
* stat the name - if it exists then we are all done !
*/
1998-08-17 07:40:06 +00:00
2000-02-03 23:08:24 +00:00
if ( conn - > vfs_ops . stat ( dos_to_unix ( name , False ) , & st ) = = 0 ) {
1998-08-27 20:38:53 +00:00
stat_cache_add ( orig_path , name ) ;
DEBUG ( 5 , ( " conversion finished %s -> %s \n " , orig_path , name ) ) ;
if ( pst )
* pst = st ;
1998-08-17 07:40:06 +00:00
return ( True ) ;
1998-08-27 20:38:53 +00:00
}
1998-08-17 07:40:06 +00:00
1998-08-27 20:38:53 +00:00
DEBUG ( 5 , ( " unix_convert begin: name = %s, dirpath = %s, start = %s \n " ,
name , dirpath , start ) ) ;
1998-08-17 07:40:06 +00:00
1998-08-19 01:49:34 +00:00
/*
* A special case - if we don ' t have any mangling chars and are case
* sensitive then searching won ' t help .
*/
1998-08-17 07:40:06 +00:00
if ( case_sensitive & & ! is_mangled ( name ) & &
1999-12-13 13:27:58 +00:00
! lp_strip_dot ( ) & & ! use_mangled_map )
1998-08-17 07:40:06 +00:00
return ( False ) ;
2000-04-30 11:04:28 +00:00
name_has_wildcard = ms_has_wild ( start ) ;
1998-08-27 20:38:53 +00:00
1998-09-08 19:21:04 +00:00
/*
* is_mangled ( ) was changed to look at an entire pathname , not
* just a component . JRA .
*/
if ( is_mangled ( start ) )
component_was_mangled = True ;
#if 0
/* Keep Andrew's conservative code around, just in case. JRA. */
1998-09-05 15:31:10 +00:00
/* this is an extremely conservative test for mangled names. */
if ( strchr ( start , magic_char ) )
component_was_mangled = True ;
1998-09-08 19:21:04 +00:00
# endif
1998-09-05 15:31:10 +00:00
1998-08-19 01:49:34 +00:00
/*
* Now we need to recursively match the name against the real
* directory structure .
*/
1998-08-17 07:40:06 +00:00
1998-08-19 01:49:34 +00:00
/*
* Match each part of the path name separately , trying the names
* as is first , then trying to scan the directory for matching names .
*/
1998-09-28 21:43:48 +00:00
for ( ; start ; start = ( end ? end + 1 : ( char * ) NULL ) ) {
1998-08-19 01:49:34 +00:00
/*
* Pinpoint the end of this section of the filename .
*/
1998-08-17 07:40:06 +00:00
end = strchr ( start , ' / ' ) ;
1998-08-19 01:49:34 +00:00
/*
* Chop the name at this point .
*/
if ( end )
* end = 0 ;
1998-08-17 07:40:06 +00:00
if ( saved_last_component ! = 0 )
pstrcpy ( saved_last_component , end ? end + 1 : start ) ;
1998-08-19 01:49:34 +00:00
/*
* Check if the name exists up to this point .
*/
2000-02-03 05:17:25 +00:00
2000-02-03 23:08:24 +00:00
if ( conn - > vfs_ops . stat ( dos_to_unix ( name , False ) , & st ) = = 0 ) {
1998-08-19 01:49:34 +00:00
/*
* It exists . it must either be a directory or this must be
* the last part of the path for it to be OK .
*/
if ( end & & ! ( st . st_mode & S_IFDIR ) ) {
/*
* An intermediate part of the name isn ' t a directory .
*/
DEBUG ( 5 , ( " Not a dir %s \n " , start ) ) ;
* end = ' / ' ;
return ( False ) ;
}
1998-08-27 20:38:53 +00:00
1998-08-19 01:49:34 +00:00
} else {
pstring rest ;
* rest = 0 ;
/*
* Remember the rest of the pathname so it can be restored
* later .
*/
if ( end )
pstrcpy ( rest , end + 1 ) ;
/*
* Try to find this part of the path in the directory .
*/
1998-08-27 20:38:53 +00:00
2000-04-30 11:04:28 +00:00
if ( ms_has_wild ( start ) | |
1998-08-19 01:49:34 +00:00
! scan_directory ( dirpath , start , conn , end ? True : False ) ) {
if ( end ) {
/*
* An intermediate part of the name can ' t be found .
*/
DEBUG ( 5 , ( " Intermediate not found %s \n " , start ) ) ;
* end = ' / ' ;
/*
* We need to return the fact that the intermediate
* name resolution failed . This is used to return an
* error of ERRbadpath rather than ERRbadfile . Some
* Windows applications depend on the difference between
* these two errors .
*/
* bad_path = True ;
return ( False ) ;
}
1998-08-17 07:40:06 +00:00
1998-08-19 01:49:34 +00:00
/*
* Just the last part of the name doesn ' t exist .
* We may need to strupper ( ) or strlower ( ) it in case
* this conversion is being used for file creation
* purposes . If the filename is of mixed case then
* don ' t normalise it .
*/
if ( ! case_preserve & & ( ! strhasupper ( start ) | | ! strhaslower ( start ) ) )
strnorm ( start ) ;
/*
* check on the mangled stack to see if we can recover the
* base of the filename .
*/
1998-08-27 20:38:53 +00:00
if ( is_mangled ( start ) ) {
1998-08-19 01:49:34 +00:00
check_mangled_cache ( start ) ;
1998-08-27 20:38:53 +00:00
}
1998-08-19 01:49:34 +00:00
DEBUG ( 5 , ( " New file %s \n " , start ) ) ;
return ( True ) ;
}
/*
* Restore the rest of the string .
*/
if ( end ) {
pstrcpy ( start + strlen ( start ) + 1 , rest ) ;
end = start + strlen ( start ) ;
}
} /* end else */
/*
* Add to the dirpath that we have resolved so far .
*/
1998-08-27 20:38:53 +00:00
if ( * dirpath )
pstrcat ( dirpath , " / " ) ;
pstrcat ( dirpath , start ) ;
/*
* Don ' t cache a name with mangled or wildcard components
* as this can change the size .
*/
if ( ! component_was_mangled & & ! name_has_wildcard )
stat_cache_add ( orig_path , dirpath ) ;
1998-08-17 07:40:06 +00:00
1998-08-19 01:49:34 +00:00
/*
* Restore the / that we wiped out earlier .
*/
if ( end )
* end = ' / ' ;
}
1998-08-17 07:40:06 +00:00
1998-08-27 20:38:53 +00:00
/*
* Don ' t cache a name with mangled or wildcard components
* as this can change the size .
*/
if ( ! component_was_mangled & & ! name_has_wildcard )
stat_cache_add ( orig_path , name ) ;
1998-08-19 01:49:34 +00:00
/*
* The name has been resolved .
*/
1998-08-27 20:38:53 +00:00
DEBUG ( 5 , ( " conversion finished %s -> %s \n " , orig_path , name ) ) ;
1998-08-17 07:40:06 +00:00
return ( True ) ;
}
/****************************************************************************
check a filename - possibly caling reducename
This is called by every routine before it allows an operation on a filename .
It does any final confirmation necessary to ensure that the filename is
a valid one for the user to access .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
BOOL check_name ( char * name , connection_struct * conn )
{
BOOL ret ;
errno = 0 ;
if ( IS_VETO_PATH ( conn , name ) ) {
DEBUG ( 5 , ( " file path name %s vetoed \n " , name ) ) ;
return ( 0 ) ;
}
ret = reduce_name ( name , conn - > connectpath , lp_widelinks ( SNUM ( conn ) ) ) ;
/* Check if we are allowing users to follow symlinks */
/* Patch from David Clerc <David.Clerc@cui.unige.ch>
University of Geneva */
# ifdef S_ISLNK
if ( ! lp_symlinks ( SNUM ( conn ) ) )
{
1998-09-01 20:11:54 +00:00
SMB_STRUCT_STAT statbuf ;
2000-02-03 05:17:25 +00:00
if ( ( conn - > vfs_ops . lstat ( dos_to_unix ( name , False ) , & statbuf ) ! = - 1 ) & &
1998-08-17 07:40:06 +00:00
( S_ISLNK ( statbuf . st_mode ) ) )
{
DEBUG ( 3 , ( " check_name: denied: file path name %s is a symlink \n " , name ) ) ;
ret = 0 ;
}
}
# endif
if ( ! ret )
DEBUG ( 5 , ( " check_name on %s failed \n " , name ) ) ;
return ( ret ) ;
}
/****************************************************************************
scan a directory to find a filename , matching without case sensitivity
If the name looks like a mangled name then try via the mangling functions
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-09-05 05:07:05 +00:00
static BOOL scan_directory ( char * path , char * name , connection_struct * conn , BOOL docache )
1998-08-17 07:40:06 +00:00
{
void * cur_dir ;
char * dname ;
BOOL mangled ;
pstring name2 ;
mangled = is_mangled ( name ) ;
/* handle null paths */
if ( * path = = 0 )
path = " . " ;
if ( docache & & ( dname = DirCacheCheck ( path , name , SNUM ( conn ) ) ) ) {
pstrcpy ( name , dname ) ;
return ( True ) ;
}
/*
* The incoming name can be mangled , and if we de - mangle it
* here it will not compare correctly against the filename ( name2 )
* read from the directory and then mangled by the name_map_mangle ( )
* call . We need to mangle both names or neither .
* ( JRA ) .
*/
if ( mangled )
mangled = ! check_mangled_cache ( name ) ;
/* open the directory */
if ( ! ( cur_dir = OpenDir ( conn , path , True ) ) )
{
DEBUG ( 3 , ( " scan dir didn't open dir [%s] \n " , path ) ) ;
return ( False ) ;
}
/* now scan for matching names */
while ( ( dname = ReadDirName ( cur_dir ) ) )
{
if ( * dname = = ' . ' & &
( strequal ( dname , " . " ) | | strequal ( dname , " .. " ) ) )
continue ;
pstrcpy ( name2 , dname ) ;
1999-12-13 13:27:58 +00:00
if ( ! name_map_mangle ( name2 , False , True , SNUM ( conn ) ) )
continue ;
1998-08-17 07:40:06 +00:00
if ( ( mangled & & mangled_equal ( name , name2 ) )
| | fname_equal ( name , name2 ) )
{
/* we've found the file, change it's name and return */
if ( docache ) DirCacheAdd ( path , name , dname , SNUM ( conn ) ) ;
pstrcpy ( name , dname ) ;
CloseDir ( cur_dir ) ;
return ( True ) ;
}
}
CloseDir ( cur_dir ) ;
return ( False ) ;
}
2000-01-26 21:25:35 +00:00
2000-05-10 10:41:59 +00:00
# undef OLD_NTDOMAIN