1998-08-17 11:40:06 +04:00
/*
2002-01-30 09:08:46 +03:00
Unix SMB / CIFS implementation .
1998-08-17 11:40:06 +04:00
filename handling routines
Copyright ( C ) Andrew Tridgell 1992 - 1998
2000-01-27 00:25:35 +03:00
Copyright ( C ) Jeremy Allison 1999 - 200
Copyright ( C ) Ying Chen 2000
1998-08-17 11:40:06 +04: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-27 00:25:35 +03:00
/*
* New hash table stat cache code added by Ying Chen .
*/
1998-08-17 11:40:06 +04:00
# include "includes.h"
extern BOOL case_sensitive ;
extern BOOL case_preserve ;
extern BOOL short_case_preserve ;
extern BOOL use_mangled_map ;
2003-02-07 07:11:36 +03:00
static BOOL scan_directory ( const char * path , char * name , size_t maxlength ,
connection_struct * conn , BOOL docache ) ;
1998-09-05 09:07:05 +04:00
1998-08-17 11:40:06 +04:00
/****************************************************************************
1998-08-28 00:38:53 +04:00
Check if two filenames are equal .
This needs to be careful about whether we are case sensitive .
1998-08-17 11:40:06 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2002-07-15 14:35:28 +04:00
2003-03-18 01:56:13 +03:00
static BOOL fname_equal ( const char * name1 , const char * name2 )
1998-08-17 11:40:06 +04:00
{
2002-07-15 14:35:28 +04:00
/* Normal filename handling */
if ( case_sensitive )
return ( strcmp ( name1 , name2 ) = = 0 ) ;
1998-08-17 11:40:06 +04:00
2002-07-15 14:35:28 +04:00
return ( strequal ( name1 , name2 ) ) ;
}
1998-08-17 11:40:06 +04:00
/****************************************************************************
1998-08-28 00:38:53 +04:00
Mangle the 2 nd name and check if it is then equal to the first name .
1998-08-17 11:40:06 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2002-07-15 14:35:28 +04:00
2003-03-18 01:56:13 +03:00
static BOOL mangled_equal ( const char * name1 , const char * name2 , int snum )
1998-08-17 11:40:06 +04:00
{
2002-04-08 05:58:44 +04:00
pstring tmpname ;
pstrcpy ( tmpname , name2 ) ;
2002-07-15 14:35:28 +04:00
mangle_map ( tmpname , True , False , snum ) ;
return strequal ( name1 , tmpname ) ;
1998-08-17 11:40:06 +04: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 .
2000-10-19 06:58:24 +04:00
On exit from unix_convert , if * pst was not null , then the file stat
struct will be returned if the file exists and was found , if not this
stat struct will be filled with zeros ( and this can be detected by checking
for nlinks = 0 , which can never be true for any file ) .
1998-08-17 11:40:06 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1999-12-13 16:27:58 +03:00
2002-04-08 05:58:44 +04:00
BOOL unix_convert ( pstring name , connection_struct * conn , char * saved_last_component ,
1999-12-13 16:27:58 +03:00
BOOL * bad_path , SMB_STRUCT_STAT * pst )
1998-08-17 11:40:06 +04:00
{
2002-07-15 14:35:28 +04:00
SMB_STRUCT_STAT st ;
char * start , * end ;
pstring dirpath ;
pstring orig_path ;
BOOL component_was_mangled = False ;
BOOL name_has_wildcard = False ;
ZERO_STRUCTP ( pst ) ;
* dirpath = 0 ;
* bad_path = False ;
if ( saved_last_component )
* saved_last_component = 0 ;
if ( conn - > printer ) {
/* we don't ever use the filenames on a printer share as a
filename - so don ' t convert them */
return True ;
}
DEBUG ( 5 , ( " unix_convert called on file \" %s \" \n " , name ) ) ;
/*
* Convert to basic unix format - removing \ chars and cleaning it up .
*/
unix_format ( name ) ;
unix_clean_name ( name ) ;
/*
* Names must be relative to the root of the service - trim any leading / .
* also trim trailing / ' s .
*/
trim_string ( name , " / " , " / " ) ;
/*
* 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 ) {
name [ 0 ] = ' . ' ;
name [ 1 ] = ' \0 ' ;
}
/*
* Ensure saved_last_component is valid even if file exists .
*/
if ( saved_last_component ) {
end = strrchr_m ( name , ' / ' ) ;
if ( end )
pstrcpy ( saved_last_component , end + 1 ) ;
else
pstrcpy ( saved_last_component , name ) ;
}
if ( ! case_sensitive & & ( ! case_preserve | | ( mangle_is_8_3 ( name , False ) & & ! short_case_preserve ) ) )
strnorm ( name ) ;
/*
* 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 ) ;
start = name ;
while ( strncmp ( start , " ./ " , 2 ) = = 0 )
start + = 2 ;
pstrcpy ( orig_path , name ) ;
2002-09-25 19:19:00 +04:00
if ( ! case_sensitive & & stat_cache_lookup ( conn , name , dirpath , & start , & st ) ) {
2002-07-15 14:35:28 +04:00
* pst = st ;
return True ;
}
/*
* stat the name - if it exists then we are all done !
*/
if ( vfs_stat ( conn , name , & st ) = = 0 ) {
stat_cache_add ( orig_path , name ) ;
DEBUG ( 5 , ( " conversion finished %s -> %s \n " , orig_path , name ) ) ;
* pst = st ;
return ( True ) ;
}
DEBUG ( 5 , ( " unix_convert begin: name = %s, dirpath = %s, start = %s \n " , name , dirpath , start ) ) ;
/*
* A special case - if we don ' t have any mangling chars and are case
* sensitive then searching won ' t help .
*/
if ( case_sensitive & & ! mangle_is_mangled ( name ) & & ! use_mangled_map )
return ( False ) ;
name_has_wildcard = ms_has_wild ( start ) ;
/*
* is_mangled ( ) was changed to look at an entire pathname , not
* just a component . JRA .
*/
if ( mangle_is_mangled ( start ) )
component_was_mangled = True ;
/*
* Now we need to recursively match the name against the real
* directory structure .
*/
/*
* Match each part of the path name separately , trying the names
* as is first , then trying to scan the directory for matching names .
*/
for ( ; start ; start = ( end ? end + 1 : ( char * ) NULL ) ) {
/*
* Pinpoint the end of this section of the filename .
*/
end = strchr_m ( start , ' / ' ) ;
/*
* Chop the name at this point .
*/
if ( end )
* end = 0 ;
if ( saved_last_component ! = 0 )
pstrcpy ( saved_last_component , end ? end + 1 : start ) ;
/*
* Check if the name exists up to this point .
*/
if ( vfs_stat ( conn , name , & st ) = = 0 ) {
/*
* 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 ) ;
}
} else {
pstring rest ;
/* Stat failed - ensure we don't use it. */
ZERO_STRUCT ( st ) ;
* 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 .
*/
2003-02-07 07:11:36 +03:00
if ( ms_has_wild ( start ) | |
! scan_directory ( dirpath , start ,
sizeof ( pstring ) - 1 - ( start - name ) ,
conn ,
end ? True : False ) ) {
2002-07-15 14:35:28 +04:00
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 11:40:06 +04:00
2002-07-15 14:35:28 +04: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 .
*/
if ( mangle_is_mangled ( start ) ) {
mangle_check_cache ( start ) ;
}
DEBUG ( 5 , ( " New file %s \n " , start ) ) ;
return ( True ) ;
}
/*
* Restore the rest of the string . If the string was mangled the size
* may have changed .
*/
if ( end ) {
end = start + strlen ( start ) ;
2003-02-07 07:11:36 +03:00
if ( ! safe_strcat ( start , " / " , sizeof ( pstring ) - 1 - ( start - name ) ) | |
! safe_strcat ( start , rest , sizeof ( pstring ) - 1 - ( start - name ) ) ) {
return False ;
}
2002-07-15 14:35:28 +04:00
* end = ' \0 ' ;
2002-10-11 05:19:00 +04:00
} else {
/*
* We just scanned for , and found the end of the path .
* We must return a valid stat struct if it exists .
* JRA .
*/
if ( vfs_stat ( conn , name , & st ) = = 0 ) {
* pst = st ;
} else {
ZERO_STRUCT ( st ) ;
}
2002-07-15 14:35:28 +04:00
}
} /* end else */
/*
* Add to the dirpath that we have resolved so far .
*/
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 ) ;
/*
* Restore the / that we wiped out earlier .
*/
if ( end )
* end = ' / ' ;
}
1998-08-17 11:40:06 +04:00
2002-07-15 14:35:28 +04:00
/*
* Don ' t cache a name with mangled or wildcard components
* as this can change the size .
*/
2000-12-12 03:11:34 +03:00
2002-07-15 14:35:28 +04:00
if ( ! component_was_mangled & & ! name_has_wildcard )
stat_cache_add ( orig_path , name ) ;
2000-12-12 03:11:34 +03:00
2002-07-15 14:35:28 +04:00
/*
* If we ended up resolving the entire path then return a valid
* stat struct if we got one .
*/
2000-12-12 03:11:34 +03:00
2002-07-15 14:35:28 +04:00
if ( VALID_STAT ( st ) & & ( strlen ( orig_path ) = = strlen ( name ) ) )
* pst = st ;
2000-10-19 06:58:24 +04:00
2002-07-15 14:35:28 +04:00
/*
* The name has been resolved .
*/
1998-08-28 00:38:53 +04:00
2002-07-15 14:35:28 +04:00
DEBUG ( 5 , ( " conversion finished %s -> %s \n " , orig_path , name ) ) ;
return ( True ) ;
1998-08-17 11:40:06 +04:00
}
/****************************************************************************
2002-07-15 14:35:28 +04:00
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 .
1998-08-17 11:40:06 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2002-07-15 14:35:28 +04:00
2003-01-03 11:28:12 +03:00
BOOL check_name ( pstring name , connection_struct * conn )
1998-08-17 11:40:06 +04:00
{
2002-07-15 14:35:28 +04:00
BOOL ret ;
1998-08-17 11:40:06 +04:00
2002-07-15 14:35:28 +04:00
errno = 0 ;
1998-08-17 11:40:06 +04:00
2002-07-15 14:35:28 +04:00
if ( IS_VETO_PATH ( conn , name ) ) {
if ( strcmp ( name , " . " ) & & strcmp ( name , " .. " ) ) {
DEBUG ( 5 , ( " file path name %s vetoed \n " , name ) ) ;
return ( 0 ) ;
}
}
1998-08-17 11:40:06 +04:00
2002-07-15 14:35:28 +04:00
ret = reduce_name ( conn , name , conn - > connectpath , lp_widelinks ( SNUM ( conn ) ) ) ;
1998-08-17 11:40:06 +04:00
2002-07-15 14:35:28 +04:00
/* Check if we are allowing users to follow symlinks */
/* Patch from David Clerc <David.Clerc@cui.unige.ch>
University of Geneva */
1998-08-17 11:40:06 +04:00
# ifdef S_ISLNK
2002-07-15 14:35:28 +04:00
if ( ! lp_symlinks ( SNUM ( conn ) ) ) {
SMB_STRUCT_STAT statbuf ;
if ( ( conn - > vfs_ops . lstat ( conn , name , & statbuf ) ! = - 1 ) & &
( S_ISLNK ( statbuf . st_mode ) ) ) {
DEBUG ( 3 , ( " check_name: denied: file path name %s is a symlink \n " , name ) ) ;
ret = 0 ;
}
}
1998-08-17 11:40:06 +04:00
# endif
2002-07-15 14:35:28 +04:00
if ( ! ret )
DEBUG ( 5 , ( " check_name on %s failed \n " , name ) ) ;
1998-08-17 11:40:06 +04:00
2002-07-15 14:35:28 +04:00
return ( ret ) ;
1998-08-17 11:40:06 +04:00
}
/****************************************************************************
2002-07-15 14:35:28 +04:00
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-08-17 11:40:06 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2002-07-15 14:35:28 +04:00
2003-02-07 07:11:36 +03:00
static BOOL scan_directory ( const char * path , char * name , size_t maxlength ,
connection_struct * conn , BOOL docache )
1998-08-17 11:40:06 +04:00
{
2002-07-15 14:35:28 +04:00
void * cur_dir ;
2003-03-18 01:56:13 +03:00
const char * dname ;
2002-07-15 14:35:28 +04:00
BOOL mangled ;
mangled = mangle_is_mangled ( name ) ;
/* handle null paths */
if ( * path = = 0 )
path = " . " ;
if ( docache & & ( dname = DirCacheCheck ( path , name , SNUM ( conn ) ) ) ) {
2003-02-07 07:11:36 +03:00
safe_strcpy ( name , dname , maxlength ) ;
2002-07-15 14:35:28 +04:00
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 mangle_map ( )
* call . We need to mangle both names or neither .
* ( JRA ) .
*/
if ( mangled )
mangled = ! mangle_check_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 ;
/*
* At this point dname is the unmangled name .
* name is either mangled or not , depending on the state of the " mangled "
* variable . JRA .
*/
/*
* Check mangled name against mangled name , or unmangled name
* against unmangled name .
*/
if ( ( mangled & & mangled_equal ( name , dname , SNUM ( conn ) ) ) | | fname_equal ( name , dname ) ) {
/* we've found the file, change it's name and return */
if ( docache )
DirCacheAdd ( path , name , dname , SNUM ( conn ) ) ;
2003-02-07 07:11:36 +03:00
safe_strcpy ( name , dname , maxlength ) ;
2002-07-15 14:35:28 +04:00
CloseDir ( cur_dir ) ;
return ( True ) ;
}
}
CloseDir ( cur_dir ) ;
return ( False ) ;
1998-08-17 11:40:06 +04:00
}