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"
2021-12-08 09:08:10 +03:00
# include "lib/util/memcache.h"
1998-08-17 11:40:06 +04:00
2022-03-07 20:00:20 +03:00
static NTSTATUS get_real_filename ( connection_struct * conn ,
struct smb_filename * path ,
const char * name ,
TALLOC_CTX * mem_ctx ,
char * * found_name ) ;
2021-10-16 00:04:07 +03:00
2021-12-08 01:39:42 +03:00
static NTSTATUS check_name ( connection_struct * conn ,
const struct smb_filename * smb_fname ) ;
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 ;
2017-05-18 22:18:58 +03:00
if ( req ! = NULL ) {
if ( req - > posix_pathnames ) {
ucf_flags | = UCF_POSIX_PATHNAMES ;
}
if ( req - > flags2 & FLAGS2_DFS_PATHNAMES ) {
ucf_flags | = UCF_DFS_PATHNAME ;
}
2017-05-18 23:24:26 +03:00
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 ;
}
2008-01-20 01:25:36 +03:00
static NTSTATUS build_stream_path ( TALLOC_CTX * mem_ctx ,
connection_struct * conn ,
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04:00
struct smb_filename * smb_fname ) ;
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
}
2009-11-26 00:17:56 +03:00
static NTSTATUS check_for_dot_component ( const struct smb_filename * smb_fname )
{
/* Ensure we catch all names with in "/."
this is disallowed under Windows and
in POSIX they ' ve already been removed . */
const char * p = strstr ( smb_fname - > base_name , " /. " ) ; /*mb safe*/
if ( p ) {
if ( p [ 2 ] = = ' / ' ) {
/* Error code within a pathname. */
return NT_STATUS_OBJECT_PATH_NOT_FOUND ;
} else if ( p [ 2 ] = = ' \0 ' ) {
/* Error code at the end of a pathname. */
return NT_STATUS_OBJECT_NAME_INVALID ;
}
}
return NT_STATUS_OK ;
}
2010-09-11 10:28:15 +04:00
/****************************************************************************
Optimization for common case where the missing part
is in the last component and the client already
sent the correct case .
Returns NT_STATUS_OK to mean continue the tree walk
( possibly with modified start pointer ) .
Any other NT_STATUS_XXX error means terminate the path
lookup here .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static NTSTATUS check_parent_exists ( TALLOC_CTX * ctx ,
connection_struct * conn ,
bool posix_pathnames ,
2011-04-04 21:15:16 +04:00
const struct smb_filename * smb_fname ,
2010-09-11 10:28:15 +04:00
char * * pp_dirpath ,
2021-05-11 21:31:55 +03:00
char * * pp_start ,
int * p_parent_stat_errno )
2010-09-11 10:28:15 +04:00
{
2020-04-29 16:43:30 +03:00
char * parent_name = NULL ;
struct smb_filename * parent_fname = NULL ;
2010-09-11 10:28:15 +04:00
const char * last_component = NULL ;
NTSTATUS status ;
int ret ;
if ( ! parent_dirname ( ctx , smb_fname - > base_name ,
2020-04-29 16:43:30 +03:00
& parent_name ,
2010-09-11 10:28:15 +04:00
& last_component ) ) {
return NT_STATUS_NO_MEMORY ;
}
2015-12-12 01:38:49 +03:00
if ( ! posix_pathnames ) {
2020-04-29 16:43:30 +03:00
if ( ms_has_wild ( parent_name ) ) {
2018-08-22 19:15:12 +03:00
goto no_optimization_out ;
}
2015-12-12 01:38:49 +03:00
}
2010-09-11 10:28:15 +04:00
/*
* If there was no parent component in
2018-08-22 19:15:12 +03:00
* smb_fname - > base_name then don ' t do this
2010-09-11 10:28:15 +04:00
* optimization .
*/
2018-08-22 19:15:12 +03:00
if ( smb_fname - > base_name = = last_component ) {
goto no_optimization_out ;
2010-09-11 10:28:15 +04:00
}
2020-04-29 16:43:30 +03:00
parent_fname = synthetic_smb_fname ( ctx ,
parent_name ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
smb_fname - > twrp ,
2020-04-29 16:43:30 +03:00
smb_fname - > flags ) ;
if ( parent_fname = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2021-12-14 00:42:35 +03:00
ret = vfs_stat ( conn , parent_fname ) ;
2010-09-11 10:28:15 +04:00
/* If the parent stat failed, just continue
with the normal tree walk . */
if ( ret = = - 1 ) {
2021-05-11 21:31:55 +03:00
/*
* Optimization . Preserving the
* errno from the STAT / LSTAT here
* will allow us to save a duplicate
* STAT / LSTAT system call of the parent
* pathname in a hot code path in the caller .
*/
if ( p_parent_stat_errno ! = NULL ) {
* p_parent_stat_errno = errno ;
}
2018-08-22 19:15:12 +03:00
goto no_optimization_out ;
2010-09-11 10:28:15 +04:00
}
2020-04-29 16:43:30 +03:00
status = check_for_dot_component ( parent_fname ) ;
2010-09-11 10:28:15 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
/* Parent exists - set "start" to be the
2015-03-23 17:38:35 +03:00
* last component to shorten the tree walk . */
2010-09-11 10:28:15 +04:00
/*
2011-05-06 03:19:49 +04:00
* Safe to use discard_const_p
2010-09-11 10:28:15 +04:00
* here as last_component points
* into our smb_fname - > base_name .
*/
2011-05-05 21:41:59 +04:00
* pp_start = discard_const_p ( char , last_component ) ;
2010-09-11 10:28:15 +04:00
/* Update dirpath. */
TALLOC_FREE ( * pp_dirpath ) ;
2020-04-29 16:43:30 +03:00
* pp_dirpath = talloc_strdup ( ctx , parent_fname - > base_name ) ;
2010-09-11 10:33:18 +04:00
if ( ! * pp_dirpath ) {
return NT_STATUS_NO_MEMORY ;
}
2010-09-11 10:28:15 +04:00
DEBUG ( 5 , ( " check_parent_exists: name "
" = %s, dirpath = %s, "
" start = %s \n " ,
smb_fname - > base_name ,
* pp_dirpath ,
* pp_start ) ) ;
return NT_STATUS_OK ;
2018-08-22 19:15:12 +03:00
no_optimization_out :
2018-08-22 23:37:04 +03:00
/*
* We must still return an * pp_dirpath
* initialized to " . " , and a * pp_start
* pointing at smb_fname - > base_name .
*/
2020-04-29 16:43:30 +03:00
TALLOC_FREE ( parent_name ) ;
TALLOC_FREE ( parent_fname ) ;
2018-08-22 23:37:04 +03:00
* pp_dirpath = talloc_strdup ( ctx , " . " ) ;
if ( * pp_dirpath = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
/*
* Safe to use discard_const_p
* here as by convention smb_fname - > base_name
* is allocated off ctx .
*/
* pp_start = discard_const_p ( char , smb_fname - > base_name ) ;
2018-08-22 19:15:12 +03:00
return NT_STATUS_OK ;
2010-09-11 10:28:15 +04:00
}
2022-07-12 18:08:19 +03:00
static bool find_snapshot_token (
const char * filename ,
const char * * _start ,
const char * * _next_component ,
NTTIME * twrp )
2017-01-12 03:30:38 +03:00
{
2022-07-12 18:08:19 +03:00
const char * start = NULL ;
const char * end = NULL ;
struct tm tm ;
time_t t ;
2017-01-12 03:30:38 +03:00
2022-07-12 18:08:19 +03:00
start = strstr_m ( filename , " @GMT- " ) ;
2017-01-12 03:30:38 +03:00
2022-07-12 18:08:19 +03:00
if ( start = = NULL ) {
return false ;
}
2017-01-12 03:30:38 +03:00
2022-07-12 18:08:19 +03:00
if ( ( start > filename ) & & ( start [ - 1 ] ! = ' / ' ) ) {
/* the GMT-token does not start a path-component */
return false ;
}
end = strptime ( start , GMT_FORMAT , & tm ) ;
if ( end = = NULL ) {
/* Not a valid timestring. */
return false ;
2017-01-12 03:30:38 +03:00
}
2022-07-12 18:08:19 +03:00
if ( ( end [ 0 ] ! = ' \0 ' ) & & ( end [ 0 ] ! = ' / ' ) ) {
2017-01-12 03:30:38 +03:00
/*
2022-07-12 18:08:19 +03:00
* It is not a complete path component , i . e . the path
* component continues after the gmt - token .
2017-01-12 03:30:38 +03:00
*/
2022-07-12 18:08:19 +03:00
return false ;
2017-01-12 03:30:38 +03:00
}
2022-07-12 18:08:19 +03:00
tm . tm_isdst = - 1 ;
t = timegm ( & tm ) ;
unix_to_nt_time ( twrp , t ) ;
2017-01-12 03:30:38 +03:00
2022-07-12 18:08:19 +03:00
DBG_DEBUG ( " Extracted @GMT-Timestamp %s \n " ,
nt_time_string ( talloc_tos ( ) , * twrp ) ) ;
2017-01-12 03:30:38 +03:00
2022-07-12 18:08:19 +03:00
* _start = start ;
2017-01-12 03:30:38 +03:00
2022-07-12 18:08:19 +03:00
if ( end [ 0 ] = = ' / ' ) {
end + = 1 ;
}
* _next_component = end ;
2017-01-12 03:30:38 +03:00
2022-07-12 18:08:19 +03:00
return true ;
2017-01-12 03:30:38 +03:00
}
2022-08-03 19:08:48 +03:00
bool extract_snapshot_token ( char * fname , uint32_t ucf_flags , NTTIME * twrp )
2022-07-14 16:13:40 +03:00
{
const char * start = NULL ;
const char * next = NULL ;
size_t remaining ;
bool found ;
2022-08-03 19:20:36 +03:00
bool posix_path = ( ucf_flags & UCF_POSIX_PATHNAMES ) ;
bool msdfs_path = ( ucf_flags & UCF_DFS_PATHNAME ) ;
2022-07-14 16:13:40 +03:00
2022-08-03 19:20:36 +03:00
if ( msdfs_path & & ! posix_path ) {
/*
* A raw ( non - POSIX ) MSDFS path looks like \ server \ share \ path .
* find_snapshot_token only looks for ' / ' separators .
* Convert the separator characters in place .
*/
string_replace ( fname , ' \\ ' , ' / ' ) ;
}
2022-07-14 16:13:40 +03:00
found = find_snapshot_token ( fname , & start , & next , twrp ) ;
2022-08-03 19:20:36 +03:00
if ( msdfs_path & & ! posix_path ) {
/* Put the original separators back. */
string_replace ( fname , ' / ' , ' \\ ' ) ;
}
2022-07-14 16:13:40 +03:00
if ( ! found ) {
return false ;
}
remaining = strlen ( next ) ;
memmove ( discard_const_p ( char , start ) , next , remaining + 1 ) ;
return true ;
}
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-03 19:08:48 +03:00
found = extract_snapshot_token ( smb_fname - > base_name , ucf_flags , & 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 ;
}
1998-08-17 11:40:06 +04:00
/****************************************************************************
This routine is called to convert names from the dos namespace to unix
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04:00
namespace . It needs to handle any case conversions , mangling , format changes ,
streams etc .
1998-08-17 11:40:06 +04:00
We assume that we have already done a chdir ( ) to the right " root " directory
for this service .
2020-03-25 09:53:29 +03:00
Conversion to basic unix format is already done in check_path_syntax ( ) .
Names must be relative to the root of the service - any leading / . and
trailing / ' s should have been trimmed by check_path_syntax ( ) .
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
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04: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
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04:00
If the orig_path was a stream , smb_filename - > base_name will point to the base
filename , and smb_filename - > stream_name will point to the stream name . If
orig_path was not a stream , then smb_filename - > stream_name will be NULL .
On exit from unix_convert , the smb_filename - > st stat struct will be populated
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
2020-04-23 11:06:56 +03:00
struct uc_state {
TALLOC_CTX * mem_ctx ;
struct connection_struct * conn ;
struct smb_filename * smb_fname ;
const char * orig_path ;
uint32_t ucf_flags ;
char * name ;
char * end ;
char * dirpath ;
char * stream ;
bool component_was_mangled ;
bool posix_pathnames ;
bool done ;
2021-10-15 21:59:56 +03:00
bool case_sensitive ;
bool case_preserve ;
bool short_case_preserve ;
2020-04-23 11:06:56 +03:00
} ;
2022-03-07 20:00:20 +03:00
static NTSTATUS unix_convert_step_search_fail (
struct uc_state * state , NTSTATUS status )
2020-04-23 13:35:12 +03:00
{
char * unmangled ;
if ( state - > end ) {
/*
* An intermediate part of the name
* can ' t be found .
*/
DBG_DEBUG ( " Intermediate [%s] missing \n " ,
state - > name ) ;
* state - > 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 .
*/
/*
* ENOENT , ENOTDIR and ELOOP all map
* to NT_STATUS_OBJECT_PATH_NOT_FOUND
* in the filename walk .
*/
2022-03-07 20:00:20 +03:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_NAME_NOT_FOUND ) | |
NT_STATUS_EQUAL ( status , NT_STATUS_STOPPED_ON_SYMLINK ) | |
NT_STATUS_EQUAL ( status , NT_STATUS_NOT_A_DIRECTORY ) ) {
status = NT_STATUS_OBJECT_PATH_NOT_FOUND ;
2020-04-23 13:35:12 +03:00
}
2022-03-07 20:00:20 +03:00
return status ;
2020-04-23 13:35:12 +03:00
}
/*
* ENOENT / EACCESS are the only valid errors
* here .
*/
2022-03-07 20:00:20 +03:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_ACCESS_DENIED ) ) {
2020-04-23 13:35:12 +03:00
if ( ( state - > ucf_flags & UCF_PREP_CREATEFILE ) = = 0 ) {
2021-12-08 01:39:42 +03:00
/*
* Could be a symlink pointing to
* a directory outside the share
* to which we don ' t have access .
* If so , we need to know that here
* so we can return the correct error code .
* check_name ( ) is never called if we
* error out of filename_convert ( ) .
*/
int ret ;
struct smb_filename dname = ( struct smb_filename ) {
. base_name = state - > dirpath ,
. twrp = state - > smb_fname - > twrp ,
} ;
/* handle null paths */
if ( ( dname . base_name = = NULL ) | |
( dname . base_name [ 0 ] = = ' \0 ' ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
ret = SMB_VFS_LSTAT ( state - > conn , & dname ) ;
if ( ret ! = 0 ) {
return NT_STATUS_ACCESS_DENIED ;
}
if ( ! S_ISLNK ( dname . st . st_ex_mode ) ) {
return NT_STATUS_ACCESS_DENIED ;
}
status = check_name ( state - > conn , & dname ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
/* We know this is an intermediate path. */
return NT_STATUS_OBJECT_PATH_NOT_FOUND ;
}
2020-04-23 13:35:12 +03:00
return NT_STATUS_ACCESS_DENIED ;
} else {
/*
* This is the dropbox
* behaviour . A dropbox is a
* directory with only - wx
* permissions , so
* get_real_filename fails
* with EACCESS , it needs to
* list the directory . We
* nevertheless want to allow
* users creating a file .
*/
2022-03-07 20:00:20 +03:00
status = NT_STATUS_OK ;
2020-04-23 13:35:12 +03:00
}
}
2022-03-07 20:00:20 +03:00
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_NAME_NOT_FOUND ) ) {
2020-04-23 13:35:12 +03:00
/*
* ENOTDIR and ELOOP both map to
* NT_STATUS_OBJECT_PATH_NOT_FOUND
* in the filename walk .
*/
2022-03-07 20:00:20 +03:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NOT_A_DIRECTORY ) | |
NT_STATUS_EQUAL ( status , NT_STATUS_STOPPED_ON_SYMLINK ) ) {
status = NT_STATUS_OBJECT_PATH_NOT_FOUND ;
2020-04-23 13:35:12 +03:00
}
2022-03-07 20:00:20 +03:00
return status ;
2020-04-23 13:35:12 +03:00
}
2021-10-16 02:20:34 +03:00
/*
* POSIX pathnames must never call into mangling .
*/
if ( state - > posix_pathnames ) {
goto done ;
}
2020-04-23 13:35:12 +03:00
/*
* Just the last part of the name doesn ' t exist .
* We need to strupper ( ) or strlower ( ) it as
* this conversion may be used for file creation
* purposes . Fix inspired by
* Thomas Neumann < t . neumann @ iku - ag . de > .
*/
2021-10-15 22:07:05 +03:00
if ( ! state - > case_preserve | |
2020-04-23 13:35:12 +03:00
( mangle_is_8_3 ( state - > name , false ,
state - > conn - > params ) & &
2021-10-15 22:08:25 +03:00
! state - > short_case_preserve ) ) {
2020-04-23 13:35:12 +03:00
if ( ! strnorm ( state - > name ,
lp_default_case ( SNUM ( state - > conn ) ) ) ) {
DBG_DEBUG ( " strnorm %s failed \n " ,
state - > name ) ;
return NT_STATUS_INVALID_PARAMETER ;
}
}
/*
* check on the mangled stack to see if we can
* recover the base of the filename .
*/
if ( mangle_is_mangled ( state - > name , state - > conn - > params )
& & mangle_lookup_name_from_8_3 ( state - > mem_ctx ,
state - > name ,
& unmangled ,
state - > conn - > params ) ) {
char * tmp ;
size_t name_ofs =
state - > name - state - > smb_fname - > base_name ;
if ( ! ISDOT ( state - > dirpath ) ) {
tmp = talloc_asprintf (
state - > smb_fname , " %s/%s " ,
state - > dirpath , unmangled ) ;
TALLOC_FREE ( unmangled ) ;
}
else {
tmp = unmangled ;
}
if ( tmp = = NULL ) {
DBG_ERR ( " talloc failed \n " ) ;
return NT_STATUS_NO_MEMORY ;
}
TALLOC_FREE ( state - > smb_fname - > base_name ) ;
state - > smb_fname - > base_name = tmp ;
state - > name =
state - > smb_fname - > base_name + name_ofs ;
state - > end = state - > name + strlen ( state - > name ) ;
}
2021-10-16 02:20:34 +03:00
done :
2020-04-23 13:35:12 +03:00
DBG_DEBUG ( " New file [%s] \n " , state - > name ) ;
state - > done = true ;
return NT_STATUS_OK ;
}
2020-04-23 12:40:25 +03:00
static NTSTATUS unix_convert_step_stat ( struct uc_state * state )
2020-04-23 12:16:36 +03:00
{
2020-04-30 17:22:19 +03:00
struct smb_filename dname ;
char dot [ 2 ] = " . " ;
2020-04-23 12:46:19 +03:00
char * found_name = NULL ;
2020-04-23 12:16:36 +03:00
int ret ;
2022-03-07 20:00:20 +03:00
NTSTATUS status ;
2020-04-23 12:16:36 +03:00
/*
* Check if the name exists up to this point .
*/
2020-04-23 17:09:28 +03:00
DBG_DEBUG ( " smb_fname [%s] \n " , smb_fname_str_dbg ( state - > smb_fname ) ) ;
2020-11-25 14:29:40 +03:00
ret = vfs_stat ( state - > conn , state - > smb_fname ) ;
2020-04-23 12:16:36 +03:00
if ( ret = = 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 ( state - > end & & ! S_ISDIR ( state - > smb_fname - > st . st_ex_mode ) ) {
/*
* An intermediate part of the name isn ' t
* a directory .
*/
DBG_DEBUG ( " Not a dir [%s] \n " , state - > name ) ;
* state - > 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 .
*/
2020-04-23 13:18:29 +03:00
return NT_STATUS_OBJECT_PATH_NOT_FOUND ;
2020-04-23 12:16:36 +03:00
}
2020-04-23 12:46:19 +03:00
return NT_STATUS_OK ;
}
2020-04-23 12:16:36 +03:00
2020-04-23 12:46:19 +03:00
/* Stat failed - ensure we don't use it. */
SET_STAT_INVALID ( state - > smb_fname - > st ) ;
2020-04-23 12:16:36 +03:00
2020-04-23 12:46:19 +03:00
if ( state - > posix_pathnames ) {
/*
* For posix_pathnames , we ' re done .
2021-12-04 00:05:55 +03:00
* Don ' t blunder into the
* get_real_filename ( ) codepath as they may
2020-04-23 12:46:19 +03:00
* be doing case insensitive lookups . So when
* creating a new POSIX directory Foo they might
* match on name foo .
*
* BUG : https : //bugzilla.samba.org/show_bug.cgi?id=13803
*/
if ( state - > end ! = NULL ) {
const char * morepath = NULL ;
2020-04-23 12:16:36 +03:00
/*
2020-04-23 12:46:19 +03:00
* If this is intermediate we must
* restore the full path .
2020-04-23 12:16:36 +03:00
*/
2020-04-23 12:46:19 +03:00
* state - > end = ' / ' ;
/*
* If there are any more components
* after the failed LSTAT we cannot
* continue .
*/
morepath = strchr ( state - > end + 1 , ' / ' ) ;
if ( morepath ! = NULL ) {
return NT_STATUS_OBJECT_PATH_NOT_FOUND ;
2020-04-23 12:16:36 +03:00
}
}
2020-04-23 12:46:19 +03:00
if ( errno = = ENOENT ) {
/* New file or directory. */
state - > done = true ;
return NT_STATUS_OK ;
}
if ( ( errno = = EACCES ) & &
( state - > ucf_flags & UCF_PREP_CREATEFILE ) ) {
/* POSIX Dropbox case. */
errno = 0 ;
state - > done = true ;
return NT_STATUS_OK ;
}
return map_nt_error_from_unix ( errno ) ;
}
2020-04-23 12:16:36 +03:00
2020-04-23 12:46:19 +03:00
/*
* Reset errno so we can detect
* directory open errors .
*/
errno = 0 ;
2020-04-23 12:16:36 +03:00
2020-04-23 12:46:19 +03:00
/*
* Try to find this part of the path in the directory .
*/
2020-04-23 12:16:36 +03:00
2020-04-30 17:22:19 +03:00
dname = ( struct smb_filename ) {
. base_name = state - > dirpath ,
. twrp = state - > smb_fname - > twrp ,
} ;
/* handle null paths */
if ( ( dname . base_name = = NULL ) | | ( dname . base_name [ 0 ] = = ' \0 ' ) ) {
dname . base_name = dot ;
}
2022-03-07 20:00:20 +03:00
status = get_real_filename ( state - > conn ,
& dname ,
state - > name ,
talloc_tos ( ) ,
& found_name ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return unix_convert_step_search_fail ( state , status ) ;
2020-04-23 12:46:19 +03:00
}
2020-04-23 12:16:36 +03:00
2020-04-23 12:46:19 +03:00
/*
* Restore the rest of the string . If the string was
* mangled the size may have changed .
*/
if ( state - > end ) {
char * tmp ;
size_t name_ofs =
state - > name - state - > smb_fname - > base_name ;
if ( ! ISDOT ( state - > dirpath ) ) {
tmp = talloc_asprintf ( state - > smb_fname ,
" %s/%s/%s " , state - > dirpath ,
found_name , state - > end + 1 ) ;
}
else {
tmp = talloc_asprintf ( state - > smb_fname ,
" %s/%s " , found_name ,
state - > end + 1 ) ;
2020-04-23 12:16:36 +03:00
}
2020-04-23 12:46:19 +03:00
if ( tmp = = NULL ) {
DBG_ERR ( " talloc_asprintf failed \n " ) ;
return NT_STATUS_NO_MEMORY ;
}
TALLOC_FREE ( state - > smb_fname - > base_name ) ;
state - > smb_fname - > base_name = tmp ;
state - > name = state - > smb_fname - > base_name + name_ofs ;
state - > end = state - > name + strlen ( found_name ) ;
* state - > end = ' \0 ' ;
} else {
char * tmp ;
size_t name_ofs =
state - > name - state - > smb_fname - > base_name ;
if ( ! ISDOT ( state - > dirpath ) ) {
tmp = talloc_asprintf ( state - > smb_fname ,
" %s/%s " , state - > dirpath ,
found_name ) ;
} else {
tmp = talloc_strdup ( state - > smb_fname ,
found_name ) ;
}
if ( tmp = = NULL ) {
DBG_ERR ( " talloc failed \n " ) ;
return NT_STATUS_NO_MEMORY ;
}
TALLOC_FREE ( state - > smb_fname - > base_name ) ;
state - > smb_fname - > base_name = tmp ;
state - > name = state - > smb_fname - > base_name + name_ofs ;
/*
* We just scanned for , and found the end of
* the path . We must return a valid stat struct
* if it exists . JRA .
*/
2020-04-23 12:16:36 +03:00
2020-11-25 14:29:40 +03:00
ret = vfs_stat ( state - > conn , state - > smb_fname ) ;
2020-04-23 12:46:19 +03:00
if ( ret ! = 0 ) {
SET_STAT_INVALID ( state - > smb_fname - > st ) ;
}
}
2020-04-23 12:16:36 +03:00
2020-04-23 12:46:19 +03:00
TALLOC_FREE ( found_name ) ;
2020-04-23 12:40:25 +03:00
return NT_STATUS_OK ;
}
static NTSTATUS unix_convert_step ( struct uc_state * state )
{
NTSTATUS status ;
/*
* Pinpoint the end of this section of the filename .
*/
/* mb safe. '/' can't be in any encoded char. */
state - > end = strchr ( state - > name , ' / ' ) ;
/*
* Chop the name at this point .
*/
2020-04-24 23:55:49 +03:00
if ( state - > end ! = NULL ) {
2020-04-23 12:40:25 +03:00
* state - > end = 0 ;
}
2020-04-23 17:09:28 +03:00
DBG_DEBUG ( " dirpath [%s] name [%s] \n " , state - > dirpath , state - > name ) ;
2020-04-23 12:40:25 +03:00
/* The name cannot have a component of "." */
if ( ISDOT ( state - > name ) ) {
2020-04-24 23:55:49 +03:00
if ( state - > end = = NULL ) {
2020-04-23 12:40:25 +03:00
/* Error code at the end of a pathname. */
return NT_STATUS_OBJECT_NAME_INVALID ;
}
2021-12-03 23:37:15 +03:00
return NT_STATUS_OBJECT_PATH_NOT_FOUND ;
2020-04-23 12:40:25 +03:00
}
status = unix_convert_step_stat ( state ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2020-04-28 09:04:41 +03:00
if ( state - > done ) {
return NT_STATUS_OK ;
}
2020-04-23 12:40:25 +03:00
2020-04-23 12:16:36 +03:00
/*
* Add to the dirpath that we have resolved so far .
*/
if ( ! ISDOT ( state - > dirpath ) ) {
char * tmp = talloc_asprintf ( state - > mem_ctx ,
" %s/%s " , state - > dirpath , state - > name ) ;
if ( ! tmp ) {
DBG_ERR ( " talloc_asprintf failed \n " ) ;
2020-04-23 13:18:29 +03:00
return NT_STATUS_NO_MEMORY ;
2020-04-23 12:16:36 +03:00
}
TALLOC_FREE ( state - > dirpath ) ;
state - > dirpath = tmp ;
}
else {
TALLOC_FREE ( state - > dirpath ) ;
if ( ! ( state - > dirpath = talloc_strdup ( state - > mem_ctx , state - > name ) ) ) {
DBG_ERR ( " talloc_strdup failed \n " ) ;
2020-04-23 13:18:29 +03:00
return NT_STATUS_NO_MEMORY ;
2020-04-23 12:16:36 +03:00
}
}
/*
* Cache the dirpath thus far . Don ' t cache a name with mangled
2021-12-04 00:03:47 +03:00
* components as this can change the size .
2020-04-23 12:16:36 +03:00
*/
2021-12-04 00:03:47 +03:00
if ( ! state - > component_was_mangled ) {
2020-05-05 14:00:00 +03:00
stat_cache_add ( state - > orig_path ,
state - > dirpath ,
2020-04-30 16:24:44 +03:00
state - > smb_fname - > twrp ,
2021-10-15 22:02:33 +03:00
state - > case_sensitive ) ;
2020-04-23 12:16:36 +03:00
}
/*
* Restore the / that we wiped out earlier .
*/
2020-04-24 23:55:49 +03:00
if ( state - > end ! = NULL ) {
2020-04-23 12:16:36 +03:00
* state - > end = ' / ' ;
}
return NT_STATUS_OK ;
}
2020-04-23 10:39:22 +03:00
NTSTATUS unix_convert ( TALLOC_CTX * mem_ctx ,
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04:00
connection_struct * conn ,
const char * orig_path ,
2020-04-30 17:04:54 +03:00
NTTIME twrp ,
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04:00
struct smb_filename * * smb_fname_out ,
uint32_t ucf_flags )
1998-08-17 11:40:06 +04:00
{
2020-04-23 11:06:56 +03:00
struct uc_state uc_state ;
struct uc_state * state = & uc_state ;
2009-07-22 20:52:09 +04:00
NTSTATUS status ;
2009-02-05 03:33:52 +03:00
int ret = - 1 ;
2021-12-03 23:59:50 +03:00
int parent_stat_errno = 0 ;
2002-07-15 14:35:28 +04:00
2020-04-23 11:06:56 +03:00
* state = ( struct uc_state ) {
. mem_ctx = mem_ctx ,
. conn = conn ,
. orig_path = orig_path ,
. ucf_flags = ucf_flags ,
. posix_pathnames = ( ucf_flags & UCF_POSIX_PATHNAMES ) ,
2021-10-15 21:59:56 +03:00
. case_sensitive = conn - > case_sensitive ,
. case_preserve = conn - > case_preserve ,
. short_case_preserve = conn - > short_case_preserve ,
2020-04-23 11:06:56 +03:00
} ;
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04:00
* smb_fname_out = NULL ;
2021-10-15 23:32:27 +03:00
if ( state - > posix_pathnames ) {
/* POSIX means ignore case settings on share. */
state - > case_sensitive = true ;
state - > case_preserve = true ;
state - > short_case_preserve = true ;
}
2020-04-23 11:06:56 +03:00
state - > smb_fname = talloc_zero ( state - > mem_ctx , struct smb_filename ) ;
if ( state - > smb_fname = = NULL ) {
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04:00
return NT_STATUS_NO_MEMORY ;
2007-01-13 02:47:16 +03:00
}
2002-07-15 14:35:28 +04:00
2020-04-23 11:06:56 +03:00
if ( state - > conn - > printer ) {
2002-07-15 14:35:28 +04:00
/* we don't ever use the filenames on a printer share as a
filename - so don ' t convert them */
2020-06-15 13:16:36 +03:00
state - > smb_fname - > base_name = talloc_strdup (
state - > smb_fname , state - > orig_path ) ;
if ( state - > smb_fname - > base_name = = NULL ) {
2009-07-22 20:52:09 +04:00
status = NT_STATUS_NO_MEMORY ;
goto err ;
2007-09-08 00:57:01 +04:00
}
2009-07-22 20:52:09 +04:00
goto done ;
2002-07-15 14:35:28 +04:00
}
2020-04-23 11:06:56 +03:00
state - > smb_fname - > flags = state - > posix_pathnames ? SMB_FILENAME_POSIX_PATH : 0 ;
2016-03-18 02:20:17 +03:00
2020-04-23 11:06:56 +03:00
DBG_DEBUG ( " Called on file [%s] \n " , state - > orig_path ) ;
2002-07-15 14:35:28 +04:00
2020-04-23 11:06:56 +03:00
if ( state - > orig_path [ 0 ] = = ' / ' ) {
DBG_ERR ( " Path [%s] starts with '/' \n " , state - > orig_path ) ;
2020-03-25 09:56:37 +03:00
return NT_STATUS_OBJECT_NAME_INVALID ;
}
2002-07-15 14:35:28 +04:00
2020-05-05 05:29:17 +03:00
/* Start with the full orig_path as given by the caller. */
2020-06-15 13:16:36 +03:00
state - > smb_fname - > base_name = talloc_strdup (
state - > smb_fname , state - > orig_path ) ;
if ( state - > smb_fname - > base_name = = NULL ) {
2020-05-05 05:29:17 +03:00
DBG_ERR ( " talloc_strdup failed \n " ) ;
status = NT_STATUS_NO_MEMORY ;
goto err ;
}
/* Canonicalize any @GMT- paths. */
status = canonicalize_snapshot_path ( state - > smb_fname , ucf_flags , twrp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto err ;
}
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
*/
2020-05-05 05:29:17 +03:00
if ( state - > smb_fname - > base_name [ 0 ] = = ' \0 ' ) {
2020-06-15 13:16:36 +03:00
state - > smb_fname - > base_name = talloc_strdup ( state - > smb_fname , " . " ) ;
if ( state - > smb_fname - > base_name = = NULL ) {
2009-07-22 20:52:09 +04:00
status = NT_STATUS_NO_MEMORY ;
goto err ;
2007-07-12 02:39:11 +04:00
}
2020-04-23 11:06:56 +03:00
if ( SMB_VFS_STAT ( state - > conn , state - > smb_fname ) ! = 0 ) {
2009-07-22 20:52:09 +04:00
status = map_nt_error_from_unix ( errno ) ;
goto err ;
2004-05-14 05:22:17 +04:00
}
2020-03-25 21:00:32 +03:00
DBG_DEBUG ( " conversion finished [] -> [%s] \n " ,
2020-04-23 11:06:56 +03:00
state - > smb_fname - > base_name ) ;
2007-07-12 02:39:11 +04:00
goto done ;
2007-01-13 02:47:16 +03:00
}
2020-04-23 11:06:56 +03:00
if ( state - > orig_path [ 0 ] = = ' . ' & & ( state - > orig_path [ 1 ] = = ' / ' | |
state - > orig_path [ 1 ] = = ' \0 ' ) ) {
2007-01-13 02:47:16 +03:00
/* Start of pathname can't be "." only. */
2020-04-23 11:06:56 +03:00
if ( state - > orig_path [ 1 ] = = ' \0 ' | | state - > orig_path [ 2 ] = = ' \0 ' ) {
2009-07-22 20:52:09 +04:00
status = NT_STATUS_OBJECT_NAME_INVALID ;
2007-01-13 04:07:39 +03:00
} else {
2021-12-03 23:37:15 +03:00
status = NT_STATUS_OBJECT_PATH_NOT_FOUND ;
2007-01-13 04:07:39 +03:00
}
2009-07-22 20:52:09 +04:00
goto err ;
2002-07-15 14:35:28 +04:00
}
2008-11-18 21:57:54 +03:00
/*
* 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 .
*/
2021-09-18 03:02:06 +03:00
status = normalize_filename_case ( state - > conn ,
state - > smb_fname - > base_name ,
ucf_flags ) ;
2020-03-26 22:35:26 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " normalize_filename_case %s failed \n " ,
2020-04-23 11:06:56 +03:00
state - > smb_fname - > base_name ) ;
2020-03-26 22:35:26 +03:00
goto err ;
2008-11-18 21:57:54 +03:00
}
2009-07-22 20:52:09 +04:00
/*
* Strip off the stream , and add it back when we ' re done with the
* base_name .
*/
2020-04-23 11:06:56 +03:00
if ( ! state - > posix_pathnames ) {
state - > stream = strchr_m ( state - > smb_fname - > base_name , ' : ' ) ;
2008-01-20 08:53:49 +03:00
2020-04-23 11:06:56 +03:00
if ( state - > stream ! = NULL ) {
char * tmp = talloc_strdup ( state - > smb_fname , state - > stream ) ;
2008-01-20 08:53:49 +03:00
if ( tmp = = NULL ) {
2009-07-22 20:52:09 +04:00
status = NT_STATUS_NO_MEMORY ;
goto err ;
2008-01-20 08:53:49 +03:00
}
2009-07-22 20:52:09 +04:00
/*
* Since this is actually pointing into
* smb_fname - > base_name this truncates base_name .
*/
2020-04-23 11:06:56 +03:00
* state - > stream = ' \0 ' ;
state - > stream = tmp ;
2015-09-16 22:03:34 +03:00
2020-04-23 11:06:56 +03:00
if ( state - > smb_fname - > base_name [ 0 ] = = ' \0 ' ) {
2015-09-16 22:03:34 +03:00
/*
* orig_name was just a stream name .
* This is a stream on the root of
* the share . Replace base_name with
* a " . "
*/
2020-04-23 11:06:56 +03:00
state - > smb_fname - > base_name =
talloc_strdup ( state - > smb_fname , " . " ) ;
if ( state - > smb_fname - > base_name = = NULL ) {
2015-09-16 22:03:34 +03:00
status = NT_STATUS_NO_MEMORY ;
goto err ;
}
2020-04-23 11:06:56 +03:00
if ( SMB_VFS_STAT ( state - > conn , state - > smb_fname ) ! = 0 ) {
2015-09-16 22:03:34 +03:00
status = map_nt_error_from_unix ( errno ) ;
goto err ;
}
2015-10-01 03:12:11 +03:00
/* dirpath must exist. */
2020-04-23 11:06:56 +03:00
state - > dirpath = talloc_strdup ( state - > mem_ctx , " . " ) ;
if ( state - > dirpath = = NULL ) {
2015-10-01 03:12:11 +03:00
status = NT_STATUS_NO_MEMORY ;
goto err ;
}
2020-03-25 21:00:32 +03:00
DBG_INFO ( " conversion finished [%s] -> [%s] \n " ,
2020-04-23 11:06:56 +03:00
state - > orig_path ,
state - > smb_fname - > base_name ) ;
2015-09-16 22:03:34 +03:00
goto done ;
}
2008-01-20 01:25:36 +03:00
}
}
2020-04-23 11:06:56 +03:00
state - > name = state - > smb_fname - > base_name ;
2002-07-15 14:35:28 +04:00
2009-07-22 20:52:09 +04:00
/*
2010-12-06 16:05:49 +03:00
* If we ' re providing case insensitive semantics or
2007-12-23 04:38:18 +03:00
* the underlying filesystem is case insensitive ,
* then a case - normalized hit in the stat - cache is
2020-06-13 16:32:31 +03:00
* authoritative . JRA .
2009-07-22 20:52:09 +04:00
*
* Note : We ' re only checking base_name . The stream_name will be
* added and verified in build_stream_path ( ) .
2007-12-23 04:38:18 +03:00
*/
2021-10-15 22:02:33 +03:00
if ( ! state - > case_sensitive | |
2020-05-05 12:13:47 +03:00
! ( state - > conn - > fs_capabilities & FILE_CASE_SENSITIVE_SEARCH ) )
{
bool found ;
found = stat_cache_lookup ( state - > conn ,
& state - > smb_fname - > base_name ,
& state - > dirpath ,
& state - > name ,
2020-04-30 16:24:44 +03:00
state - > smb_fname - > twrp ,
2020-05-05 12:13:47 +03:00
& state - > smb_fname - > st ) ;
2022-02-23 17:56:41 +03:00
/*
* stat_cache_lookup ( ) allocates on talloc_tos ( ) even
* when ! found , reparent correctly
*/
talloc_steal ( state - > smb_fname , state - > smb_fname - > base_name ) ;
talloc_steal ( state - > mem_ctx , state - > dirpath ) ;
2020-05-05 12:13:47 +03:00
if ( found ) {
goto done ;
}
2007-07-12 02:39:11 +04:00
}
/*
* Make sure " dirpath " is an allocated string , we use this for
2010-12-06 16:05:49 +03:00
* building the directories with talloc_asprintf and free it .
2007-07-12 02:39:11 +04:00
*/
2020-06-15 13:16:36 +03:00
if ( state - > dirpath = = NULL ) {
state - > dirpath = talloc_strdup ( state - > mem_ctx , " . " ) ;
if ( state - > dirpath = = NULL ) {
DBG_ERR ( " talloc_strdup failed \n " ) ;
status = NT_STATUS_NO_MEMORY ;
goto err ;
}
2002-07-15 14:35:28 +04:00
}
2007-07-12 02:39:11 +04:00
/*
2009-11-26 00:17:56 +03:00
* If we have a wildcard we must walk the path to
* find where the error is , even if case sensitive
* is true .
2002-07-15 14:35:28 +04:00
*/
2020-04-23 11:06:56 +03:00
if ( ! state - > posix_pathnames ) {
2015-12-12 01:41:38 +03:00
/* POSIX pathnames have no wildcards. */
2021-12-03 23:53:36 +03:00
bool name_has_wildcard = ms_has_wild ( state - > smb_fname - > base_name ) ;
if ( name_has_wildcard ) {
2015-12-12 01:41:38 +03:00
/* Wildcard not valid anywhere. */
status = NT_STATUS_OBJECT_NAME_INVALID ;
goto fail ;
}
2009-02-05 03:33:52 +03:00
}
2020-04-23 10:32:53 +03:00
DBG_DEBUG ( " Begin: name [%s] dirpath [%s] name [%s] \n " ,
2020-04-23 11:06:56 +03:00
state - > smb_fname - > base_name , state - > dirpath , state - > name ) ;
2009-11-26 00:17:56 +03:00
2021-12-03 23:59:50 +03:00
/*
* stat the name - if it exists then we can add the stream back ( if
* there was one ) and be done !
*/
2021-05-11 21:41:42 +03:00
2021-12-03 23:59:50 +03:00
ret = vfs_stat ( state - > conn , state - > smb_fname ) ;
if ( ret = = 0 ) {
status = check_for_dot_component ( state - > smb_fname ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
/* Add the path (not including the stream) to the cache. */
stat_cache_add ( state - > orig_path ,
state - > smb_fname - > base_name ,
state - > smb_fname - > twrp ,
state - > case_sensitive ) ;
DBG_DEBUG ( " Conversion of base_name finished "
" [%s] -> [%s] \n " ,
state - > orig_path , state - > smb_fname - > base_name ) ;
goto done ;
}
2009-11-26 00:17:56 +03:00
2021-12-03 23:59:50 +03:00
/* Stat failed - ensure we don't use it. */
SET_STAT_INVALID ( state - > smb_fname - > st ) ;
/*
* Note : we must continue processing a path if we get EACCES
* from stat . With NFS4 permissions the file might be lacking
* READ_ATTR , but if the parent has LIST permissions we can
* resolve the path in the path traversal loop down below .
*/
if ( errno = = ENOENT ) {
/* Optimization when creating a new file - only
the last component doesn ' t exist .
NOTE : check_parent_exists ( ) doesn ' t preserve errno .
*/
int saved_errno = errno ;
status = check_parent_exists ( state - > mem_ctx ,
state - > conn ,
state - > posix_pathnames ,
state - > smb_fname ,
& state - > dirpath ,
& state - > name ,
& parent_stat_errno ) ;
errno = saved_errno ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
2007-01-13 02:47:16 +03:00
}
2021-12-03 23:59:50 +03:00
}
2002-07-15 14:35:28 +04:00
2021-12-03 23:59:50 +03:00
/*
* A special case - if we don ' t have any wildcards or mangling chars and are case
* sensitive or the underlying filesystem is case insensitive then searching
* won ' t help .
*
* NB . As POSIX sets state - > case_sensitive as
* true we will never call into mangle_is_mangled ( ) here .
*/
2011-04-04 21:22:03 +04:00
2021-12-03 23:59:50 +03:00
if ( ( state - > case_sensitive | | ! ( state - > conn - > fs_capabilities &
FILE_CASE_SENSITIVE_SEARCH ) ) & &
! mangle_is_mangled ( state - > smb_fname - > base_name , state - > conn - > params ) ) {
2020-04-22 16:13:04 +03:00
2021-12-03 23:59:50 +03:00
status = check_for_dot_component ( state - > smb_fname ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
2010-09-11 10:28:15 +04:00
}
2009-11-26 00:17:56 +03:00
/*
2021-12-03 23:59:50 +03:00
* The stat failed . Could be ok as it could be
* a new file .
2009-11-26 00:17:56 +03:00
*/
2002-07-15 14:35:28 +04:00
2021-12-03 23:59:50 +03:00
if ( errno = = ENOTDIR | | errno = = ELOOP ) {
status = NT_STATUS_OBJECT_PATH_NOT_FOUND ;
goto fail ;
} else if ( errno = = ENOENT ) {
2009-11-26 00:17:56 +03:00
/*
2021-12-03 23:59:50 +03:00
* Was it a missing last component ?
* or a missing intermediate component ?
*
* Optimization .
*
* For this code path we can guarantee that
* we have gone through check_parent_exists ( )
* and it returned NT_STATUS_OK .
*
* Either there was no parent component ( " . " )
* parent_stat_errno = = 0 and we have a missing
* last component here .
*
* OR check_parent_exists ( ) called STAT / LSTAT
* and if it failed parent_stat_errno has been
* set telling us if the parent existed or not .
*
* Either way we can avoid another STAT / LSTAT
* system call on the parent here .
*/
if ( parent_stat_errno = = ENOTDIR | |
parent_stat_errno = = ENOENT | |
parent_stat_errno = = ELOOP ) {
2009-11-26 00:17:56 +03:00
status = NT_STATUS_OBJECT_PATH_NOT_FOUND ;
goto fail ;
}
2021-12-03 23:59:50 +03:00
/*
* Missing last component is ok - new file .
* Also deal with permission denied elsewhere .
* Just drop out to done .
*/
goto done ;
2009-11-26 00:17:56 +03:00
}
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 .
*/
2021-10-16 02:26:24 +03:00
if ( state - > posix_pathnames ) {
/*
* POSIX names are never mangled and we must not
* call into mangling functions .
*/
state - > component_was_mangled = false ;
} else if ( mangle_is_mangled ( state - > name , state - > conn - > params ) ) {
2020-04-23 11:06:56 +03:00
state - > 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 .
*/
2020-04-23 11:06:56 +03:00
for ( ; state - > name ; state - > name = ( state - > end ? state - > end + 1 : ( char * ) NULL ) ) {
2020-04-23 12:16:36 +03:00
status = unix_convert_step ( state ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NO_MEMORY ) ) {
goto err ;
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
}
2020-04-23 12:16:36 +03:00
if ( state - > done ) {
2010-09-09 03:55:24 +04:00
goto done ;
}
2002-07-15 14:35:28 +04:00
}
2007-07-12 02:39:11 +04:00
2002-07-15 14:35:28 +04:00
/*
2009-07-22 20:52:09 +04:00
* Cache the full path . Don ' t cache a name with mangled or wildcard
* components as this can change the size .
2002-07-15 14:35:28 +04:00
*/
2000-12-12 03:11:34 +03:00
2021-12-03 23:55:41 +03:00
if ( ! state - > component_was_mangled ) {
2020-05-05 14:00:00 +03:00
stat_cache_add ( state - > orig_path ,
state - > smb_fname - > base_name ,
2020-04-30 16:24:44 +03:00
state - > smb_fname - > twrp ,
2021-10-15 22:02:33 +03:00
state - > 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
2007-07-12 02:39:11 +04:00
done :
2009-07-22 20:52:09 +04:00
/* Add back the stream if one was stripped off originally. */
2020-04-23 11:06:56 +03:00
if ( state - > stream ! = NULL ) {
state - > smb_fname - > stream_name = state - > stream ;
2008-01-20 01:25:36 +03:00
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04:00
/* Check path now that the base_name has been converted. */
2020-04-23 11:06:56 +03:00
status = build_stream_path ( state - > mem_ctx , state - > conn , state - > smb_fname ) ;
2009-07-22 20:52:09 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2008-01-20 01:25:36 +03:00
goto fail ;
}
}
2020-05-02 09:09:24 +03:00
DBG_DEBUG ( " Conversion finished [%s] -> [%s] \n " ,
state - > orig_path , smb_fname_str_dbg ( state - > smb_fname ) ) ;
2020-04-23 11:06:56 +03:00
TALLOC_FREE ( state - > dirpath ) ;
* smb_fname_out = state - > smb_fname ;
2007-08-20 00:00:43 +04:00
return NT_STATUS_OK ;
2007-08-16 19:44:13 +04:00
fail :
2020-04-23 10:32:53 +03:00
DBG_DEBUG ( " Conversion failed: dirpath [%s] name [%s] \n " ,
2020-04-23 11:06:56 +03:00
state - > dirpath , state - > name ) ;
2020-06-15 13:16:36 +03:00
if ( ( state - > dirpath ! = NULL ) & & ! ISDOT ( state - > dirpath ) ) {
state - > smb_fname - > base_name = talloc_asprintf (
state - > smb_fname ,
" %s/%s " ,
state - > dirpath ,
state - > name ) ;
2007-09-08 00:57:01 +04:00
} else {
2020-06-15 13:16:36 +03:00
state - > smb_fname - > base_name = talloc_strdup (
state - > smb_fname , state - > name ) ;
2007-09-08 00:57:01 +04:00
}
2020-06-15 13:16:36 +03:00
if ( state - > smb_fname - > base_name = = NULL ) {
2020-03-25 21:00:32 +03:00
DBG_ERR ( " talloc_asprintf failed \n " ) ;
2009-07-22 20:52:09 +04:00
status = NT_STATUS_NO_MEMORY ;
goto err ;
2007-09-08 00:57:01 +04:00
}
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04:00
2020-04-23 11:06:56 +03:00
* smb_fname_out = state - > smb_fname ;
TALLOC_FREE ( state - > dirpath ) ;
2009-07-22 20:52:09 +04:00
return status ;
err :
2020-04-23 11:06:56 +03:00
TALLOC_FREE ( state - > smb_fname ) ;
2009-07-22 20:52:09 +04:00
return status ;
1998-08-17 11:40:06 +04:00
}
/****************************************************************************
2015-02-09 17:42:23 +03:00
Ensure a path is not vetoed .
1998-08-17 11:40:06 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2002-07-15 14:35:28 +04:00
2017-06-30 21:59:20 +03:00
static NTSTATUS check_veto_path ( connection_struct * conn ,
const struct smb_filename * smb_fname )
1998-08-17 11:40:06 +04:00
{
2017-06-30 21:59:20 +03:00
const char * name = smb_fname - > base_name ;
2009-11-23 18:33:53 +03:00
if ( IS_VETO_PATH ( conn , name ) ) {
2004-03-06 01:32:45 +03:00
/* Is it not dot or dot dot. */
2011-07-26 03:12:45 +04:00
if ( ! ( ISDOT ( name ) | | ISDOTDOT ( name ) ) ) {
2011-10-22 01:12:41 +04:00
DEBUG ( 5 , ( " check_veto_path: file path name %s vetoed \n " ,
2007-08-31 01:46:42 +04:00
name ) ) ;
2007-01-17 05:09:37 +03:00
return map_nt_error_from_unix ( ENOENT ) ;
2002-07-15 14:35:28 +04:00
}
}
2011-10-22 01:12:41 +04:00
return NT_STATUS_OK ;
}
/****************************************************************************
Check a filename - possibly calling check_reduced_name .
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 .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-12-10 03:47:13 +03:00
static NTSTATUS check_name ( connection_struct * conn ,
2017-06-30 21:59:20 +03:00
const struct smb_filename * smb_fname )
2011-10-22 01:12:41 +04:00
{
2017-06-30 21:59:20 +03:00
NTSTATUS status = check_veto_path ( conn , smb_fname ) ;
2011-10-22 01:12:41 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
1998-08-17 11:40:06 +04:00
2014-02-04 06:09:03 +04:00
if ( ! lp_widelinks ( SNUM ( conn ) ) | | ! lp_follow_symlinks ( SNUM ( conn ) ) ) {
2017-06-30 21:59:20 +03:00
status = check_reduced_name ( conn , NULL , smb_fname ) ;
2007-01-17 05:09:37 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2017-06-30 21:59:20 +03:00
DEBUG ( 5 , ( " check_name: name %s failed with %s \n " ,
smb_fname - > base_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 ) ) ;
}
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 ;
2009-04-23 16:54:52 +04:00
long curpos ;
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 */
2005-01-29 00:01:58 +03:00
curpos = 0 ;
2009-11-16 11:49:23 +03:00
while ( ( dname = ReadDirName ( cur_dir , & curpos , NULL , & 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
2022-03-13 17:55:25 +03:00
NTSTATUS get_real_filename_full_scan ( connection_struct * conn ,
const char * path ,
const char * name ,
bool mangled ,
TALLOC_CTX * mem_ctx ,
char * * found_name )
{
struct smb_filename * smb_dname = NULL ;
NTSTATUS status ;
/* handle null paths */
if ( ( path = = NULL ) | | ( * path = = 0 ) ) {
path = " . " ;
}
status = synthetic_pathref (
talloc_tos ( ) ,
conn - > cwd_fsp ,
path ,
NULL ,
NULL ,
0 ,
0 ,
& smb_dname ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
status = get_real_filename_full_scan_at (
smb_dname - > fsp , name , mangled , mem_ctx , found_name ) ;
TALLOC_FREE ( smb_dname ) ;
return status ;
}
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 ;
}
2021-12-08 09:08:10 +03:00
/*
* Create the memcache - key for GETREALFILENAME_CACHE : This supplements
* the stat cache for the last component to be looked up . Cache
* contents is the correctly capitalized translation of the parameter
* " name " as it exists on disk . This is indexed by inode of the dirfsp
* and name , and contrary to stat_cahce_lookup ( ) it does not
* vfs_stat ( ) the last component . This will be taken care of by an
* attempt to do a openat_pathref_fsp ( ) .
*/
static bool get_real_filename_cache_key (
TALLOC_CTX * mem_ctx ,
struct files_struct * dirfsp ,
const char * name ,
DATA_BLOB * _key )
{
struct file_id fid = vfs_file_id_from_sbuf (
dirfsp - > conn , & dirfsp - > fsp_name - > st ) ;
char * upper = NULL ;
uint8_t * key = NULL ;
size_t namelen , keylen ;
upper = talloc_strdup_upper ( mem_ctx , name ) ;
if ( upper = = NULL ) {
return false ;
}
namelen = talloc_get_size ( upper ) ;
keylen = namelen + sizeof ( fid ) ;
if ( keylen < sizeof ( fid ) ) {
TALLOC_FREE ( upper ) ;
return false ;
}
key = talloc_size ( mem_ctx , keylen ) ;
if ( key = = NULL ) {
TALLOC_FREE ( upper ) ;
return false ;
}
memcpy ( key , & fid , sizeof ( fid ) ) ;
memcpy ( key + sizeof ( fid ) , upper , namelen ) ;
TALLOC_FREE ( upper ) ;
* _key = ( DATA_BLOB ) { . data = key , . length = keylen , } ;
return true ;
}
2022-03-13 18:31:20 +03:00
static NTSTATUS get_real_filename ( connection_struct * conn ,
struct smb_filename * path ,
const char * name ,
TALLOC_CTX * mem_ctx ,
char * * found_name )
{
struct smb_filename * smb_dname = NULL ;
NTSTATUS status ;
smb_dname = cp_smb_filename_nostream ( talloc_tos ( ) , path ) ;
if ( smb_dname = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
again :
status = openat_pathref_fsp ( conn - > cwd_fsp , smb_dname ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_NAME_NOT_FOUND ) & &
S_ISLNK ( smb_dname - > st . st_ex_mode ) ) {
status = NT_STATUS_STOPPED_ON_SYMLINK ;
}
if ( NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_NAME_NOT_FOUND ) & &
( smb_dname - > twrp ! = 0 ) ) {
/*
* Retry looking at the non - snapshot path , copying the
* fallback mechanism from vfs_shadow_copy2 . c when
* shadow_copy2_convert ( ) fails . This path - based
* routine get_real_filename ( ) should go away and be
* replaced with a fd - based one , so spoiling it with a
* shadow_copy2 specific mechanism should not be too
* bad .
*/
smb_dname - > twrp = 0 ;
goto again ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " openat_pathref_fsp(%s) failed: %s \n " ,
smb_fname_str_dbg ( smb_dname ) ,
nt_errstr ( status ) ) ;
/*
* ENOTDIR and ELOOP both map to
* NT_STATUS_OBJECT_PATH_NOT_FOUND in the filename
* walk .
*/
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NOT_A_DIRECTORY ) | |
NT_STATUS_EQUAL ( status , NT_STATUS_STOPPED_ON_SYMLINK ) ) {
status = NT_STATUS_OBJECT_PATH_NOT_FOUND ;
}
return status ;
}
status = get_real_filename_at (
smb_dname - > fsp , name , mem_ctx , found_name ) ;
TALLOC_FREE ( smb_dname ) ;
return status ;
2009-05-02 04:28:38 +04:00
}
2008-01-20 01:25:36 +03:00
static NTSTATUS build_stream_path ( TALLOC_CTX * mem_ctx ,
connection_struct * conn ,
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04:00
struct smb_filename * smb_fname )
2008-01-20 01:25:36 +03:00
{
NTSTATUS status ;
2011-10-14 02:41:53 +04:00
unsigned int i , num_streams = 0 ;
2008-01-20 01:25:36 +03:00
struct stream_struct * streams = NULL ;
2021-02-08 13:42:22 +03:00
struct smb_filename * pathref = NULL ;
2008-01-20 01:25:36 +03:00
2009-06-23 02:26:56 +04:00
if ( SMB_VFS_STAT ( conn , smb_fname ) = = 0 ) {
2009-07-22 20:52:09 +04:00
DEBUG ( 10 , ( " '%s' exists \n " , smb_fname_str_dbg ( smb_fname ) ) ) ;
2008-01-20 01:25:36 +03:00
return NT_STATUS_OK ;
}
if ( errno ! = ENOENT ) {
2009-08-29 02:53:05 +04:00
DEBUG ( 10 , ( " vfs_stat failed: %s \n " , strerror ( errno ) ) ) ;
2008-01-20 01:25:36 +03:00
status = map_nt_error_from_unix ( errno ) ;
goto fail ;
}
2021-02-08 13:42:22 +03:00
if ( smb_fname - > fsp = = NULL ) {
status = synthetic_pathref ( mem_ctx ,
conn - > cwd_fsp ,
smb_fname - > base_name ,
NULL ,
NULL ,
smb_fname - > twrp ,
smb_fname - > flags ,
& pathref ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
if ( NT_STATUS_EQUAL ( status ,
NT_STATUS_OBJECT_NAME_NOT_FOUND ) ) {
TALLOC_FREE ( pathref ) ;
SET_STAT_INVALID ( smb_fname - > st ) ;
return NT_STATUS_OK ;
}
DBG_DEBUG ( " synthetic_pathref failed: %s \n " ,
nt_errstr ( status ) ) ;
goto fail ;
}
} else {
pathref = smb_fname ;
}
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04:00
/* Fall back to a case-insensitive scan of all streams on the file. */
2021-04-28 17:10:07 +03:00
status = vfs_fstreaminfo ( pathref - > fsp , mem_ctx ,
2011-10-17 22:10:29 +04:00
& num_streams , & streams ) ;
2008-01-20 01:25:36 +03:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_NAME_NOT_FOUND ) ) {
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04:00
SET_STAT_INVALID ( smb_fname - > st ) ;
2021-02-08 13:42:22 +03:00
TALLOC_FREE ( pathref ) ;
2008-01-20 01:25:36 +03:00
return NT_STATUS_OK ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
2021-04-28 17:10:07 +03:00
DEBUG ( 10 , ( " vfs_fstreaminfo failed: %s \n " , nt_errstr ( status ) ) ) ;
2008-01-20 01:25:36 +03:00
goto fail ;
}
for ( i = 0 ; i < num_streams ; i + + ) {
2021-12-21 19:12:55 +03:00
bool equal = sname_equal (
smb_fname - > stream_name ,
streams [ i ] . name ,
conn - > case_sensitive ) ;
DBG_DEBUG ( " comparing [%s] and [%s]: %sequal \n " ,
smb_fname - > stream_name ,
streams [ i ] . name ,
equal ? " " : " not " ) ;
if ( equal ) {
2008-01-20 01:25:36 +03:00
break ;
}
}
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04:00
/* Couldn't find the stream. */
2008-01-20 01:25:36 +03:00
if ( i = = num_streams ) {
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04:00
SET_STAT_INVALID ( smb_fname - > st ) ;
2021-02-08 13:42:22 +03:00
TALLOC_FREE ( pathref ) ;
2008-01-20 01:25:36 +03:00
TALLOC_FREE ( streams ) ;
return NT_STATUS_OK ;
}
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04:00
DEBUG ( 10 , ( " case insensitive stream. requested: %s, actual: %s \n " ,
smb_fname - > stream_name , streams [ i ] . name ) ) ;
2008-01-20 01:25:36 +03:00
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04:00
TALLOC_FREE ( smb_fname - > stream_name ) ;
2009-07-22 20:52:09 +04:00
smb_fname - > stream_name = talloc_strdup ( smb_fname , streams [ i ] . name ) ;
if ( smb_fname - > stream_name = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
s3: Change unix_convert (and its callers) to use struct smb_filename
This is the first of a series of patches that change path based
operations to operate on a struct smb_filename instead of a char *.
This same concept already exists in source4.
My goals for this series of patches are to eventually:
1) Solve the stream vs. posix filename that contains a colon ambiguity
that currently exists.
2) Make unix_convert the only function that parses the stream name.
3) Clean up the unix_convert API.
4) Change all path based vfs operation to take a struct smb_filename.
5) Make is_ntfs_stream_name() a constant operation that can simply
check the state of struct smb_filename rather than re-parse the
filename.
6) Eliminate the need for split_ntfs_stream_name() to exist.
My strategy is to start from the inside at unix_convert() and work my
way out through the vfs layer, call by call. This first patch does
just that, by changing unix_convert and all of its callers to operate
on struct smb_filename. Since this is such a large change, I plan on
pushing the patches in phases, where each phase keeps full
compatibility and passes make test.
The API of unix_convert has been simplified from:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
bool allow_wcard_last_component,
char **pp_conv_path,
char **pp_saved_last_component,
SMB_STRUCT_STAT *pst)
to:
NTSTATUS unix_convert(TALLOC_CTX *ctx,
connection_struct *conn,
const char *orig_path,
struct smb_filename *smb_fname,
uint32_t ucf_flags)
Currently the smb_filename struct looks like:
struct smb_filename {
char *base_name;
char *stream_name;
char *original_lcomp;
SMB_STRUCT_STAT st;
};
One key point here is the decision to break up the base_name and
stream_name. I have introduced a helper function called
get_full_smb_filename() that takes an smb_filename struct and
allocates the full_name. I changed the callers of unix_convert() to
subsequently call get_full_smb_filename() for the time being, but I
plan to eventually eliminate get_full_smb_filename().
2009-04-08 00:39:57 +04:00
SET_STAT_INVALID ( smb_fname - > st ) ;
2008-01-20 01:25:36 +03:00
2009-06-23 02:26:56 +04:00
if ( SMB_VFS_STAT ( conn , smb_fname ) = = 0 ) {
2009-07-22 20:52:09 +04:00
DEBUG ( 10 , ( " '%s' exists \n " , smb_fname_str_dbg ( smb_fname ) ) ) ;
2009-06-23 02:26:56 +04:00
}
status = NT_STATUS_OK ;
2008-01-20 01:25:36 +03:00
fail :
2021-02-08 13:42:22 +03:00
TALLOC_FREE ( pathref ) ;
2008-01-20 01:25:36 +03:00
TALLOC_FREE ( streams ) ;
return status ;
}
2009-06-19 00:13:38 +04:00
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 )
{
struct smb_filename * smb_fname = NULL ;
char * last_slash = NULL ;
char * orig_lcomp ;
char * fname = NULL ;
2021-12-04 03:14:08 +03:00
NTTIME twrp = 0 ;
2020-03-25 20:00:57 +03:00
NTSTATUS status ;
if ( ucf_flags & UCF_DFS_PATHNAME ) {
2020-09-30 00:24:10 +03:00
status = dfs_redirect ( ctx ,
2020-03-25 20:00:57 +03:00
conn ,
filename_in ,
ucf_flags ,
! conn - > sconn - > using_smb2 ,
2021-12-04 03:14:08 +03:00
& twrp ,
2020-09-30 00:17:35 +03:00
& fname ) ;
2020-03-25 20:00:57 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2020-09-30 00:24:10 +03:00
DBG_DEBUG ( " dfs_redirect "
2020-03-25 20:00:57 +03:00
" failed for name %s with %s \n " ,
filename_in ,
nt_errstr ( status ) ) ;
return NULL ;
}
filename_in = fname ;
ucf_flags & = ~ UCF_DFS_PATHNAME ;
}
/*
* NB . We don ' t need to care about
* is_fake_file_path ( filename_in ) here as these
* code paths don ' t ever return original_lcomp
* or use it anyway .
*/
if ( ucf_flags & UCF_GMT_PATHNAME ) {
/*
* Ensure we don ' t return a @ GMT
* value as the last component .
*/
smb_fname = synthetic_smb_fname ( ctx ,
filename_in ,
NULL ,
NULL ,
2021-12-04 03:14:08 +03:00
twrp ,
2020-03-25 20:00:57 +03:00
0 ) ;
if ( smb_fname = = NULL ) {
TALLOC_FREE ( fname ) ;
return NULL ;
}
2020-05-04 14:51:37 +03:00
status = canonicalize_snapshot_path ( smb_fname ,
ucf_flags ,
2021-12-04 03:14:08 +03:00
twrp ) ;
2020-03-25 20:00:57 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( fname ) ;
TALLOC_FREE ( smb_fname ) ;
return NULL ;
}
filename_in = smb_fname - > base_name ;
}
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 ) ;
}
/* We're done with any temp names here. */
TALLOC_FREE ( smb_fname ) ;
TALLOC_FREE ( fname ) ;
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 ;
}
2009-07-24 23:13:07 +04:00
/**
* Go through all the steps to validate a filename .
*
* @ param ctx talloc_ctx to allocate memory with .
* @ param conn connection struct for vfs calls .
2012-04-24 16:07:00 +04:00
* @ param smbreq SMB request if we ' re using privileges .
2009-07-24 23:13:07 +04:00
* @ param name_in The unconverted name .
* @ param ucf_flags flags to pass through to unix_convert ( ) .
2018-11-24 11:05:37 +03:00
* @ param twrp Optional VSS time
2009-07-24 23:13:07 +04:00
* @ param p_cont_wcard If not NULL , will be set to true if the dfs path
* resolution detects a wildcard .
2020-03-31 19:57:55 +03:00
* @ param _smb_fname The final converted name will be allocated if the
2009-07-24 23:13:07 +04:00
* return is NT_STATUS_OK .
*
2018-05-04 23:24:00 +03:00
* @ return NT_STATUS_OK if all operations completed successfully , appropriate
2009-07-24 23:13:07 +04:00
* error otherwise .
*/
2021-12-03 21:19:38 +03:00
NTSTATUS filename_convert ( TALLOC_CTX * ctx ,
connection_struct * conn ,
const char * name_in ,
uint32_t ucf_flags ,
NTTIME twrp ,
struct smb_filename * * _smb_fname )
2009-06-19 00:13:38 +04:00
{
2020-03-26 17:08:53 +03:00
struct smb_filename * smb_fname = NULL ;
2009-06-19 00:13:38 +04:00
NTSTATUS status ;
2020-03-26 17:08:53 +03:00
* _smb_fname = NULL ;
2009-06-19 00:13:38 +04:00
2017-05-18 22:29:50 +03:00
if ( ucf_flags & UCF_DFS_PATHNAME ) {
2017-05-18 21:36:56 +03:00
char * fname = NULL ;
2021-12-04 03:14:08 +03:00
NTTIME dfs_twrp = 0 ;
2020-09-30 00:24:10 +03:00
status = dfs_redirect ( ctx , conn ,
2009-06-19 00:13:38 +04:00
name_in ,
2016-03-25 21:23:29 +03:00
ucf_flags ,
2012-05-23 15:22:47 +04:00
! conn - > sconn - > using_smb2 ,
2021-12-04 03:14:08 +03:00
& dfs_twrp ,
2020-09-30 00:17:35 +03:00
& fname ) ;
2017-05-18 21:34:33 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2021-12-03 21:19:38 +03:00
DBG_DEBUG ( " dfs_redirect "
2017-05-18 21:34:33 +03:00
" failed for name %s with %s \n " ,
name_in ,
2021-12-03 21:19:38 +03:00
nt_errstr ( status ) ) ;
2017-05-18 21:34:33 +03:00
return status ;
}
2017-05-18 21:36:56 +03:00
name_in = fname ;
2017-05-18 22:29:50 +03:00
ucf_flags & = ~ UCF_DFS_PATHNAME ;
2021-12-04 03:14:08 +03:00
if ( twrp = = 0 & & dfs_twrp ! = 0 ) {
twrp = dfs_twrp ;
}
2009-06-19 00:13:38 +04:00
}
2009-07-24 23:13:07 +04:00
2009-08-19 11:58:38 +04:00
if ( is_fake_file_path ( name_in ) ) {
2020-03-26 17:08:53 +03:00
smb_fname = synthetic_smb_fname_split ( ctx ,
2016-03-10 03:00:47 +03:00
name_in ,
( ucf_flags & UCF_POSIX_PATHNAMES ) ) ;
2020-03-26 17:08:53 +03:00
if ( smb_fname = = NULL ) {
2013-04-12 13:49:19 +04:00
return NT_STATUS_NO_MEMORY ;
}
2020-03-26 17:08:53 +03:00
smb_fname - > st = ( SMB_STRUCT_STAT ) { . st_ex_nlink = 1 } ;
2021-06-07 20:02:56 +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 } ;
2020-03-26 17:08:53 +03:00
* _smb_fname = smb_fname ;
2013-04-12 13:49:19 +04:00
return NT_STATUS_OK ;
2009-08-19 11:58:38 +04:00
}
2020-04-30 16:14:36 +03:00
status = unix_convert ( ctx , conn , name_in , twrp , & smb_fname , ucf_flags ) ;
2009-06-19 00:13:38 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2021-12-03 21:19:38 +03:00
DBG_DEBUG ( " unix_convert failed "
2009-06-19 00:13:38 +04:00
" for name %s with %s \n " ,
2017-05-18 21:36:56 +03:00
name_in ,
2021-12-03 21:19:38 +03:00
nt_errstr ( status ) ) ;
2009-06-19 00:13:38 +04:00
return status ;
}
2020-10-22 12:04:59 +03:00
if ( ( ucf_flags & UCF_POSIX_PATHNAMES ) & &
VALID_STAT ( smb_fname - > st ) & &
S_ISLNK ( smb_fname - > st . st_ex_mode ) )
{
2020-03-26 17:08:53 +03:00
status = check_veto_path ( conn , smb_fname ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( smb_fname ) ;
return status ;
}
2012-02-25 02:12:05 +04:00
} else {
2021-12-03 21:10:45 +03:00
status = check_name ( conn , smb_fname ) ;
2012-02-25 02:12:05 +04:00
}
2009-06-19 00:13:38 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2021-12-03 21:19:38 +03:00
DBG_NOTICE ( " check_name failed "
2009-06-19 00:13:38 +04:00
" for name %s with %s \n " ,
2020-03-26 17:08:53 +03:00
smb_fname_str_dbg ( smb_fname ) ,
2021-12-03 21:19:38 +03:00
nt_errstr ( status ) ) ;
2020-03-26 17:08:53 +03:00
TALLOC_FREE ( smb_fname ) ;
2009-06-19 00:13:38 +04:00
return status ;
}
2009-07-02 03:14:40 +04:00
2020-06-13 20:16:39 +03:00
if ( ! VALID_STAT ( smb_fname - > st ) ) {
DBG_DEBUG ( " [%s] does not exist, skipping pathref fsp \n " ,
smb_fname_str_dbg ( smb_fname ) ) ;
* _smb_fname = smb_fname ;
return NT_STATUS_OK ;
}
status = openat_pathref_fsp ( conn - > cwd_fsp , smb_fname ) ;
2021-02-02 13:18:54 +03:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_NAME_NOT_FOUND ) ) {
2020-06-13 20:16:39 +03:00
/*
2021-02-02 13:18:54 +03:00
* We deal with symlinks here as we do in
2020-06-13 20:16:39 +03:00
* SMB_VFS_CREATE_FILE ( ) : return success for POSIX clients with
* the notable difference that there will be no fsp in
* smb_fname - > fsp .
*
* For Windows ( non POSIX ) clients fail with
* NT_STATUS_OBJECT_NAME_NOT_FOUND .
*/
2021-02-02 13:18:54 +03:00
if ( smb_fname - > flags & SMB_FILENAME_POSIX_PATH & &
S_ISLNK ( smb_fname - > st . st_ex_mode ) )
{
2020-06-13 20:16:39 +03:00
status = NT_STATUS_OK ;
}
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
2021-11-25 18:52:41 +03:00
DBG_DEBUG ( " openat_pathref_fsp [%s] failed: %s \n " ,
2021-04-06 15:47:39 +03:00
smb_fname_str_dbg ( smb_fname ) ,
nt_errstr ( status ) ) ;
2020-06-13 20:16:39 +03:00
return status ;
}
2020-03-26 17:08:53 +03:00
* _smb_fname = smb_fname ;
2009-06-19 00:13:38 +04:00
return status ;
}
2012-02-25 02:12:05 +04:00
2021-12-03 21:35:09 +03:00
/*
* Deal with the SMB1 semantics of sending a pathname with a
* wildcard as the terminal component for a SMB1search or
* trans2 findfirst .
*/
NTSTATUS filename_convert_smb1_search_path ( TALLOC_CTX * ctx ,
connection_struct * conn ,
2022-08-03 19:27:37 +03:00
char * name_in ,
2021-12-03 21:35:09 +03:00
uint32_t ucf_flags ,
struct smb_filename * * _smb_fname_out ,
char * * _mask_out )
{
NTSTATUS status ;
char * p = NULL ;
char * mask = NULL ;
struct smb_filename * smb_fname = NULL ;
bool posix_pathnames = ( ucf_flags & UCF_POSIX_PATHNAMES ) ;
NTTIME twrp = 0 ;
* _smb_fname_out = NULL ;
* _mask_out = NULL ;
DBG_DEBUG ( " name_in: %s \n " , name_in ) ;
2022-08-03 19:50:02 +03:00
if ( ucf_flags & UCF_GMT_PATHNAME ) {
extract_snapshot_token ( name_in , ucf_flags , & twrp ) ;
ucf_flags & = ~ UCF_GMT_PATHNAME ;
}
2021-12-03 21:35:09 +03:00
if ( ucf_flags & UCF_DFS_PATHNAME ) {
/*
* We ' ve been given a raw DFS pathname .
2022-08-03 19:50:02 +03:00
* In Windows mode this is separated by ' \ '
* characters , in POSIX by ' / ' characters .
2021-12-03 21:35:09 +03:00
*/
char path_sep = posix_pathnames ? ' / ' : ' \\ ' ;
char * fname = NULL ;
char * name_in_copy = NULL ;
char * last_component = NULL ;
/* Work on a copy of name_in. */
2022-08-03 19:50:02 +03:00
name_in_copy = talloc_strdup ( ctx , name_in ) ;
2021-12-03 21:35:09 +03:00
if ( name_in_copy = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
/*
* Now we know that the last component is the
* wildcard . Copy it and truncate to remove it .
*/
p = strrchr_m ( name_in_copy , path_sep ) ;
if ( p = = NULL ) {
2022-08-03 19:33:48 +03:00
last_component = talloc_strdup ( ctx , name_in_copy ) ;
2021-12-03 21:35:09 +03:00
name_in_copy [ 0 ] = ' \0 ' ;
} else {
2022-08-03 19:33:48 +03:00
last_component = talloc_strdup ( ctx , p + 1 ) ;
2021-12-03 21:35:09 +03:00
* p = ' \0 ' ;
}
if ( last_component = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2022-08-03 19:50:02 +03:00
DBG_DEBUG ( " name_in_copy: %s \n " , name_in_copy ) ;
2021-12-03 21:35:09 +03:00
/*
* Now we can call dfs_redirect ( )
* on the name without wildcard .
*/
2022-08-03 19:33:48 +03:00
status = dfs_redirect ( ctx ,
2021-12-03 21:35:09 +03:00
conn ,
name_in_copy ,
ucf_flags ,
! conn - > sconn - > using_smb2 ,
NULL ,
& fname ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " dfs_redirect "
" failed for name %s with %s \n " ,
name_in_copy ,
nt_errstr ( status ) ) ;
return status ;
}
/* Add the last component back. */
if ( fname [ 0 ] = = ' \0 ' ) {
2022-08-03 19:33:48 +03:00
name_in = talloc_strdup ( ctx , last_component ) ;
2021-12-03 21:35:09 +03:00
} else {
2022-08-03 19:33:48 +03:00
name_in = talloc_asprintf ( ctx ,
2021-12-03 21:35:09 +03:00
" %s%c%s " ,
fname ,
path_sep ,
last_component ) ;
}
if ( name_in = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
ucf_flags & = ~ UCF_DFS_PATHNAME ;
DBG_DEBUG ( " After DFS redirect name_in: %s \n " , name_in ) ;
}
2022-08-03 19:33:48 +03:00
smb_fname = synthetic_smb_fname ( ctx ,
2021-12-03 21:35:09 +03:00
name_in ,
NULL ,
NULL ,
twrp ,
posix_pathnames ?
SMB_FILENAME_POSIX_PATH : 0 ) ;
if ( smb_fname = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
/* Get the original lcomp. */
2022-08-03 19:33:48 +03:00
mask = get_original_lcomp ( ctx ,
2021-12-03 21:35:09 +03:00
conn ,
name_in ,
ucf_flags ) ;
if ( mask = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
if ( mask [ 0 ] = = ' \0 ' ) {
/* Windows and OS/2 systems treat search on the root as * */
TALLOC_FREE ( mask ) ;
2022-08-03 19:33:48 +03:00
mask = talloc_strdup ( ctx , " * " ) ;
2021-12-03 21:35:09 +03:00
if ( mask = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
}
DBG_DEBUG ( " mask = %s \n " , mask ) ;
/*
* Remove the terminal component so
* filename_convert never sees the mask .
*/
p = strrchr_m ( smb_fname - > base_name , ' / ' ) ;
if ( p = = NULL ) {
/* filename_convert handles a '\0' base_name. */
smb_fname - > base_name [ 0 ] = ' \0 ' ;
} else {
* p = ' \0 ' ;
}
DBG_DEBUG ( " For filename_convert: smb_fname = %s \n " ,
smb_fname_str_dbg ( smb_fname ) ) ;
/* Convert the parent directory path. */
2022-08-03 19:33:48 +03:00
status = filename_convert ( ctx ,
2021-12-03 21:35:09 +03:00
conn ,
smb_fname - > base_name ,
ucf_flags ,
smb_fname - > twrp ,
& smb_fname ) ;
2022-08-03 19:33:48 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2021-12-03 21:35:09 +03:00
DBG_DEBUG ( " filename_convert error for %s: %s \n " ,
smb_fname_str_dbg ( smb_fname ) ,
nt_errstr ( status ) ) ;
}
2022-08-03 19:33:48 +03:00
* _smb_fname_out = talloc_move ( ctx , & smb_fname ) ;
* _mask_out = talloc_move ( ctx , & mask ) ;
2021-12-03 21:35:09 +03:00
return status ;
}
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 ;
}
/*
* No slash , dir is emtpy
*/
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 ;
}
/*
* Open smb_fname_rel - > fsp as a pathref fsp with a case insensitive
* fallback using GETREALFILENAME_CACHE and get_real_filename_at ( ) if
* the first attempt based on the filename sent by the client gives
* ENOENT .
*/
static NTSTATUS openat_pathref_fsp_case_insensitive (
struct files_struct * dirfsp ,
struct smb_filename * smb_fname_rel ,
uint32_t ucf_flags )
{
const bool posix = ( ucf_flags & UCF_POSIX_PATHNAMES ) ;
DATA_BLOB cache_key = { . data = NULL , } ;
char * found_name = NULL ;
NTSTATUS status ;
bool ok ;
SET_STAT_INVALID ( smb_fname_rel - > st ) ;
status = openat_pathref_fsp ( dirfsp , smb_fname_rel ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
return NT_STATUS_OK ;
}
if ( VALID_STAT ( smb_fname_rel - > st ) ) {
/*
* We got an error although the object existed . Might
* be a symlink we don ' t want .
*/
return status ;
}
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_NAME_NOT_FOUND ) ) {
/*
* Only retry on ENOENT
*/
return status ;
}
if ( posix | | dirfsp - > conn - > case_sensitive ) {
/*
* Only return case insensitive if required
*/
return status ;
}
if ( lp_stat_cache ( ) ) {
char * base_name = smb_fname_rel - > base_name ;
DATA_BLOB value = { . data = NULL } ;
ok = get_real_filename_cache_key (
talloc_tos ( ) , dirfsp , base_name , & cache_key ) ;
if ( ! ok ) {
/*
* probably ENOMEM , just bail
*/
return status ;
}
DO_PROFILE_INC ( statcache_lookups ) ;
ok = memcache_lookup (
NULL , GETREALFILENAME_CACHE , cache_key , & value ) ;
if ( ! ok ) {
DO_PROFILE_INC ( statcache_misses ) ;
goto lookup ;
}
DO_PROFILE_INC ( statcache_hits ) ;
TALLOC_FREE ( smb_fname_rel - > base_name ) ;
smb_fname_rel - > base_name = talloc_memdup (
smb_fname_rel , value . data , value . length ) ;
if ( smb_fname_rel - > base_name = = NULL ) {
TALLOC_FREE ( cache_key . data ) ;
return NT_STATUS_NO_MEMORY ;
}
status = openat_pathref_fsp ( dirfsp , smb_fname_rel ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( cache_key . data ) ;
return NT_STATUS_OK ;
}
memcache_delete ( NULL , GETREALFILENAME_CACHE , cache_key ) ;
}
lookup :
status = get_real_filename_at (
dirfsp , smb_fname_rel - > base_name , smb_fname_rel , & found_name ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_ACCESS_DENIED ) & &
( ucf_flags & UCF_PREP_CREATEFILE ) ) {
/*
* dropbox
*/
status = NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
if ( NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( smb_fname_rel - > base_name ) ;
smb_fname_rel - > base_name = found_name ;
status = openat_pathref_fsp ( dirfsp , smb_fname_rel ) ;
}
if ( NT_STATUS_IS_OK ( status ) & & ( cache_key . data ! = NULL ) ) {
DATA_BLOB value = {
. data = ( uint8_t * ) smb_fname_rel - > base_name ,
. length = strlen ( smb_fname_rel - > base_name ) + 1 ,
} ;
memcache_add ( NULL , GETREALFILENAME_CACHE , cache_key , value ) ;
}
TALLOC_FREE ( cache_key . data ) ;
return status ;
}
/*
* Split up name_in as sent by the client into a directory pathref fsp
* and a relative smb_filename .
*/
2022-07-14 20:47:23 +03:00
static const char * previous_slash ( const char * name_in , const char * slash )
{
const char * prev = name_in ;
while ( true ) {
const char * next = strchr_m ( prev , ' / ' ) ;
SMB_ASSERT ( next ! = NULL ) ; /* we have at least one slash */
if ( next = = slash ) {
break ;
}
prev = next + 1 ;
} ;
if ( prev = = name_in ) {
/* no previous slash */
return NULL ;
}
return prev ;
}
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 ) {
/* no previous slash */
parent = name_in ;
}
ret = talloc_asprintf (
mem_ctx ,
" %.*s%s%s " ,
( int ) ( parent - name_in ) ,
name_in ,
substitute ,
p_unparsed ) ;
return ret ;
}
/*
* 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 ,
char * * _substitute ,
size_t * _unparsed )
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 ;
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 ;
if ( ucf_flags & UCF_DFS_PATHNAME ) {
char * fname = NULL ;
NTTIME dfs_twrp = 0 ;
status = dfs_redirect (
mem_ctx ,
conn ,
name_in ,
ucf_flags ,
! conn - > sconn - > using_smb2 ,
& dfs_twrp ,
& fname ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " dfs_redirect "
" failed for name %s with %s \n " ,
name_in ,
nt_errstr ( status ) ) ;
return status ;
}
name_in = fname ;
ucf_flags & = ~ UCF_DFS_PATHNAME ;
if ( twrp = = 0 & & dfs_twrp ! = 0 ) {
twrp = dfs_twrp ;
}
}
if ( is_fake_file_path ( name_in ) | | conn - > printer ) {
smb_fname = synthetic_smb_fname_split ( mem_ctx , name_in , posix ) ;
if ( smb_fname = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
smb_fname - > st = ( SMB_STRUCT_STAT ) { . st_ex_nlink = 1 } ;
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 ;
}
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 {
char * substitute = NULL ;
size_t unparsed = 0 ;
status = openat_pathref_dirfsp_nosymlink (
mem_ctx ,
conn ,
dirname ,
0 ,
& smb_dirname ,
& unparsed ,
& substitute ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_STOPPED_ON_SYMLINK ) ) {
size_t name_in_len = strlen ( name_in ) ;
size_t dirname_len = strlen ( dirname ) ;
SMB_ASSERT ( name_in_len > = dirname_len ) ;
* _substitute = substitute ;
* _unparsed = unparsed + ( name_in_len - dirname_len ) ;
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-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 ;
}
status = openat_pathref_fsp_case_insensitive (
smb_dirname - > fsp , smb_fname_rel , ucf_flags ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_NAME_NOT_FOUND ) ) {
char * normalized = NULL ;
if ( VALID_STAT ( smb_fname_rel - > st ) ) {
2022-08-02 00:24:31 +03:00
# if defined(WITH_SMB1SERVER)
/*
* In SMB1 posix mode , if this is a symlink ,
* allow access to the name with a NULL smb_fname - > fsp .
*/
if ( ! conn - > sconn - > using_smb2 & &
posix & &
S_ISLNK ( smb_fname_rel - > st . st_ex_mode ) ) {
SMB_ASSERT ( smb_fname_rel - > fsp = = NULL ) ;
SMB_ASSERT ( streamname = = NULL ) ;
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 ;
}
# endif
2021-12-08 09:08:10 +03:00
/*
* NT_STATUS_OBJECT_NAME_NOT_FOUND is
* misleading : The object exists but might be
* a symlink pointing outside the share .
*/
goto fail ;
}
/*
* 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 ;
}
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 ;
}
goto done ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
done :
* _dirfsp = smb_dirname - > fsp ;
* _smb_fname = smb_fname ;
smb_fname_fsp_unlink ( smb_fname_rel ) ;
TALLOC_FREE ( smb_fname_rel ) ;
return NT_STATUS_OK ;
fail :
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 )
{
char * substitute = NULL ;
size_t unparsed = 0 ;
NTSTATUS status ;
char * target = NULL ;
char * abs_target = NULL ;
char * abs_target_canon = NULL ;
size_t symlink_redirects = 0 ;
bool in_share ;
next :
if ( symlink_redirects > 40 ) {
return NT_STATUS_OBJECT_PATH_NOT_FOUND ;
}
status = filename_convert_dirfsp_nosymlink (
mem_ctx ,
conn ,
name_in ,
ucf_flags ,
twrp ,
_dirfsp ,
_smb_fname ,
& substitute ,
& unparsed ) ;
2022-08-02 03:55:23 +03:00
# if defined(WITH_SMB1SERVER)
/*
* This isn ' t 100 % correct , but it gets us close enough
* to the old behavior for SMB1 + POSIX libsmbclient . If we went through a
* symlink , and we got NT_STATUS_ACCESS_DENIED on the directory
* containing the target , just don ' t allow the client to see the
* intermediate path .
*/
if ( ! conn - > sconn - > using_smb2 & &
( ucf_flags & UCF_POSIX_PATHNAMES ) & &
symlink_redirects > 0 & &
NT_STATUS_EQUAL ( status , NT_STATUS_ACCESS_DENIED ) ) {
return NT_STATUS_OBJECT_PATH_NOT_FOUND ;
}
# endif
2022-07-14 20:47:23 +03:00
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_STOPPED_ON_SYMLINK ) ) {
return status ;
}
if ( ! lp_follow_symlinks ( SNUM ( conn ) ) ) {
return NT_STATUS_OBJECT_PATH_NOT_FOUND ;
}
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
target = symlink_target_path ( mem_ctx , name_in , substitute , unparsed ) ;
if ( target = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
DBG_DEBUG ( " name_in: %s, substitute: %s, unparsed: %zu, target=%s \n " ,
name_in ,
substitute ,
unparsed ,
target ) ;
if ( target [ 0 ] = = ' / ' ) {
abs_target = target ;
} else {
abs_target = talloc_asprintf (
mem_ctx , " %s/%s " , conn - > connectpath , target ) ;
if ( abs_target = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
}
abs_target_canon = canonicalize_absolute_path ( mem_ctx , abs_target ) ;
if ( abs_target_canon = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
DBG_DEBUG ( " abs_target_canon=%s \n " , abs_target_canon ) ;
in_share = strncmp (
abs_target_canon ,
conn - > connectpath ,
strlen ( conn - > connectpath ) ) = = 0 ;
if ( ! in_share ) {
DBG_DEBUG ( " wide link to %s \n " , abs_target_canon ) ;
return NT_STATUS_OBJECT_PATH_NOT_FOUND ;
}
name_in = talloc_strdup (
mem_ctx , abs_target_canon + strlen ( conn - > connectpath ) + 1 ) ;
symlink_redirects + = 1 ;
goto next ;
}
2020-04-14 18:44:37 +03:00
/*
* 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 ;
if ( dirfsp = = dirfsp - > conn - > cwd_fsp | |
2020-12-14 17:53:11 +03:00
ISDOT ( dirfsp - > fsp_name - > base_name ) | |
atname - > base_name [ 0 ] = = ' / ' )
2020-04-14 18:44:37 +03:00
{
path = talloc_strdup ( mem_ctx , atname - > base_name ) ;
} else {
path = talloc_asprintf ( mem_ctx , " %s/%s " ,
dirfsp - > fsp_name - > base_name ,
atname - > base_name ) ;
}
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 ;
}