2007-08-31 01:46:42 +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
2007-09-08 00:57:01 +04:00
Copyright ( C ) Jeremy Allison 1999 - 2007
2000-01-27 00:25:35 +03:00
Copyright ( C ) Ying Chen 2000
2007-07-12 02:39:11 +04:00
Copyright ( C ) Volker Lendecke 2007
2007-08-31 01:46:42 +04:00
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
2007-07-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
1998-08-17 11:40:06 +04:00
( at your option ) any later version .
2007-08-31 01:46:42 +04:00
1998-08-17 11:40:06 +04:00
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 .
2007-08-31 01:46:42 +04:00
1998-08-17 11:40:06 +04:00
You should have received a copy of the GNU General Public License
2007-07-10 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
1998-08-17 11:40:06 +04:00
*/
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"
2008-01-20 01:25:36 +03:00
static NTSTATUS build_stream_path ( TALLOC_CTX * mem_ctx ,
connection_struct * conn ,
const char * orig_path ,
const char * basepath ,
const char * streamname ,
SMB_STRUCT_STAT * pst ,
char * * path ) ;
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
2007-10-19 04:40:25 +04:00
static bool mangled_equal ( const char * name1 ,
2007-08-31 01:46:42 +04:00
const char * name2 ,
const struct share_params * p )
1998-08-17 11:40:06 +04:00
{
2007-09-08 00:57:01 +04:00
char mname [ 13 ] ;
2007-08-31 01:46:42 +04:00
2007-09-08 00:57:01 +04:00
if ( ! name_to_8_3 ( name2 , mname , False , p ) ) {
return False ;
}
return strequal ( name1 , mname ) ;
1998-08-17 11:40:06 +04:00
}
2007-01-13 04:29:10 +03:00
/****************************************************************************
Cope with the differing wildcard and non - wildcard error cases .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-08-31 01:46:42 +04:00
static NTSTATUS determine_path_error ( const char * name ,
2007-10-19 04:40:25 +04:00
bool allow_wcard_last_component )
2007-01-13 04:29:10 +03:00
{
const char * p ;
if ( ! allow_wcard_last_component ) {
/* Error code within a pathname. */
return NT_STATUS_OBJECT_PATH_NOT_FOUND ;
}
/* We're terminating here so we
* can be a little slower and get
* the error code right . Windows
* treats the last part of the pathname
* separately I think , so if the last
* component is a wildcard then we treat
* this . / as " end of component " */
p = strchr ( name , ' / ' ) ;
if ( ! p & & ( ms_has_wild ( name ) | | ISDOT ( name ) ) ) {
/* Error code at the end of a pathname. */
return NT_STATUS_OBJECT_NAME_INVALID ;
} else {
/* Error code within a pathname. */
return NT_STATUS_OBJECT_PATH_NOT_FOUND ;
}
}
2007-08-31 01:46:42 +04:00
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 .
2007-08-31 01:46:42 +04:00
The function will return an NTSTATUS error if some part of the name except for
the last part cannot be resolved , else NT_STATUS_OK .
2007-01-13 02:47:16 +03:00
Note NT_STATUS_OK doesn ' t mean the name exists or is valid , just that we didn ' t
get any fatal errors that should immediately terminate the calling
SMB processing whilst resolving .
1998-08-17 11:40:06 +04:00
If the saved_last_component ! = 0 , then the unmodified last component
2008-09-24 02:05:45 +04:00
of the pathname is returned there . If saved_last_component = = 0 then nothing
1998-08-17 11:40:06 +04:00
is returned there .
2007-01-13 02:47:16 +03:00
If last_component_wcard is true then a MS wildcard was detected and
should be allowed in the last component of the path only .
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
2007-09-14 02:08:59 +04:00
NTSTATUS unix_convert ( TALLOC_CTX * ctx ,
connection_struct * conn ,
2007-09-08 00:57:01 +04:00
const char * orig_path ,
2007-10-19 04:40:25 +04:00
bool allow_wcard_last_component ,
2007-09-08 00:57:01 +04:00
char * * pp_conv_path ,
char * * pp_saved_last_component ,
2007-01-13 02:47:16 +03:00
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 ;
2007-07-12 02:39:11 +04:00
char * dirpath = NULL ;
char * name = NULL ;
2008-01-20 01:25:36 +03:00
char * stream = NULL ;
2007-10-19 04:40:25 +04:00
bool component_was_mangled = False ;
bool name_has_wildcard = False ;
2009-02-05 03:33:52 +03:00
bool posix_pathnames = false ;
2007-07-12 02:39:11 +04:00
NTSTATUS result ;
2009-02-05 03:33:52 +03:00
int ret = - 1 ;
2002-07-15 14:35:28 +04:00
2005-06-03 09:35:04 +04:00
SET_STAT_INVALID ( * pst ) ;
2007-09-08 00:57:01 +04:00
* pp_conv_path = NULL ;
if ( pp_saved_last_component ) {
* pp_saved_last_component = NULL ;
2007-01-13 02:47:16 +03:00
}
2002-07-15 14:35:28 +04:00
if ( conn - > printer ) {
/* we don't ever use the filenames on a printer share as a
filename - so don ' t convert them */
2007-09-08 00:57:01 +04:00
if ( ! ( * pp_conv_path = talloc_strdup ( ctx , orig_path ) ) ) {
return NT_STATUS_NO_MEMORY ;
}
2007-01-13 02:47:16 +03:00
return NT_STATUS_OK ;
2002-07-15 14:35:28 +04:00
}
2007-07-12 02:39:11 +04:00
DEBUG ( 5 , ( " unix_convert called on file \" %s \" \n " , orig_path ) ) ;
2002-07-15 14:35:28 +04:00
2007-07-12 02:39:11 +04:00
/*
2007-08-31 01:46:42 +04:00
* Conversion to basic unix format is already done in
* check_path_syntax ( ) .
2002-07-15 14:35:28 +04:00
*/
2007-07-12 02:39:11 +04:00
/*
2004-03-03 23:55:59 +03:00
* Names must be relative to the root of the service - any leading / .
* and trailing / ' s should have been trimmed by check_path_syntax ( ) .
2002-07-15 14:35:28 +04:00
*/
2004-03-03 23:55:59 +03:00
# ifdef DEVELOPER
2007-07-12 02:39:11 +04:00
SMB_ASSERT ( * orig_path ! = ' / ' ) ;
2004-03-03 23:55:59 +03:00
# endif
2002-07-15 14:35:28 +04: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 .
2004-03-03 23:55:59 +03:00
* As we know this is valid we can return true here .
2002-07-15 14:35:28 +04:00
*/
2007-07-12 02:39:11 +04:00
if ( ! * orig_path ) {
2007-09-08 00:57:01 +04:00
if ( ! ( name = talloc_strdup ( ctx , " . " ) ) ) {
2007-08-21 16:58:10 +04:00
return NT_STATUS_NO_MEMORY ;
2007-07-12 02:39:11 +04:00
}
2004-05-14 05:22:17 +04:00
if ( SMB_VFS_STAT ( conn , name , & st ) = = 0 ) {
* pst = st ;
2007-09-08 00:57:01 +04:00
} else {
return map_nt_error_from_unix ( errno ) ;
2004-05-14 05:22:17 +04:00
}
2004-05-28 05:54:01 +04:00
DEBUG ( 5 , ( " conversion finished \" \" -> %s \n " , name ) ) ;
2007-07-12 02:39:11 +04:00
goto done ;
2007-01-13 02:47:16 +03:00
}
2007-08-31 01:46:42 +04:00
if ( orig_path [ 0 ] = = ' . ' & & ( orig_path [ 1 ] = = ' / ' | |
orig_path [ 1 ] = = ' \0 ' ) ) {
2007-01-13 02:47:16 +03:00
/* Start of pathname can't be "." only. */
2007-07-12 02:39:11 +04:00
if ( orig_path [ 1 ] = = ' \0 ' | | orig_path [ 2 ] = = ' \0 ' ) {
result = NT_STATUS_OBJECT_NAME_INVALID ;
2007-01-13 04:07:39 +03:00
} else {
2007-07-12 02:39:11 +04:00
result = determine_path_error (
& orig_path [ 2 ] , allow_wcard_last_component ) ;
2007-01-13 04:07:39 +03:00
}
2007-08-20 00:00:43 +04:00
return result ;
2002-07-15 14:35:28 +04:00
}
2008-11-18 21:57:54 +03:00
if ( ! ( name = talloc_strdup ( ctx , orig_path ) ) ) {
DEBUG ( 0 , ( " talloc_strdup failed \n " ) ) ;
return NT_STATUS_NO_MEMORY ;
}
/*
* Large directory fix normalization . If we ' re case sensitive , and
* the case preserving parameters are set to " no " , normalize the case of
* the incoming filename from the client WHETHER IT EXISTS OR NOT !
* This is in conflict with the current ( 3.0 .20 ) man page , but is
* what people expect from the " large directory howto " . I ' ll update
* the man page . Thanks to jht @ samba . org for finding this . JRA .
*/
if ( conn - > case_sensitive & & ! conn - > case_preserve & &
! conn - > short_case_preserve ) {
strnorm ( name , lp_defaultcase ( SNUM ( conn ) ) ) ;
}
2002-07-15 14:35:28 +04:00
/*
* Ensure saved_last_component is valid even if file exists .
*/
2007-09-08 00:57:01 +04:00
if ( pp_saved_last_component ) {
2008-11-18 21:57:54 +03:00
end = strrchr_m ( name , ' / ' ) ;
2007-01-13 02:47:16 +03:00
if ( end ) {
2007-09-08 00:57:01 +04:00
* pp_saved_last_component = talloc_strdup ( ctx , end + 1 ) ;
2007-01-13 02:47:16 +03:00
} else {
2007-09-08 00:57:01 +04:00
* pp_saved_last_component = talloc_strdup ( ctx ,
2008-11-18 21:57:54 +03:00
name ) ;
2008-11-18 19:03:38 +03:00
}
2002-07-15 14:35:28 +04:00
}
2009-02-05 03:33:52 +03:00
posix_pathnames = lp_posix_pathnames ( ) ;
if ( ! posix_pathnames ) {
2008-01-20 08:53:49 +03:00
stream = strchr_m ( name , ' : ' ) ;
if ( stream ! = NULL ) {
char * tmp = talloc_strdup ( ctx , stream ) ;
if ( tmp = = NULL ) {
TALLOC_FREE ( name ) ;
return NT_STATUS_NO_MEMORY ;
}
* stream = ' \0 ' ;
stream = tmp ;
2008-01-20 01:25:36 +03:00
}
}
2002-07-15 14:35:28 +04:00
start = name ;
2007-12-23 04:38:18 +03:00
/* If we're providing case insentive semantics or
* the underlying filesystem is case insensitive ,
* then a case - normalized hit in the stat - cache is
* authoratitive . JRA .
*/
if ( ( ! conn - > case_sensitive | | ! ( conn - > fs_capabilities & FILE_CASE_SENSITIVE_SEARCH ) ) & &
stat_cache_lookup ( conn , & name , & dirpath , & start , & st ) ) {
2002-07-15 14:35:28 +04:00
* pst = st ;
2007-07-12 02:39:11 +04:00
goto done ;
}
/*
* Make sure " dirpath " is an allocated string , we use this for
* building the directories with asprintf and free it .
*/
2007-09-08 00:57:01 +04:00
if ( ( dirpath = = NULL ) & & ( ! ( dirpath = talloc_strdup ( ctx , " " ) ) ) ) {
DEBUG ( 0 , ( " talloc_strdup failed \n " ) ) ;
TALLOC_FREE ( name ) ;
2007-08-20 00:00:43 +04:00
return NT_STATUS_NO_MEMORY ;
2002-07-15 14:35:28 +04:00
}
2007-07-12 02:39:11 +04:00
/*
2002-07-15 14:35:28 +04:00
* stat the name - if it exists then we are all done !
*/
2009-02-05 03:33:52 +03:00
if ( posix_pathnames ) {
ret = SMB_VFS_LSTAT ( conn , name , & st ) ;
} else {
ret = SMB_VFS_STAT ( conn , name , & st ) ;
}
if ( ret = = 0 ) {
2007-01-13 02:47:16 +03:00
/* Ensure we catch all names with in "/."
this is disallowed under Windows . */
const char * p = strstr ( name , " /. " ) ; /* mb safe. */
if ( p ) {
if ( p [ 2 ] = = ' / ' ) {
/* Error code within a pathname. */
2007-07-15 13:42:43 +04:00
result = NT_STATUS_OBJECT_PATH_NOT_FOUND ;
goto fail ;
2007-01-13 02:47:16 +03:00
} else if ( p [ 2 ] = = ' \0 ' ) {
/* Error code at the end of a pathname. */
2007-07-15 13:42:43 +04:00
result = NT_STATUS_OBJECT_NAME_INVALID ;
goto fail ;
2007-01-13 02:47:16 +03:00
}
}
2004-05-07 22:37:47 +04:00
stat_cache_add ( orig_path , name , conn - > case_sensitive ) ;
2002-07-15 14:35:28 +04:00
DEBUG ( 5 , ( " conversion finished %s -> %s \n " , orig_path , name ) ) ;
* pst = st ;
2007-07-12 02:39:11 +04:00
goto done ;
2002-07-15 14:35:28 +04:00
}
2007-08-31 01:46:42 +04:00
DEBUG ( 5 , ( " unix_convert begin: name = %s, dirpath = %s, start = %s \n " ,
name , dirpath , start ) ) ;
2002-07-15 14:35:28 +04:00
2007-07-12 02:39:11 +04:00
/*
2002-07-15 14:35:28 +04:00
* A special case - if we don ' t have any mangling chars and are case
2007-12-23 04:38:18 +03:00
* sensitive or the underlying filesystem is case insentive then searching
* won ' t help .
2002-07-15 14:35:28 +04:00
*/
2007-12-23 04:38:18 +03:00
if ( ( conn - > case_sensitive | | ! ( conn - > fs_capabilities & FILE_CASE_SENSITIVE_SEARCH ) ) & &
2007-09-08 00:57:01 +04:00
! mangle_is_mangled ( name , conn - > params ) ) {
2007-07-12 02:39:11 +04:00
goto done ;
2007-01-13 02:47:16 +03:00
}
2002-07-15 14:35:28 +04:00
2007-07-12 02:39:11 +04:00
/*
* is_mangled ( ) was changed to look at an entire pathname , not
2002-07-15 14:35:28 +04:00
* just a component . JRA .
*/
2007-01-13 02:47:16 +03:00
if ( mangle_is_mangled ( start , conn - > params ) ) {
2002-07-15 14:35:28 +04:00
component_was_mangled = True ;
2007-01-13 02:47:16 +03:00
}
2002-07-15 14:35:28 +04:00
2007-07-12 02:39:11 +04:00
/*
* Now we need to recursively match the name against the real
2002-07-15 14:35:28 +04:00
* directory structure .
*/
2007-07-12 02:39:11 +04:00
/*
2002-07-15 14:35:28 +04:00
* 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 ) ) {
2007-07-12 02:39:11 +04:00
/*
2002-07-15 14:35:28 +04:00
* Pinpoint the end of this section of the filename .
*/
2007-08-31 01:46:42 +04:00
/* mb safe. '/' can't be in any encoded char. */
end = strchr ( start , ' / ' ) ;
2002-07-15 14:35:28 +04:00
2007-07-12 02:39:11 +04:00
/*
2002-07-15 14:35:28 +04:00
* Chop the name at this point .
*/
2007-01-13 02:47:16 +03:00
if ( end ) {
2002-07-15 14:35:28 +04:00
* end = 0 ;
2007-01-13 02:47:16 +03:00
}
2002-07-15 14:35:28 +04:00
2007-09-08 00:57:01 +04:00
if ( pp_saved_last_component ) {
TALLOC_FREE ( * pp_saved_last_component ) ;
* pp_saved_last_component = talloc_strdup ( ctx ,
end ? end + 1 : start ) ;
if ( ! * pp_saved_last_component ) {
DEBUG ( 0 , ( " talloc failed \n " ) ) ;
return NT_STATUS_NO_MEMORY ;
}
2007-01-13 02:47:16 +03:00
}
/* The name cannot have a component of "." */
if ( ISDOT ( start ) ) {
2007-01-13 04:29:10 +03:00
if ( ! end ) {
2007-01-13 02:47:16 +03:00
/* Error code at the end of a pathname. */
2007-07-12 02:39:11 +04:00
result = NT_STATUS_OBJECT_NAME_INVALID ;
} else {
result = determine_path_error ( end + 1 ,
allow_wcard_last_component ) ;
2007-01-13 02:47:16 +03:00
}
2007-07-12 02:39:11 +04:00
goto fail ;
2007-01-13 02:47:16 +03:00
}
/* The name cannot have a wildcard if it's not
the last component . */
name_has_wildcard = ms_has_wild ( start ) ;
/* Wildcard not valid anywhere. */
if ( name_has_wildcard & & ! allow_wcard_last_component ) {
2007-07-12 02:39:11 +04:00
result = NT_STATUS_OBJECT_NAME_INVALID ;
goto fail ;
2007-01-13 02:47:16 +03:00
}
/* Wildcards never valid within a pathname. */
if ( name_has_wildcard & & end ) {
2007-07-12 02:39:11 +04:00
result = NT_STATUS_OBJECT_NAME_INVALID ;
goto fail ;
2007-01-13 02:47:16 +03:00
}
2002-07-15 14:35:28 +04:00
2007-07-12 02:39:11 +04:00
/*
2002-07-15 14:35:28 +04:00
* Check if the name exists up to this point .
*/
2009-02-05 03:33:52 +03:00
if ( posix_pathnames ) {
ret = SMB_VFS_LSTAT ( conn , name , & st ) ;
} else {
ret = SMB_VFS_STAT ( conn , name , & st ) ;
}
if ( ret = = 0 ) {
2002-07-15 14:35:28 +04:00
/*
2007-08-31 01:46:42 +04:00
* It exists . it must either be a directory or this must
* be the last part of the path for it to be OK .
2002-07-15 14:35:28 +04:00
*/
if ( end & & ! ( st . st_mode & S_IFDIR ) ) {
/*
2007-08-31 01:46:42 +04:00
* An intermediate part of the name isn ' t
* a directory .
2002-07-15 14:35:28 +04:00
*/
DEBUG ( 5 , ( " Not a dir %s \n " , start ) ) ;
* end = ' / ' ;
2007-07-12 02:39:11 +04:00
/*
2007-08-31 01:46:42 +04:00
* 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
2004-06-11 21:54:23 +04:00
* these two errors .
*/
2007-07-12 02:39:11 +04:00
result = NT_STATUS_OBJECT_PATH_NOT_FOUND ;
goto fail ;
2002-07-15 14:35:28 +04:00
}
2003-05-09 05:06:27 +04:00
if ( ! end ) {
/*
2007-08-31 01:46:42 +04:00
* We just scanned for , and found the end of
* the path . We must return the valid stat
* struct . JRA .
2003-05-09 05:06:27 +04:00
*/
* pst = st ;
}
2002-07-15 14:35:28 +04:00
} else {
2007-07-12 02:39:11 +04:00
char * found_name = NULL ;
2002-07-15 14:35:28 +04:00
/* Stat failed - ensure we don't use it. */
2005-06-03 03:18:52 +04:00
SET_STAT_INVALID ( st ) ;
2002-07-15 14:35:28 +04:00
2007-08-31 01:46:42 +04:00
/*
* Reset errno so we can detect
* directory open errors .
*/
2004-06-11 21:54:23 +04:00
errno = 0 ;
2002-07-15 14:35:28 +04:00
/*
* Try to find this part of the path in the directory .
*/
2007-08-31 01:46:42 +04:00
if ( name_has_wildcard | |
2009-05-02 04:28:38 +04:00
( get_real_filename ( conn , dirpath , start ,
talloc_tos ( ) ,
& found_name ) = = - 1 ) ) {
2007-07-12 02:39:11 +04:00
char * unmangled ;
2002-07-15 14:35:28 +04:00
if ( end ) {
/*
2007-08-31 01:46:42 +04:00
* An intermediate part of the name
* can ' t be found .
2002-07-15 14:35:28 +04:00
*/
2007-08-31 01:46:42 +04:00
DEBUG ( 5 , ( " Intermediate not found %s \n " ,
start ) ) ;
2002-07-15 14:35:28 +04:00
* end = ' / ' ;
2007-07-12 02:39:11 +04:00
/*
2007-08-31 01:46:42 +04:00
* 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 .
2002-07-15 14:35:28 +04:00
*/
2007-01-13 05:13:45 +03:00
2007-07-12 03:54:01 +04:00
/*
* ENOENT , ENOTDIR and ELOOP all map
* to NT_STATUS_OBJECT_PATH_NOT_FOUND
* in the filename walk .
*/
2007-01-13 05:13:45 +03:00
2007-07-12 03:54:01 +04:00
if ( errno = = ENOENT | |
errno = = ENOTDIR | |
errno = = ELOOP ) {
2007-08-31 01:46:42 +04:00
result =
NT_STATUS_OBJECT_PATH_NOT_FOUND ;
2007-07-12 02:39:11 +04:00
}
else {
2007-08-31 01:46:42 +04:00
result =
map_nt_error_from_unix ( errno ) ;
2007-01-13 02:47:16 +03:00
}
2007-07-12 02:39:11 +04:00
goto fail ;
2002-07-15 14:35:28 +04:00
}
2007-07-12 02:39:11 +04:00
2007-01-13 05:13:45 +03:00
/* ENOENT is the only valid error here. */
2008-08-12 16:59:59 +04:00
if ( ( errno ! = 0 ) & & ( errno ! = ENOENT ) ) {
2007-07-12 03:54:01 +04:00
/*
* ENOTDIR and ELOOP both map to
* NT_STATUS_OBJECT_PATH_NOT_FOUND
* in the filename walk .
*/
if ( errno = = ENOTDIR | |
errno = = ELOOP ) {
2007-08-31 01:46:42 +04:00
result =
NT_STATUS_OBJECT_PATH_NOT_FOUND ;
2007-07-12 02:39:11 +04:00
}
else {
2007-08-31 01:46:42 +04:00
result =
map_nt_error_from_unix ( errno ) ;
2007-01-13 05:13:45 +03:00
}
2007-07-12 02:39:11 +04:00
goto fail ;
2004-06-11 21:54:23 +04:00
}
2005-11-22 19:21:52 +03:00
/*
2002-07-15 14:35:28 +04:00
* Just the last part of the name doesn ' t exist .
2005-11-22 19:21:52 +03:00
* We need to strupper ( ) or strlower ( ) it as
2007-08-31 01:46:42 +04:00
* this conversion may be used for file creation
* purposes . Fix inspired by
* Thomas Neumann < t . neumann @ iku - ag . de > .
2002-07-15 14:35:28 +04:00
*/
2005-11-22 19:21:52 +03:00
if ( ! conn - > case_preserve | |
2007-08-31 01:46:42 +04:00
( mangle_is_8_3 ( start , False ,
conn - > params ) & &
2005-11-22 19:21:52 +03:00
! conn - > short_case_preserve ) ) {
2007-08-31 01:46:42 +04:00
strnorm ( start ,
lp_defaultcase ( SNUM ( conn ) ) ) ;
2005-11-22 19:21:52 +03:00
}
2002-07-15 14:35:28 +04:00
/*
2007-08-31 01:46:42 +04:00
* check on the mangled stack to see if we can
* recover the base of the filename .
2002-07-15 14:35:28 +04:00
*/
2007-07-12 02:39:11 +04:00
if ( mangle_is_mangled ( start , conn - > params )
2007-09-08 00:57:01 +04:00
& & mangle_lookup_name_from_8_3 ( ctx ,
start ,
& unmangled ,
conn - > params ) ) {
2007-07-12 02:39:11 +04:00
char * tmp ;
size_t start_ofs = start - name ;
if ( * dirpath ! = ' \0 ' ) {
2007-09-08 00:57:01 +04:00
tmp = talloc_asprintf ( ctx ,
" %s/%s " , dirpath ,
unmangled ) ;
TALLOC_FREE ( unmangled ) ;
2007-07-12 02:39:11 +04:00
}
else {
tmp = unmangled ;
}
if ( tmp = = NULL ) {
2007-09-08 00:57:01 +04:00
DEBUG ( 0 , ( " talloc failed \n " ) ) ;
return NT_STATUS_NO_MEMORY ;
2007-07-12 02:39:11 +04:00
}
2007-09-08 00:57:01 +04:00
TALLOC_FREE ( name ) ;
2007-07-12 02:39:11 +04:00
name = tmp ;
start = name + start_ofs ;
end = start + strlen ( start ) ;
2002-07-15 14:35:28 +04:00
}
DEBUG ( 5 , ( " New file %s \n " , start ) ) ;
2007-07-12 02:39:11 +04:00
goto done ;
2002-07-15 14:35:28 +04:00
}
2007-07-12 02:39:11 +04:00
/*
2007-08-31 01:46:42 +04:00
* Restore the rest of the string . If the string was
* mangled the size may have changed .
2002-07-15 14:35:28 +04:00
*/
if ( end ) {
2007-07-12 02:39:11 +04:00
char * tmp ;
size_t start_ofs = start - name ;
if ( * dirpath ! = ' \0 ' ) {
2007-09-08 00:57:01 +04:00
tmp = talloc_asprintf ( ctx ,
" %s/%s/%s " , dirpath ,
found_name , end + 1 ) ;
2003-02-07 07:11:36 +03:00
}
2007-07-12 02:39:11 +04:00
else {
2007-09-08 00:57:01 +04:00
tmp = talloc_asprintf ( ctx ,
" %s/%s " , found_name ,
end + 1 ) ;
2007-07-12 02:39:11 +04:00
}
if ( tmp = = NULL ) {
2007-09-08 00:57:01 +04:00
DEBUG ( 0 , ( " talloc_asprintf failed \n " ) ) ;
return NT_STATUS_NO_MEMORY ;
2007-07-12 02:39:11 +04:00
}
2007-09-08 00:57:01 +04:00
TALLOC_FREE ( name ) ;
2007-07-12 02:39:11 +04:00
name = tmp ;
start = name + start_ofs ;
end = start + strlen ( found_name ) ;
2002-07-15 14:35:28 +04:00
* end = ' \0 ' ;
2002-10-11 05:19:00 +04:00
} else {
2007-07-12 02:39:11 +04:00
char * tmp ;
size_t start_ofs = start - name ;
if ( * dirpath ! = ' \0 ' ) {
2007-09-08 00:57:01 +04:00
tmp = talloc_asprintf ( ctx ,
" %s/%s " , dirpath ,
found_name ) ;
2007-09-12 03:57:59 +04:00
} else {
2007-09-08 00:57:01 +04:00
tmp = talloc_strdup ( ctx ,
found_name ) ;
2007-07-12 02:39:11 +04:00
}
if ( tmp = = NULL ) {
2007-09-08 00:57:01 +04:00
DEBUG ( 0 , ( " talloc failed \n " ) ) ;
return NT_STATUS_NO_MEMORY ;
2007-07-12 02:39:11 +04:00
}
2007-09-08 00:57:01 +04:00
TALLOC_FREE ( name ) ;
2007-07-12 02:39:11 +04:00
name = tmp ;
start = name + start_ofs ;
2002-10-11 05:19:00 +04:00
/*
2007-08-31 01:46:42 +04:00
* We just scanned for , and found the end of
* the path . We must return a valid stat struct
* if it exists . JRA .
2002-10-11 05:19:00 +04:00
*/
2009-02-05 03:33:52 +03:00
if ( posix_pathnames ) {
ret = SMB_VFS_LSTAT ( conn , name , & st ) ;
} else {
ret = SMB_VFS_STAT ( conn , name , & st ) ;
}
if ( ret = = 0 ) {
2002-10-11 05:19:00 +04:00
* pst = st ;
} else {
2005-06-03 03:18:52 +04:00
SET_STAT_INVALID ( st ) ;
2002-10-11 05:19:00 +04:00
}
2002-07-15 14:35:28 +04:00
}
2007-07-12 02:39:11 +04:00
2007-09-08 00:57:01 +04:00
TALLOC_FREE ( found_name ) ;
2002-07-15 14:35:28 +04:00
} /* end else */
2007-01-20 00:46:12 +03:00
# ifdef DEVELOPER
2008-04-01 13:40:23 +04:00
/*
* This sucks !
* We should never provide different behaviors
* depending on DEVELOPER ! ! !
*/
if ( VALID_STAT ( st ) ) {
bool delete_pending ;
get_file_infos ( vfs_file_id_from_sbuf ( conn , & st ) ,
& delete_pending , NULL ) ;
if ( delete_pending ) {
result = NT_STATUS_DELETE_PENDING ;
goto fail ;
}
2007-01-20 00:46:12 +03:00
}
# endif
2007-08-31 01:46:42 +04:00
/*
2002-07-15 14:35:28 +04:00
* Add to the dirpath that we have resolved so far .
*/
2007-07-12 02:39:11 +04:00
if ( * dirpath ! = ' \0 ' ) {
2007-09-08 00:57:01 +04:00
char * tmp = talloc_asprintf ( ctx ,
" %s/%s " , dirpath , start ) ;
if ( ! tmp ) {
DEBUG ( 0 , ( " talloc_asprintf failed \n " ) ) ;
2007-07-12 02:39:11 +04:00
return NT_STATUS_NO_MEMORY ;
}
2007-09-08 00:57:01 +04:00
TALLOC_FREE ( dirpath ) ;
2007-07-12 02:39:11 +04:00
dirpath = tmp ;
}
else {
2007-09-08 00:57:01 +04:00
TALLOC_FREE ( dirpath ) ;
if ( ! ( dirpath = talloc_strdup ( ctx , start ) ) ) {
DEBUG ( 0 , ( " talloc_strdup failed \n " ) ) ;
2007-07-12 02:39:11 +04:00
return NT_STATUS_NO_MEMORY ;
}
}
2002-07-15 14:35:28 +04:00
/*
* Don ' t cache a name with mangled or wildcard components
* as this can change the size .
*/
2007-07-12 02:39:11 +04:00
2007-01-13 02:47:16 +03:00
if ( ! component_was_mangled & & ! name_has_wildcard ) {
2007-08-31 01:46:42 +04:00
stat_cache_add ( orig_path , dirpath ,
conn - > case_sensitive ) ;
2007-01-13 02:47:16 +03:00
}
2007-07-12 02:39:11 +04:00
/*
2002-07-15 14:35:28 +04:00
* Restore the / that we wiped out earlier .
*/
2007-01-13 02:47:16 +03:00
if ( end ) {
2002-07-15 14:35:28 +04:00
* end = ' / ' ;
2007-01-13 02:47:16 +03:00
}
2002-07-15 14:35:28 +04:00
}
2007-07-12 02:39:11 +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
2007-01-13 02:47:16 +03:00
if ( ! component_was_mangled & & ! name_has_wildcard ) {
2004-05-07 22:37:47 +04:00
stat_cache_add ( orig_path , name , conn - > case_sensitive ) ;
2007-01-13 02:47:16 +03:00
}
2000-12-12 03:11:34 +03:00
2007-07-12 02:39:11 +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 ) ) ;
2007-07-12 02:39:11 +04:00
done :
2008-01-20 01:25:36 +03:00
if ( stream ! = NULL ) {
char * tmp = NULL ;
result = build_stream_path ( ctx , conn , orig_path , name , stream ,
pst , & tmp ) ;
if ( ! NT_STATUS_IS_OK ( result ) ) {
goto fail ;
}
DEBUG ( 10 , ( " build_stream_path returned %s \n " , tmp ) ) ;
TALLOC_FREE ( name ) ;
name = tmp ;
}
2007-09-08 00:57:01 +04:00
* pp_conv_path = name ;
TALLOC_FREE ( dirpath ) ;
2007-08-20 00:00:43 +04:00
return NT_STATUS_OK ;
2007-08-16 19:44:13 +04:00
fail :
2007-08-20 00:00:43 +04:00
DEBUG ( 10 , ( " dirpath = [%s] start = [%s] \n " , dirpath , start ) ) ;
2007-09-08 00:57:01 +04:00
if ( * dirpath ! = ' \0 ' ) {
* pp_conv_path = talloc_asprintf ( ctx ,
" %s/%s " , dirpath , start ) ;
} else {
* pp_conv_path = talloc_strdup ( ctx , start ) ;
}
if ( ! * pp_conv_path ) {
DEBUG ( 0 , ( " talloc_asprintf failed \n " ) ) ;
return NT_STATUS_NO_MEMORY ;
}
TALLOC_FREE ( name ) ;
TALLOC_FREE ( dirpath ) ;
2007-07-12 02:39:11 +04:00
return result ;
1998-08-17 11:40:06 +04:00
}
/****************************************************************************
2007-07-09 13:43:41 +04:00
Check a filename - possibly calling check_reduced_name .
2002-07-15 14:35:28 +04:00
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
2007-09-08 00:57:01 +04:00
NTSTATUS check_name ( connection_struct * conn , const char * name )
1998-08-17 11:40:06 +04:00
{
2002-07-15 14:35:28 +04:00
if ( IS_VETO_PATH ( conn , name ) ) {
2004-03-06 01:32:45 +03:00
/* Is it not dot or dot dot. */
2007-08-31 01:46:42 +04:00
if ( ! ( ( name [ 0 ] = = ' . ' ) & & ( ! name [ 1 ] | |
( name [ 1 ] = = ' . ' & & ! name [ 2 ] ) ) ) ) {
DEBUG ( 5 , ( " check_name: file path name %s vetoed \n " ,
name ) ) ;
2007-01-17 05:09:37 +03:00
return map_nt_error_from_unix ( ENOENT ) ;
2002-07-15 14:35:28 +04:00
}
}
1998-08-17 11:40:06 +04:00
2004-09-01 02:52:05 +04:00
if ( ! lp_widelinks ( SNUM ( conn ) ) | | ! lp_symlinks ( SNUM ( conn ) ) ) {
2007-07-09 13:43:41 +04:00
NTSTATUS status = check_reduced_name ( conn , name ) ;
2007-01-17 05:09:37 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2007-08-31 01:46:42 +04:00
DEBUG ( 5 , ( " check_name: name %s failed with %s \n " , name ,
nt_errstr ( status ) ) ) ;
2007-01-17 05:09:37 +03:00
return status ;
}
2004-06-11 21:54:23 +04:00
}
1998-08-17 11:40:06 +04:00
2007-01-17 05:09:37 +03:00
return NT_STATUS_OK ;
1998-08-17 11:40:06 +04:00
}
2007-07-12 02:39:11 +04:00
/****************************************************************************
Check if two filenames are equal .
This needs to be careful about whether we are case sensitive .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-10-19 04:40:25 +04:00
static bool fname_equal ( const char * name1 , const char * name2 ,
bool case_sensitive )
2007-07-12 02:39:11 +04:00
{
/* Normal filename handling */
2007-09-08 00:57:01 +04:00
if ( case_sensitive ) {
2007-07-12 02:39:11 +04:00
return ( strcmp ( name1 , name2 ) = = 0 ) ;
2007-09-08 00:57:01 +04:00
}
2007-07-12 02:39:11 +04:00
return ( strequal ( name1 , name2 ) ) ;
}
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
2009-05-02 04:28:38 +04:00
static int get_real_filename_full_scan ( connection_struct * conn ,
const char * path , const char * name ,
TALLOC_CTX * mem_ctx , char * * found_name )
1998-08-17 11:40:06 +04:00
{
2009-04-23 16:54:52 +04:00
struct smb_Dir * cur_dir ;
const char * dname ;
2007-10-19 04:40:25 +04:00
bool mangled ;
2007-07-12 02:39:11 +04:00
char * unmangled_name = NULL ;
2009-04-23 16:54:52 +04:00
long curpos ;
2002-07-15 14:35:28 +04:00
2006-07-11 22:01:26 +04:00
mangled = mangle_is_mangled ( name , conn - > params ) ;
2002-07-15 14:35:28 +04:00
/* handle null paths */
2007-09-08 00:57:01 +04:00
if ( ( path = = NULL ) | | ( * path = = 0 ) ) {
2002-07-15 14:35:28 +04:00
path = " . " ;
2007-09-08 00:57:01 +04:00
}
2002-07-15 14:35:28 +04:00
2007-12-23 01:55:37 +03:00
/* If we have a case-sensitive filesystem, it doesn't do us any
* good to search for a name . If a case variation of the name was
* there , then the original stat ( 2 ) would have found it .
*/
if ( ! mangled & & ! ( conn - > fs_capabilities & FILE_CASE_SENSITIVE_SEARCH ) ) {
errno = ENOENT ;
2008-12-10 05:03:51 +03:00
return - 1 ;
2007-12-23 01:55:37 +03:00
}
2002-07-15 14:35:28 +04:00
/*
* The incoming name can be mangled , and if we de - mangle it
* here it will not compare correctly against the filename ( name2 )
2007-09-08 00:57:01 +04:00
* read from the directory and then mangled by the name_to_8_3 ( )
2002-07-15 14:35:28 +04:00
* call . We need to mangle both names or neither .
* ( JRA ) .
2005-10-28 05:42:03 +04:00
*
* Fix for bug found by Dina Fine . If in case sensitive mode then
* the mangle cache is no good ( 3 letter extension could be wrong
* case - so don ' t demangle in this case - leave as mangled and
* allow the mangling of the directory entry read ( which is done
* case insensitively ) to match instead . This will lead to more
* false positive matches but we fail completely without it . JRA .
2002-07-15 14:35:28 +04:00
*/
2005-10-28 05:42:03 +04:00
if ( mangled & & ! conn - > case_sensitive ) {
2008-12-09 15:40:41 +03:00
mangled = ! mangle_lookup_name_from_8_3 ( talloc_tos ( ) , name ,
& unmangled_name ,
conn - > params ) ;
2007-09-14 22:24:31 +04:00
if ( ! mangled ) {
/* Name is now unmangled. */
2007-09-08 00:57:01 +04:00
name = unmangled_name ;
}
2005-10-28 05:42:03 +04:00
}
2002-07-15 14:35:28 +04:00
/* open the directory */
2008-01-12 19:08:04 +03:00
if ( ! ( cur_dir = OpenDir ( talloc_tos ( ) , conn , path , NULL , 0 ) ) ) {
2002-07-15 14:35:28 +04:00
DEBUG ( 3 , ( " scan dir didn't open dir [%s] \n " , path ) ) ;
2007-09-08 00:57:01 +04:00
TALLOC_FREE ( unmangled_name ) ;
2008-12-10 05:03:51 +03:00
return - 1 ;
2002-07-15 14:35:28 +04:00
}
/* now scan for matching names */
2005-01-29 00:01:58 +03:00
curpos = 0 ;
2009-01-23 07:18:56 +03:00
while ( ( dname = ReadDirName ( cur_dir , & curpos , NULL ) ) ) {
2004-03-06 01:32:45 +03:00
/* Is it dot or dot dot. */
2007-09-08 00:57:01 +04:00
if ( ISDOT ( dname ) | | ISDOTDOT ( dname ) ) {
2002-07-15 14:35:28 +04:00
continue ;
2004-03-06 01:32:45 +03:00
}
2002-07-15 14:35:28 +04:00
/*
* At this point dname is the unmangled name .
2007-08-31 01:46:42 +04:00
* name is either mangled or not , depending on the state
* of the " mangled " variable . JRA .
2002-07-15 14:35:28 +04:00
*/
/*
* Check mangled name against mangled name , or unmangled name
* against unmangled name .
*/
2007-08-31 01:46:42 +04:00
if ( ( mangled & & mangled_equal ( name , dname , conn - > params ) ) | |
fname_equal ( name , dname , conn - > case_sensitive ) ) {
2002-07-15 14:35:28 +04:00
/* we've found the file, change it's name and return */
2008-12-09 15:40:41 +03:00
* found_name = talloc_strdup ( mem_ctx , dname ) ;
2007-09-08 00:57:01 +04:00
TALLOC_FREE ( unmangled_name ) ;
2008-01-12 19:08:04 +03:00
TALLOC_FREE ( cur_dir ) ;
2007-09-08 00:57:01 +04:00
if ( ! * found_name ) {
errno = ENOMEM ;
2008-12-10 05:03:51 +03:00
return - 1 ;
2007-09-08 00:57:01 +04:00
}
2008-12-10 05:03:51 +03:00
return 0 ;
2002-07-15 14:35:28 +04:00
}
}
2007-09-08 00:57:01 +04:00
TALLOC_FREE ( unmangled_name ) ;
2008-01-12 19:08:04 +03:00
TALLOC_FREE ( cur_dir ) ;
2004-06-11 21:54:23 +04:00
errno = ENOENT ;
2008-12-10 05:03:51 +03:00
return - 1 ;
1998-08-17 11:40:06 +04:00
}
2008-01-20 01:25:36 +03:00
2009-05-02 04:28:38 +04:00
/****************************************************************************
Wrapper around the vfs get_real_filename and the full directory scan
fallback .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int get_real_filename ( connection_struct * conn , const char * path ,
const char * name , TALLOC_CTX * mem_ctx ,
char * * found_name )
{
int ret ;
/* Try the vfs first to take advantage of case-insensitive stat. */
ret = SMB_VFS_GET_REAL_FILENAME ( conn , path , name , mem_ctx , found_name ) ;
/*
* If the case - insensitive stat was successful , or returned an error
* other than EOPNOTSUPP then there is no need to fall back on the
* full directory scan .
*/
if ( ret = = 0 | | ( ret = = - 1 & & errno ! = EOPNOTSUPP ) ) {
return ret ;
}
ret = get_real_filename_full_scan ( conn , path , name , mem_ctx ,
found_name ) ;
return ret ;
}
2008-01-20 01:25:36 +03:00
static NTSTATUS build_stream_path ( TALLOC_CTX * mem_ctx ,
connection_struct * conn ,
const char * orig_path ,
const char * basepath ,
const char * streamname ,
SMB_STRUCT_STAT * pst ,
char * * path )
{
SMB_STRUCT_STAT st ;
char * result = NULL ;
NTSTATUS status ;
unsigned int i , num_streams ;
struct stream_struct * streams = NULL ;
result = talloc_asprintf ( mem_ctx , " %s%s " , basepath , streamname ) ;
if ( result = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
if ( SMB_VFS_STAT ( conn , result , & st ) = = 0 ) {
* pst = st ;
* path = result ;
return NT_STATUS_OK ;
}
if ( errno ! = ENOENT ) {
status = map_nt_error_from_unix ( errno ) ;
DEBUG ( 10 , ( " vfs_stat failed: %s \n " , nt_errstr ( status ) ) ) ;
goto fail ;
}
status = SMB_VFS_STREAMINFO ( conn , NULL , basepath , mem_ctx ,
& num_streams , & streams ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_NAME_NOT_FOUND ) ) {
SET_STAT_INVALID ( * pst ) ;
* path = result ;
return NT_STATUS_OK ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " vfs_streaminfo failed: %s \n " , nt_errstr ( status ) ) ) ;
goto fail ;
}
for ( i = 0 ; i < num_streams ; i + + ) {
DEBUG ( 10 , ( " comparing [%s] and [%s]: " ,
streamname , streams [ i ] . name ) ) ;
if ( fname_equal ( streamname , streams [ i ] . name ,
conn - > case_sensitive ) ) {
DEBUGADD ( 10 , ( " equal \n " ) ) ;
break ;
}
DEBUGADD ( 10 , ( " not equal \n " ) ) ;
}
if ( i = = num_streams ) {
SET_STAT_INVALID ( * pst ) ;
* path = result ;
TALLOC_FREE ( streams ) ;
return NT_STATUS_OK ;
}
TALLOC_FREE ( result ) ;
result = talloc_asprintf ( mem_ctx , " %s%s " , basepath , streams [ i ] . name ) ;
if ( result = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
SET_STAT_INVALID ( * pst ) ;
if ( SMB_VFS_STAT ( conn , result , pst ) = = 0 ) {
stat_cache_add ( orig_path , result , conn - > case_sensitive ) ;
}
* path = result ;
TALLOC_FREE ( streams ) ;
return NT_STATUS_OK ;
fail :
TALLOC_FREE ( result ) ;
TALLOC_FREE ( streams ) ;
return status ;
}