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"
2011-02-26 01:20:06 +03:00
# include "system/filesys.h"
2010-08-18 18:44:47 +04:00
# include "fake_file.h"
2011-03-22 18:57:01 +03:00
# include "smbd/smbd.h"
2012-05-23 15:22:47 +04:00
# include "smbd/globals.h"
2023-08-01 16:57:50 +03:00
# include "libcli/smb/reparse.h"
2023-11-27 16:53:07 +03:00
# include "source3/smbd/dir.h"
1998-08-17 11:40:06 +04:00
2017-05-18 21:56:39 +03:00
uint32_t ucf_flags_from_smb_request ( struct smb_request * req )
2016-10-13 13:42:59 +03:00
{
uint32_t ucf_flags = 0 ;
2022-12-20 16:14:45 +03:00
if ( req = = NULL ) {
return 0 ;
}
if ( req - > posix_pathnames ) {
ucf_flags | = UCF_POSIX_PATHNAMES ;
2022-12-20 23:26:10 +03:00
2024-02-13 15:05:42 +03:00
if ( ! conn_using_smb2 ( req - > sconn ) ) {
2022-12-20 23:26:10 +03:00
ucf_flags | = UCF_LCOMP_LNK_OK ;
}
2022-12-20 16:14:45 +03:00
}
if ( req - > flags2 & FLAGS2_DFS_PATHNAMES ) {
ucf_flags | = UCF_DFS_PATHNAME ;
}
if ( req - > flags2 & FLAGS2_REPARSE_PATH ) {
ucf_flags | = UCF_GMT_PATHNAME ;
2016-10-13 13:42:59 +03:00
}
2017-05-18 21:56:39 +03:00
return ucf_flags ;
}
uint32_t filename_create_ucf_flags ( struct smb_request * req , uint32_t create_disposition )
{
uint32_t ucf_flags = 0 ;
ucf_flags | = ucf_flags_from_smb_request ( req ) ;
2016-10-13 13:42:59 +03:00
switch ( create_disposition ) {
case FILE_OPEN :
case FILE_OVERWRITE :
break ;
case FILE_SUPERSEDE :
case FILE_CREATE :
case FILE_OPEN_IF :
case FILE_OVERWRITE_IF :
ucf_flags | = UCF_PREP_CREATEFILE ;
break ;
}
return ucf_flags ;
}
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
}
2017-01-12 03:30:38 +03:00
/*
2020-05-04 22:53:21 +03:00
* Strip a valid @ GMT - token from any incoming filename path ,
* adding any NTTIME encoded in the pathname into the
* twrp field of the passed in smb_fname .
2017-01-12 03:30:38 +03:00
*
2020-05-04 22:53:21 +03:00
* Valid @ GMT - tokens look like @ GMT - YYYY - MM - DD - HH - MM - SS
* at the * start * of a pathname component .
2017-01-12 03:30:38 +03:00
*
2020-05-04 22:53:21 +03:00
* If twrp is passed in then smb_fname - > twrp is set to that
* value , and the @ GMT - token part of the filename is removed
* and does not change the stored smb_fname - > twrp .
2017-01-12 03:30:38 +03:00
*
*/
2020-05-02 13:55:33 +03:00
NTSTATUS canonicalize_snapshot_path ( struct smb_filename * smb_fname ,
2020-05-04 14:51:37 +03:00
uint32_t ucf_flags ,
2020-05-02 13:55:33 +03:00
NTTIME twrp )
2017-01-12 03:30:38 +03:00
{
2022-07-12 18:08:19 +03:00
bool found ;
2017-01-12 03:30:38 +03:00
2020-04-30 17:04:54 +03:00
if ( twrp ! = 0 ) {
smb_fname - > twrp = twrp ;
2020-04-30 16:14:36 +03:00
}
2020-05-04 14:51:37 +03:00
if ( ! ( ucf_flags & UCF_GMT_PATHNAME ) ) {
return NT_STATUS_OK ;
}
2022-08-04 20:02:15 +03:00
found = extract_snapshot_token ( smb_fname - > base_name , & twrp ) ;
2022-07-12 18:08:19 +03:00
if ( ! found ) {
2017-01-12 03:30:38 +03:00
return NT_STATUS_OK ;
}
2020-04-30 16:24:44 +03:00
if ( smb_fname - > twrp = = 0 ) {
2022-07-12 18:08:19 +03:00
smb_fname - > twrp = twrp ;
2020-04-30 16:24:44 +03:00
}
2020-04-30 16:17:11 +03:00
2020-04-30 15:13:44 +03:00
return NT_STATUS_OK ;
2017-01-12 03:30:38 +03:00
}
2022-02-17 13:24:38 +03:00
static bool strnorm ( char * s , int case_default )
{
if ( case_default = = CASE_UPPER )
return strupper_m ( s ) ;
else
return strlower_m ( s ) ;
}
2020-03-26 22:35:26 +03:00
/*
* Utility function to normalize case on an incoming client filename
* if required on this connection struct .
* Performs an in - place case conversion guaranteed to stay the same size .
*/
2021-09-18 03:02:06 +03:00
static NTSTATUS normalize_filename_case ( connection_struct * conn ,
char * filename ,
uint32_t ucf_flags )
2020-03-26 22:35:26 +03:00
{
bool ok ;
2021-10-15 21:54:38 +03:00
if ( ucf_flags & UCF_POSIX_PATHNAMES ) {
/*
* POSIX never normalizes filename case .
*/
return NT_STATUS_OK ;
}
2020-03-26 22:35:26 +03:00
if ( ! conn - > case_sensitive ) {
return NT_STATUS_OK ;
}
if ( conn - > case_preserve ) {
return NT_STATUS_OK ;
}
if ( conn - > short_case_preserve ) {
return NT_STATUS_OK ;
}
ok = strnorm ( filename , lp_default_case ( SNUM ( conn ) ) ) ;
if ( ! ok ) {
return NT_STATUS_INVALID_PARAMETER ;
}
return NT_STATUS_OK ;
}
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 ) ) ;
}
2019-04-25 18:30:43 +03:00
static bool sname_equal ( const char * name1 , const char * name2 ,
bool case_sensitive )
{
bool match ;
const char * s1 = NULL ;
const char * s2 = NULL ;
size_t n1 ;
size_t n2 ;
const char * e1 = NULL ;
const char * e2 = NULL ;
char * c1 = NULL ;
char * c2 = NULL ;
match = fname_equal ( name1 , name2 , case_sensitive ) ;
if ( match ) {
return true ;
}
if ( name1 [ 0 ] ! = ' : ' ) {
return false ;
}
if ( name2 [ 0 ] ! = ' : ' ) {
return false ;
}
s1 = & name1 [ 1 ] ;
e1 = strchr ( s1 , ' : ' ) ;
if ( e1 = = NULL ) {
n1 = strlen ( s1 ) ;
} else {
n1 = PTR_DIFF ( e1 , s1 ) ;
}
s2 = & name2 [ 1 ] ;
e2 = strchr ( s2 , ' : ' ) ;
if ( e2 = = NULL ) {
n2 = strlen ( s2 ) ;
} else {
n2 = PTR_DIFF ( e2 , s2 ) ;
}
/* Normal filename handling */
if ( case_sensitive ) {
return ( strncmp ( s1 , s2 , n1 ) = = 0 ) ;
}
/*
* We can ' t use strnequal ( ) here
* as it takes the number of codepoints
* and not the number of bytes .
*
* So we make a copy before calling
* strequal ( ) .
*
* Note that we TALLOC_FREE ( ) in reverse order
* in order to avoid memory fragmentation .
*/
c1 = talloc_strndup ( talloc_tos ( ) , s1 , n1 ) ;
c2 = talloc_strndup ( talloc_tos ( ) , s2 , n2 ) ;
if ( c1 = = NULL | | c2 = = NULL ) {
TALLOC_FREE ( c2 ) ;
TALLOC_FREE ( c1 ) ;
return ( strncmp ( s1 , s2 , n1 ) = = 0 ) ;
}
match = strequal ( c1 , c2 ) ;
TALLOC_FREE ( c2 ) ;
TALLOC_FREE ( c1 ) ;
return match ;
}
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
2022-03-13 17:55:25 +03:00
NTSTATUS get_real_filename_full_scan_at ( struct files_struct * dirfsp ,
const char * name ,
bool mangled ,
TALLOC_CTX * mem_ctx ,
char * * found_name )
1998-08-17 11:40:06 +04:00
{
2022-03-13 17:55:25 +03:00
struct connection_struct * conn = dirfsp - > conn ;
2022-02-21 19:30:29 +03:00
struct smb_Dir * cur_dir = NULL ;
2009-11-16 11:49:23 +03:00
const char * dname = NULL ;
char * talloced = NULL ;
2007-07-12 02:39:11 +04:00
char * unmangled_name = NULL ;
2022-02-21 19:43:17 +03:00
NTSTATUS status ;
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 ) ) {
2022-03-07 20:00:20 +03:00
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
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 */
2022-03-13 17:55:25 +03:00
status = OpenDir_from_pathref ( talloc_tos ( ) , dirfsp , NULL , 0 , & cur_dir ) ;
2022-02-21 19:43:17 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_NOTICE ( " scan dir didn't open dir [%s]: %s \n " ,
2022-03-13 17:55:25 +03:00
fsp_str_dbg ( dirfsp ) ,
2022-02-21 19:43:17 +03:00
nt_errstr ( status ) ) ;
2007-09-08 00:57:01 +04:00
TALLOC_FREE ( unmangled_name ) ;
2022-03-07 20:00:20 +03:00
return status ;
2002-07-15 14:35:28 +04:00
}
/* now scan for matching names */
2023-06-20 13:25:45 +03:00
while ( ( dname = ReadDirName ( cur_dir , & talloced ) ) ) {
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 ) ) {
2009-11-16 11:49:23 +03:00
TALLOC_FREE ( talloced ) ;
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 ) {
2009-11-16 11:49:23 +03:00
TALLOC_FREE ( talloced ) ;
2022-03-07 20:00:20 +03:00
return NT_STATUS_NO_MEMORY ;
2007-09-08 00:57:01 +04:00
}
2009-11-16 11:49:23 +03:00
TALLOC_FREE ( talloced ) ;
2022-03-07 20:00:20 +03:00
return NT_STATUS_OK ;
2002-07-15 14:35:28 +04:00
}
2009-11-16 11:49:23 +03:00
TALLOC_FREE ( talloced ) ;
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 ) ;
2022-03-07 20:00:20 +03:00
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
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 .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-07-14 20:37:30 +03:00
NTSTATUS get_real_filename_at ( struct files_struct * dirfsp ,
const char * name ,
TALLOC_CTX * mem_ctx ,
char * * found_name )
2009-05-02 04:28:38 +04:00
{
2022-03-13 18:31:20 +03:00
struct connection_struct * conn = dirfsp - > conn ;
2022-03-07 20:00:20 +03:00
NTSTATUS status ;
2009-05-09 13:12:52 +04:00
bool mangled ;
mangled = mangle_is_mangled ( name , conn - > params ) ;
if ( mangled ) {
2022-03-13 18:31:20 +03:00
status = get_real_filename_full_scan_at (
dirfsp , name , mangled , mem_ctx , found_name ) ;
return status ;
2009-05-09 13:12:52 +04:00
}
2009-05-02 04:28:38 +04:00
/* Try the vfs first to take advantage of case-insensitive stat. */
2022-03-13 18:31:20 +03:00
status = SMB_VFS_GET_REAL_FILENAME_AT (
dirfsp - > conn , dirfsp , name , mem_ctx , found_name ) ;
2009-05-02 04:28:38 +04:00
/*
* 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 .
*/
2022-03-07 20:00:20 +03:00
if ( NT_STATUS_IS_OK ( status ) | |
! NT_STATUS_EQUAL ( status , NT_STATUS_NOT_SUPPORTED ) ) {
return status ;
2009-05-02 04:28:38 +04:00
}
2022-03-13 18:31:20 +03:00
status = get_real_filename_full_scan_at (
dirfsp , name , mangled , mem_ctx , found_name ) ;
return status ;
}
2020-03-25 20:00:57 +03:00
/*
* Lightweight function to just get last component
* for rename / enumerate directory calls .
*/
char * get_original_lcomp ( TALLOC_CTX * ctx ,
connection_struct * conn ,
const char * filename_in ,
uint32_t ucf_flags )
{
char * last_slash = NULL ;
char * orig_lcomp ;
NTSTATUS status ;
last_slash = strrchr ( filename_in , ' / ' ) ;
if ( last_slash ! = NULL ) {
orig_lcomp = talloc_strdup ( ctx , last_slash + 1 ) ;
} else {
orig_lcomp = talloc_strdup ( ctx , filename_in ) ;
}
if ( orig_lcomp = = NULL ) {
return NULL ;
}
2021-09-18 03:02:06 +03:00
status = normalize_filename_case ( conn , orig_lcomp , ucf_flags ) ;
2020-03-25 20:00:57 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( orig_lcomp ) ;
return NULL ;
}
return orig_lcomp ;
}
2021-12-08 09:08:10 +03:00
/*
* Get the correct capitalized stream name hanging off
* base_fsp . Equivalent of get_real_filename ( ) , but for streams .
*/
static NTSTATUS get_real_stream_name (
TALLOC_CTX * mem_ctx ,
struct files_struct * base_fsp ,
const char * stream_name ,
char * * _found )
{
unsigned int i , num_streams = 0 ;
struct stream_struct * streams = NULL ;
NTSTATUS status ;
status = vfs_fstreaminfo (
base_fsp , talloc_tos ( ) , & num_streams , & streams ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
for ( i = 0 ; i < num_streams ; i + + ) {
bool equal = sname_equal ( stream_name , streams [ i ] . name , false ) ;
DBG_DEBUG ( " comparing [%s] and [%s]: %sequal \n " ,
stream_name ,
streams [ i ] . name ,
equal ? " " : " not " ) ;
if ( equal ) {
* _found = talloc_move ( mem_ctx , & streams [ i ] . name ) ;
TALLOC_FREE ( streams ) ;
return NT_STATUS_OK ;
}
}
TALLOC_FREE ( streams ) ;
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
static bool filename_split_lcomp (
TALLOC_CTX * mem_ctx ,
const char * name_in ,
bool posix ,
char * * _dirname ,
const char * * _fname_rel ,
const char * * _streamname )
{
const char * lcomp = NULL ;
const char * fname_rel = NULL ;
const char * streamname = NULL ;
char * dirname = NULL ;
if ( name_in [ 0 ] = = ' \0 ' ) {
fname_rel = " . " ;
dirname = talloc_strdup ( mem_ctx , " " ) ;
if ( dirname = = NULL ) {
return false ;
}
goto done ;
}
lcomp = strrchr_m ( name_in , ' / ' ) ;
if ( lcomp ! = NULL ) {
fname_rel = lcomp + 1 ;
dirname = talloc_strndup ( mem_ctx , name_in , lcomp - name_in ) ;
if ( dirname = = NULL ) {
return false ;
}
goto find_stream ;
}
/*
2023-07-18 12:30:18 +03:00
* No slash , dir is empty
2021-12-08 09:08:10 +03:00
*/
dirname = talloc_strdup ( mem_ctx , " " ) ;
if ( dirname = = NULL ) {
return false ;
}
2022-07-28 02:52:40 +03:00
if ( ! posix & & ( name_in [ 0 ] = = ' : ' ) ) {
2021-12-08 09:08:10 +03:00
/*
* Special case for stream on root directory
*/
fname_rel = " . " ;
streamname = name_in ;
goto done ;
}
fname_rel = name_in ;
find_stream :
if ( ! posix ) {
streamname = strchr_m ( fname_rel , ' : ' ) ;
if ( streamname ! = NULL ) {
fname_rel = talloc_strndup (
mem_ctx ,
fname_rel ,
streamname - fname_rel ) ;
if ( fname_rel = = NULL ) {
TALLOC_FREE ( dirname ) ;
return false ;
}
}
}
done :
* _dirname = dirname ;
* _fname_rel = fname_rel ;
* _streamname = streamname ;
return true ;
}
/*
* Create the correct capitalization of a file name to be created .
*/
static NTSTATUS filename_convert_normalize_new (
TALLOC_CTX * mem_ctx ,
struct connection_struct * conn ,
char * name_in ,
char * * _normalized )
{
char * name = name_in ;
* _normalized = NULL ;
if ( ! conn - > case_preserve | |
( mangle_is_8_3 ( name , false ,
conn - > params ) & &
! conn - > short_case_preserve ) ) {
char * normalized = talloc_strdup ( mem_ctx , name ) ;
if ( normalized = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
strnorm ( normalized , lp_default_case ( SNUM ( conn ) ) ) ;
name = normalized ;
}
if ( mangle_is_mangled ( name , conn - > params ) ) {
bool found ;
char * unmangled = NULL ;
found = mangle_lookup_name_from_8_3 (
mem_ctx , name , & unmangled , conn - > params ) ;
if ( found ) {
name = unmangled ;
}
}
if ( name ! = name_in ) {
* _normalized = name ;
}
return NT_STATUS_OK ;
}
2022-07-14 20:47:23 +03:00
static const char * previous_slash ( const char * name_in , const char * slash )
{
2023-10-24 14:18:32 +03:00
const char * prev = NULL ;
2022-07-14 20:47:23 +03:00
2023-10-24 14:18:32 +03:00
SMB_ASSERT ( ( name_in < = slash ) & & ( slash [ 0 ] = = ' / ' ) ) ;
2022-07-14 20:47:23 +03:00
2023-10-24 14:18:32 +03:00
prev = strchr_m ( name_in , ' / ' ) ;
2022-07-14 20:47:23 +03:00
2023-10-24 14:18:32 +03:00
if ( prev = = slash ) {
/* No previous slash */
return NULL ;
}
2022-07-14 20:47:23 +03:00
2023-10-24 14:18:32 +03:00
while ( true ) {
const char * next = strchr_m ( prev + 1 , ' / ' ) ;
2022-07-14 20:47:23 +03:00
2023-10-24 14:18:32 +03:00
if ( next = = slash ) {
return prev ;
}
prev = next ;
2022-07-14 20:47:23 +03:00
}
2023-10-24 14:18:32 +03:00
return NULL ; /* unreachable */
2022-07-14 20:47:23 +03:00
}
static char * symlink_target_path (
TALLOC_CTX * mem_ctx ,
const char * name_in ,
const char * substitute ,
size_t unparsed )
{
size_t name_in_len = strlen ( name_in ) ;
const char * p_unparsed = NULL ;
const char * parent = NULL ;
char * ret ;
SMB_ASSERT ( unparsed < = name_in_len ) ;
p_unparsed = name_in + ( name_in_len - unparsed ) ;
if ( substitute [ 0 ] = = ' / ' ) {
ret = talloc_asprintf ( mem_ctx , " %s%s " , substitute , p_unparsed ) ;
return ret ;
}
if ( unparsed = = 0 ) {
parent = strrchr_m ( name_in , ' / ' ) ;
} else {
parent = previous_slash ( name_in , p_unparsed ) ;
}
if ( parent = = NULL ) {
2023-10-24 14:18:32 +03:00
ret = talloc_asprintf ( mem_ctx , " %s%s " , substitute , p_unparsed ) ;
} else {
ret = talloc_asprintf ( mem_ctx ,
" %.*s/%s%s " ,
( int ) ( parent - name_in ) ,
name_in ,
substitute ,
p_unparsed ) ;
2022-07-14 20:47:23 +03:00
}
return ret ;
}
2024-01-02 14:49:14 +03:00
NTSTATUS safe_symlink_target_path ( TALLOC_CTX * mem_ctx ,
const char * connectpath ,
2024-01-02 15:25:25 +03:00
const char * dir ,
2024-01-02 14:49:14 +03:00
const char * target ,
size_t unparsed ,
char * * _relative )
2022-10-25 11:26:26 +03:00
{
char * abs_target = NULL ;
char * abs_target_canon = NULL ;
const char * relative = NULL ;
bool in_share ;
2024-01-02 14:49:14 +03:00
NTSTATUS status = NT_STATUS_NO_MEMORY ;
2022-10-25 11:26:26 +03:00
2024-01-02 14:49:14 +03:00
DBG_DEBUG ( " connectpath [%s] target [%s] unparsed [%zu] \n " ,
connectpath , target , unparsed ) ;
2022-10-25 11:26:26 +03:00
if ( target [ 0 ] = = ' / ' ) {
2024-01-02 14:49:14 +03:00
abs_target = talloc_strdup ( mem_ctx , target ) ;
2024-01-02 15:25:25 +03:00
} else if ( dir = = NULL ) {
abs_target = talloc_asprintf ( mem_ctx ,
" %s/%s " ,
connectpath ,
target ) ;
} else if ( dir [ 0 ] = = ' / ' ) {
2024-01-02 14:49:14 +03:00
abs_target = talloc_asprintf ( mem_ctx ,
" %s/%s " ,
2024-01-02 15:25:25 +03:00
dir ,
target ) ;
} else {
abs_target = talloc_asprintf ( mem_ctx ,
" %s/%s/%s " ,
2024-01-02 14:49:14 +03:00
connectpath ,
2024-01-02 15:25:25 +03:00
dir ,
2024-01-02 14:49:14 +03:00
target ) ;
}
if ( abs_target = = NULL ) {
goto fail ;
2022-10-25 11:26:26 +03:00
}
2024-01-02 14:49:14 +03:00
abs_target_canon = canonicalize_absolute_path ( abs_target , abs_target ) ;
2022-10-25 11:26:26 +03:00
if ( abs_target_canon = = NULL ) {
goto fail ;
}
DBG_DEBUG ( " abs_target_canon=%s \n " , abs_target_canon ) ;
in_share = subdir_of (
connectpath , strlen ( connectpath ) , abs_target_canon , & relative ) ;
if ( ! in_share ) {
DBG_DEBUG ( " wide link to %s \n " , abs_target_canon ) ;
2023-10-24 19:47:59 +03:00
status = ( unparsed ! = 0 ) ? NT_STATUS_OBJECT_PATH_NOT_FOUND
: NT_STATUS_OBJECT_NAME_NOT_FOUND ;
2022-10-25 11:26:26 +03:00
goto fail ;
}
2024-01-02 14:49:14 +03:00
* _relative = talloc_strdup ( mem_ctx , relative ) ;
if ( * _relative = = NULL ) {
2022-10-25 11:26:26 +03:00
goto fail ;
}
status = NT_STATUS_OK ;
fail :
2024-01-02 14:49:14 +03:00
TALLOC_FREE ( abs_target ) ;
2022-10-25 11:26:26 +03:00
return status ;
}
2022-07-14 20:47:23 +03:00
/*
* Split up name_in as sent by the client into a directory pathref fsp
* and a relative smb_filename .
*/
static NTSTATUS filename_convert_dirfsp_nosymlink (
2021-12-08 09:08:10 +03:00
TALLOC_CTX * mem_ctx ,
connection_struct * conn ,
const char * name_in ,
uint32_t ucf_flags ,
NTTIME twrp ,
struct files_struct * * _dirfsp ,
2022-07-14 20:47:23 +03:00
struct smb_filename * * _smb_fname ,
2024-02-04 20:07:19 +03:00
struct reparse_data_buffer * * _symlink_err )
2021-12-08 09:08:10 +03:00
{
struct smb_filename * smb_dirname = NULL ;
struct smb_filename * smb_fname_rel = NULL ;
struct smb_filename * smb_fname = NULL ;
2024-02-04 20:07:19 +03:00
struct reparse_data_buffer * symlink_err = NULL ;
2021-12-08 09:08:10 +03:00
const bool posix = ( ucf_flags & UCF_POSIX_PATHNAMES ) ;
char * dirname = NULL ;
const char * fname_rel = NULL ;
const char * streamname = NULL ;
char * saved_streamname = NULL ;
struct files_struct * base_fsp = NULL ;
bool ok ;
NTSTATUS status = NT_STATUS_UNSUCCESSFUL ;
2023-03-30 17:22:31 +03:00
SMB_ASSERT ( ! ( ucf_flags & UCF_DFS_PATHNAME ) ) ;
2021-12-08 09:08:10 +03:00
2022-08-03 04:13:52 +03:00
if ( is_fake_file_path ( name_in ) ) {
2021-12-08 09:08:10 +03:00
smb_fname = synthetic_smb_fname_split ( mem_ctx , name_in , posix ) ;
if ( smb_fname = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2023-10-23 17:40:55 +03:00
smb_fname - > st = ( SMB_STRUCT_STAT ) {
. st_ex_nlink = 1 ,
. st_ex_mode = S_IFREG | 0644 ,
} ;
2021-12-08 09:08:10 +03:00
smb_fname - > st . st_ex_btime =
( struct timespec ) { 0 , SAMBA_UTIME_OMIT } ;
smb_fname - > st . st_ex_atime =
( struct timespec ) { 0 , SAMBA_UTIME_OMIT } ;
smb_fname - > st . st_ex_mtime =
( struct timespec ) { 0 , SAMBA_UTIME_OMIT } ;
smb_fname - > st . st_ex_ctime =
( struct timespec ) { 0 , SAMBA_UTIME_OMIT } ;
* _dirfsp = conn - > cwd_fsp ;
* _smb_fname = smb_fname ;
return NT_STATUS_OK ;
}
2022-07-28 01:28:13 +03:00
/*
* Catch an invalid path of " . " before we
* call filename_split_lcomp ( ) . We need to
* do this as filename_split_lcomp ( ) will
* use " . " for the missing relative component
* when an empty name_in path is sent by
* the client .
*/
if ( ISDOT ( name_in ) ) {
status = NT_STATUS_OBJECT_NAME_INVALID ;
goto fail ;
}
2021-12-08 09:08:10 +03:00
ok = filename_split_lcomp (
talloc_tos ( ) ,
name_in ,
posix ,
& dirname ,
& fname_rel ,
& streamname ) ;
if ( ! ok ) {
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
2022-09-02 12:46:53 +03:00
if ( ( streamname ! = NULL ) & &
( ( conn - > fs_capabilities & FILE_NAMED_STREAMS ) = = 0 ) ) {
status = NT_STATUS_OBJECT_NAME_INVALID ;
goto fail ;
}
2021-12-08 09:08:10 +03:00
if ( ! posix ) {
bool name_has_wild = ms_has_wild ( dirname ) ;
name_has_wild | = ms_has_wild ( fname_rel ) ;
if ( name_has_wild ) {
status = NT_STATUS_OBJECT_NAME_INVALID ;
goto fail ;
}
}
2022-07-14 20:47:23 +03:00
if ( dirname [ 0 ] = = ' \0 ' ) {
status = synthetic_pathref (
mem_ctx ,
conn - > cwd_fsp ,
" . " ,
NULL ,
NULL ,
0 ,
posix ? SMB_FILENAME_POSIX_PATH : 0 ,
& smb_dirname ) ;
} else {
2023-02-17 12:02:37 +03:00
status = normalize_filename_case ( conn , dirname , ucf_flags ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " normalize_filename_case %s failed: %s \n " ,
dirname ,
nt_errstr ( status ) ) ;
goto fail ;
}
2022-12-12 16:04:00 +03:00
status = openat_pathref_fsp_nosymlink ( mem_ctx ,
conn ,
conn - > cwd_fsp ,
dirname ,
2023-12-28 16:38:37 +03:00
twrp ,
2022-12-12 16:04:00 +03:00
posix ,
& smb_dirname ,
& symlink_err ) ;
2022-07-14 20:47:23 +03:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_STOPPED_ON_SYMLINK ) ) {
2024-02-04 19:42:28 +03:00
struct symlink_reparse_struct
2024-02-04 20:07:19 +03:00
* lnk = & symlink_err - > parsed . lnk ;
2024-02-04 19:42:28 +03:00
size_t unparsed = lnk - > unparsed_path_length ;
2022-12-12 16:04:00 +03:00
size_t name_in_len , dirname_len ;
name_in_len = strlen ( name_in ) ;
dirname_len = strlen ( dirname ) ;
2022-07-14 20:47:23 +03:00
SMB_ASSERT ( name_in_len > = dirname_len ) ;
2024-02-04 19:42:28 +03:00
unparsed + = ( name_in_len - dirname_len ) ;
if ( unparsed > UINT16_MAX ) {
status = NT_STATUS_BUFFER_OVERFLOW ;
goto fail ;
}
lnk - > unparsed_path_length = unparsed ;
2022-12-16 18:35:00 +03:00
* _symlink_err = symlink_err ;
2022-07-14 20:47:23 +03:00
goto fail ;
}
}
2021-12-08 09:08:10 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-07-14 20:47:23 +03:00
DBG_DEBUG ( " opening directory %s failed: %s \n " ,
2021-12-08 09:08:10 +03:00
dirname ,
nt_errstr ( status ) ) ;
TALLOC_FREE ( dirname ) ;
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_ACCESS_DENIED ) ) {
/*
* Except ACCESS_DENIED , everything else leads
* to PATH_NOT_FOUND .
*/
status = NT_STATUS_OBJECT_PATH_NOT_FOUND ;
}
goto fail ;
}
if ( ! VALID_STAT_OF_DIR ( smb_dirname - > st ) ) {
status = NT_STATUS_OBJECT_PATH_NOT_FOUND ;
goto fail ;
}
2022-12-12 16:04:00 +03:00
smb_dirname - > fsp - > fsp_flags . is_directory = true ;
2021-12-08 09:08:10 +03:00
2022-07-28 01:28:13 +03:00
/*
* Only look at bad last component values
* once we know we have a valid directory . That
* way we won ' t confuse error messages from
* opening the directory path with error
* messages from a bad last component .
*/
/* Relative filename can't be empty */
if ( fname_rel [ 0 ] = = ' \0 ' ) {
status = NT_STATUS_OBJECT_NAME_INVALID ;
goto fail ;
}
/* Relative filename can't be ".." */
if ( ISDOTDOT ( fname_rel ) ) {
status = NT_STATUS_OBJECT_NAME_INVALID ;
goto fail ;
}
/* Relative name can only be dot if directory is empty. */
if ( ISDOT ( fname_rel ) & & dirname [ 0 ] ! = ' \0 ' ) {
status = NT_STATUS_OBJECT_NAME_INVALID ;
goto fail ;
}
TALLOC_FREE ( dirname ) ;
2021-12-08 09:08:10 +03:00
smb_fname_rel = synthetic_smb_fname (
mem_ctx ,
fname_rel ,
streamname ,
NULL ,
twrp ,
posix ? SMB_FILENAME_POSIX_PATH : 0 ) ;
if ( smb_fname_rel = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
if ( ( conn - > fs_capabilities & FILE_NAMED_STREAMS ) & &
is_named_stream ( smb_fname_rel ) ) {
/*
* Find the base_fsp first without the stream .
*/
saved_streamname = smb_fname_rel - > stream_name ;
smb_fname_rel - > stream_name = NULL ;
}
status = normalize_filename_case (
conn , smb_fname_rel - > base_name , ucf_flags ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " normalize_filename_case %s failed: %s \n " ,
smb_fname_rel - > base_name ,
nt_errstr ( status ) ) ;
goto fail ;
}
2023-10-18 12:50:20 +03:00
status = openat_pathref_fsp_lcomp ( smb_dirname - > fsp ,
smb_fname_rel ,
ucf_flags ) ;
2021-12-08 09:08:10 +03:00
2023-10-18 12:50:20 +03:00
if ( NT_STATUS_IS_OK ( status ) & & S_ISLNK ( smb_fname_rel - > st . st_ex_mode ) ) {
2021-12-08 09:08:10 +03:00
2022-12-20 16:38:02 +03:00
/*
2023-10-18 12:50:20 +03:00
* Upper layers might need the link target . Here we
* still have the relname around , get the symlink err .
2022-12-20 16:38:02 +03:00
*/
2024-02-04 20:07:19 +03:00
status = read_symlink_reparse ( mem_ctx ,
smb_dirname - > fsp ,
smb_fname_rel ,
& symlink_err ) ;
2023-10-18 12:50:20 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " Could not read symlink for %s: %s \n " ,
smb_fname_str_dbg (
smb_fname_rel - > fsp - > fsp_name ) ,
nt_errstr ( status ) ) ;
2022-12-20 16:38:02 +03:00
goto fail ;
}
}
2022-12-20 16:40:26 +03:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_NAME_NOT_FOUND ) & &
! VALID_STAT ( smb_fname_rel - > st ) ) {
2022-12-20 16:38:02 +03:00
char * normalized = NULL ;
2021-12-08 09:08:10 +03:00
/*
* Creating a new file
*/
status = filename_convert_normalize_new (
smb_fname_rel ,
conn ,
smb_fname_rel - > base_name ,
& normalized ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " filename_convert_normalize_new failed: "
" %s \n " ,
nt_errstr ( status ) ) ;
goto fail ;
}
if ( normalized ! = NULL ) {
smb_fname_rel - > base_name = normalized ;
}
smb_fname_rel - > stream_name = saved_streamname ;
smb_fname = full_path_from_dirfsp_atname (
mem_ctx , smb_dirname - > fsp , smb_fname_rel ) ;
if ( smb_fname = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
goto done ;
}
2023-04-05 12:03:52 +03:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NETWORK_OPEN_RESTRICTION ) ) {
/* A vetoed file, pretend it's not there */
status = NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
2021-12-08 09:08:10 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
if ( saved_streamname = = NULL ) {
2022-07-27 00:34:27 +03:00
/* smb_fname must be allocated off mem_ctx. */
smb_fname = cp_smb_filename ( mem_ctx ,
smb_fname_rel - > fsp - > fsp_name ) ;
if ( smb_fname = = NULL ) {
goto fail ;
}
status = move_smb_fname_fsp_link ( smb_fname , smb_fname_rel ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
2021-12-08 09:08:10 +03:00
goto done ;
}
base_fsp = smb_fname_rel - > fsp ;
smb_fname_fsp_unlink ( smb_fname_rel ) ;
SET_STAT_INVALID ( smb_fname_rel - > st ) ;
smb_fname_rel - > stream_name = saved_streamname ;
2022-06-13 18:31:16 +03:00
status = open_stream_pathref_fsp ( & base_fsp , smb_fname_rel ) ;
2021-12-08 09:08:10 +03:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_NAME_NOT_FOUND ) & &
! conn - > case_sensitive ) {
char * found = NULL ;
status = get_real_stream_name (
smb_fname_rel ,
base_fsp ,
smb_fname_rel - > stream_name ,
& found ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
smb_fname_rel - > stream_name = found ;
found = NULL ;
status = open_stream_pathref_fsp (
2022-06-13 18:31:16 +03:00
& base_fsp , smb_fname_rel ) ;
2021-12-08 09:08:10 +03:00
}
}
if ( NT_STATUS_IS_OK ( status ) ) {
2022-07-27 00:34:27 +03:00
/* smb_fname must be allocated off mem_ctx. */
smb_fname = cp_smb_filename ( mem_ctx ,
smb_fname_rel - > fsp - > fsp_name ) ;
if ( smb_fname = = NULL ) {
goto fail ;
}
status = move_smb_fname_fsp_link ( smb_fname , smb_fname_rel ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
2021-12-08 09:08:10 +03:00
goto done ;
}
2022-08-01 21:40:14 +03:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_NAME_NOT_FOUND ) ) {
2021-12-08 09:08:10 +03:00
/*
* Creating a new stream
*
* We should save the already - open base fsp for
* create_file_unixpath ( ) somehow .
*/
smb_fname = full_path_from_dirfsp_atname (
mem_ctx , smb_dirname - > fsp , smb_fname_rel ) ;
if ( smb_fname = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
2023-02-28 22:20:12 +03:00
/*
* When open_stream_pathref_fsp ( ) returns
* NT_STATUS_OBJECT_NAME_NOT_FOUND , smb_fname_rel - > fsp
* has been set to NULL , so we must free base_fsp separately
* to prevent fd - leaks when opening a stream that doesn ' t
* exist .
*/
fd_close ( base_fsp ) ;
file_free ( NULL , base_fsp ) ;
base_fsp = NULL ;
2021-12-08 09:08:10 +03:00
goto done ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
done :
* _dirfsp = smb_dirname - > fsp ;
* _smb_fname = smb_fname ;
2023-10-18 12:50:20 +03:00
* _symlink_err = symlink_err ;
2021-12-08 09:08:10 +03:00
smb_fname_fsp_unlink ( smb_fname_rel ) ;
TALLOC_FREE ( smb_fname_rel ) ;
return NT_STATUS_OK ;
fail :
2023-02-28 22:20:12 +03:00
/*
* If open_stream_pathref_fsp ( ) returns an error , smb_fname_rel - > fsp
* has been set to NULL , so we must free base_fsp separately
* to prevent fd - leaks when opening a stream that doesn ' t
* exist .
*/
if ( base_fsp ! = NULL ) {
fd_close ( base_fsp ) ;
file_free ( NULL , base_fsp ) ;
base_fsp = NULL ;
}
2022-07-28 01:28:13 +03:00
TALLOC_FREE ( dirname ) ;
2021-12-08 09:08:10 +03:00
TALLOC_FREE ( smb_dirname ) ;
TALLOC_FREE ( smb_fname_rel ) ;
return status ;
}
2022-07-14 20:47:23 +03:00
NTSTATUS filename_convert_dirfsp (
TALLOC_CTX * mem_ctx ,
connection_struct * conn ,
const char * name_in ,
uint32_t ucf_flags ,
NTTIME twrp ,
struct files_struct * * _dirfsp ,
struct smb_filename * * _smb_fname )
{
2024-02-04 20:07:19 +03:00
struct reparse_data_buffer * symlink_err = NULL ;
2024-02-04 18:07:22 +03:00
struct symlink_reparse_struct * lnk = NULL ;
2022-07-14 20:47:23 +03:00
NTSTATUS status ;
char * target = NULL ;
2024-01-02 14:49:14 +03:00
char * safe_target = NULL ;
2022-07-14 20:47:23 +03:00
size_t symlink_redirects = 0 ;
next :
if ( symlink_redirects > 40 ) {
return NT_STATUS_OBJECT_PATH_NOT_FOUND ;
}
2022-12-16 18:35:00 +03:00
status = filename_convert_dirfsp_nosymlink ( mem_ctx ,
conn ,
name_in ,
ucf_flags ,
twrp ,
_dirfsp ,
_smb_fname ,
& symlink_err ) ;
2022-07-14 20:47:23 +03:00
2023-10-18 12:50:20 +03:00
if ( NT_STATUS_IS_OK ( status ) & & S_ISLNK ( ( * _smb_fname ) - > st . st_ex_mode ) ) {
/*
* lcomp is a symlink
*/
if ( ucf_flags & UCF_LCOMP_LNK_OK ) {
TALLOC_FREE ( symlink_err ) ;
return NT_STATUS_OK ;
}
close_file_free ( NULL , _dirfsp , ERROR_CLOSE ) ;
status = NT_STATUS_STOPPED_ON_SYMLINK ;
}
2022-07-14 20:47:23 +03:00
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_STOPPED_ON_SYMLINK ) ) {
return status ;
}
2024-02-04 20:07:19 +03:00
lnk = & symlink_err - > parsed . lnk ;
2022-07-14 20:47:23 +03:00
2023-10-18 12:50:20 +03:00
/*
* If we ' re on an MSDFS share , see if this is
* an MSDFS link .
*/
if ( lp_host_msdfs ( ) & & lp_msdfs_root ( SNUM ( conn ) ) & &
2024-02-04 18:07:22 +03:00
strnequal ( lnk - > substitute_name , " msdfs: " , 6 ) )
2023-10-18 12:50:20 +03:00
{
TALLOC_FREE ( * _smb_fname ) ;
TALLOC_FREE ( symlink_err ) ;
return NT_STATUS_PATH_NOT_COVERED ;
}
2022-07-14 20:47:23 +03:00
if ( ! lp_follow_symlinks ( SNUM ( conn ) ) ) {
2024-02-04 19:42:28 +03:00
status = ( lnk - > unparsed_path_length = = 0 )
2023-10-25 20:22:11 +03:00
? NT_STATUS_OBJECT_NAME_NOT_FOUND
: NT_STATUS_OBJECT_PATH_NOT_FOUND ;
TALLOC_FREE ( symlink_err ) ;
return status ;
2022-07-14 20:47:23 +03:00
}
2022-08-02 00:40:54 +03:00
/*
* Right now , SMB2 and SMB1 always traverse symlinks
* within the share . SMB1 + POSIX traverses non - terminal
* symlinks within the share .
*
* When we add SMB2 + POSIX we need to return
* a NT_STATUS_STOPPED_ON_SYMLINK error here , using the
* symlink target data read below if SMB2 + POSIX has
* UCF_POSIX_PATHNAMES set to cause the client to
* resolve all symlinks locally .
*/
2022-07-14 20:47:23 +03:00
2024-01-02 14:49:14 +03:00
target = symlink_target_path ( mem_ctx ,
name_in ,
2024-02-04 18:07:22 +03:00
lnk - > substitute_name ,
2024-02-04 19:42:28 +03:00
lnk - > unparsed_path_length ) ;
2024-01-02 14:49:14 +03:00
if ( target = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2022-12-16 18:35:00 +03:00
status = safe_symlink_target_path ( mem_ctx ,
conn - > connectpath ,
2024-01-02 15:25:25 +03:00
NULL ,
2024-01-02 14:49:14 +03:00
target ,
2024-02-04 19:42:28 +03:00
lnk - > unparsed_path_length ,
2024-01-02 14:49:14 +03:00
& safe_target ) ;
2022-12-16 18:35:00 +03:00
TALLOC_FREE ( symlink_err ) ;
2022-10-25 11:26:26 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2022-07-14 20:47:23 +03:00
}
2024-01-02 14:49:14 +03:00
name_in = safe_target ;
2022-07-14 20:47:23 +03:00
symlink_redirects + = 1 ;
goto next ;
}
2023-06-22 15:46:01 +03:00
char * full_path_from_dirfsp_at_basename ( TALLOC_CTX * mem_ctx ,
const struct files_struct * dirfsp ,
const char * at_base_name )
2020-04-14 18:44:37 +03:00
{
char * path = NULL ;
if ( dirfsp = = dirfsp - > conn - > cwd_fsp | |
2023-06-22 15:46:01 +03:00
ISDOT ( dirfsp - > fsp_name - > base_name ) | | at_base_name [ 0 ] = = ' / ' ) {
path = talloc_strdup ( mem_ctx , at_base_name ) ;
2020-04-14 18:44:37 +03:00
} else {
2023-06-22 15:46:01 +03:00
path = talloc_asprintf ( mem_ctx ,
" %s/%s " ,
2020-04-14 18:44:37 +03:00
dirfsp - > fsp_name - > base_name ,
2023-06-22 15:46:01 +03:00
at_base_name ) ;
2020-04-14 18:44:37 +03:00
}
2023-06-22 15:46:01 +03:00
return path ;
}
/*
* Build the full path from a dirfsp and dirfsp relative name
*/
struct smb_filename *
full_path_from_dirfsp_atname ( TALLOC_CTX * mem_ctx ,
const struct files_struct * dirfsp ,
const struct smb_filename * atname )
{
struct smb_filename * fname = NULL ;
char * path = NULL ;
path = full_path_from_dirfsp_at_basename ( mem_ctx ,
dirfsp ,
atname - > base_name ) ;
2020-04-14 18:44:37 +03:00
if ( path = = NULL ) {
return NULL ;
}
fname = synthetic_smb_fname ( mem_ctx ,
path ,
atname - > stream_name ,
& atname - > st ,
atname - > twrp ,
atname - > flags ) ;
TALLOC_FREE ( path ) ;
if ( fname = = NULL ) {
return NULL ;
}
return fname ;
}