2022-05-11 15:19:44 +03:00
/*
2002-01-30 09:08:46 +03:00
Unix SMB / CIFS implementation .
1998-08-15 11:27:34 +04:00
Files [ ] structure handling
Copyright ( C ) Andrew Tridgell 1998
2010-03-22 11:11:24 +03:00
1998-08-15 11:27:34 +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-15 11:27:34 +04:00
( at your option ) any later version .
2010-03-22 11:11:24 +03:00
1998-08-15 11:27:34 +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 .
2010-03-22 11:11:24 +03:00
1998-08-15 11:27:34 +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-15 11:27:34 +04:00
*/
# include "includes.h"
2011-03-22 18:57:01 +03:00
# include "smbd/smbd.h"
2009-01-08 14:03:45 +03:00
# include "smbd/globals.h"
2021-10-26 14:48:28 +03:00
# include "smbd/smbXsrv_open.h"
2010-10-12 08:27:50 +04:00
# include "libcli/security/security.h"
2011-05-05 13:25:29 +04:00
# include "util_tdb.h"
2011-07-07 15:04:31 +04:00
# include "lib/util/bitmap.h"
2022-07-14 20:44:04 +03:00
# include "lib/util/strv.h"
2023-10-18 12:50:20 +03:00
# include "lib/util/memcache.h"
2023-08-01 16:57:50 +03:00
# include "libcli/smb/reparse.h"
1998-08-15 11:27:34 +04:00
1998-08-17 07:06:20 +04:00
# define FILE_HANDLE_OFFSET 0x1000
2020-12-16 17:02:11 +03:00
static NTSTATUS fsp_attach_smb_fname ( struct files_struct * fsp ,
struct smb_filename * * _smb_fname ) ;
2012-06-07 18:13:36 +04:00
/**
* create new fsp to be used for file_new or a durable handle reconnect
*/
NTSTATUS fsp_new ( struct connection_struct * conn , TALLOC_CTX * mem_ctx ,
files_struct * * result )
{
NTSTATUS status = NT_STATUS_NO_MEMORY ;
files_struct * fsp = NULL ;
struct smbd_server_connection * sconn = conn - > sconn ;
fsp = talloc_zero ( mem_ctx , struct files_struct ) ;
if ( fsp = = NULL ) {
goto fail ;
}
/*
* This can ' t be a child of fsp because the file_handle can be ref ' d
* when doing a dos / fcb open , which will then share the file_handle
* across multiple fsps .
*/
2020-09-27 22:16:03 +03:00
fsp - > fh = fd_handle_create ( mem_ctx ) ;
2012-06-07 18:13:36 +04:00
if ( fsp - > fh = = NULL ) {
goto fail ;
}
2020-11-16 13:06:08 +03:00
fsp - > fsp_flags . use_ofd_locks = ! lp_smbd_force_process_locks ( SNUM ( conn ) ) ;
# ifndef HAVE_OFD_LOCKS
fsp - > fsp_flags . use_ofd_locks = false ;
2019-01-30 20:45:34 +03:00
# endif
2020-11-16 13:06:08 +03:00
2020-09-28 11:37:36 +03:00
fh_set_refcount ( fsp - > fh , 1 ) ;
2020-09-26 22:46:51 +03:00
fsp_set_fd ( fsp , - 1 ) ;
2012-06-07 18:13:36 +04:00
2012-06-12 19:56:43 +04:00
fsp - > fnum = FNUM_FIELD_INVALID ;
2012-06-07 18:13:36 +04:00
fsp - > conn = conn ;
2019-12-02 18:30:50 +03:00
fsp - > close_write_time = make_omit_timespec ( ) ;
2012-06-07 18:13:36 +04:00
DLIST_ADD ( sconn - > files , fsp ) ;
sconn - > num_files + = 1 ;
conn - > num_files_open + + ;
2020-12-17 10:55:31 +03:00
DBG_INFO ( " allocated files structure (%u used) \n " ,
( unsigned int ) sconn - > num_files ) ;
2012-06-07 18:13:36 +04:00
* result = fsp ;
return NT_STATUS_OK ;
fail :
if ( fsp ! = NULL ) {
TALLOC_FREE ( fsp - > fh ) ;
}
TALLOC_FREE ( fsp ) ;
return status ;
}
2019-09-16 13:54:32 +03:00
void fsp_set_gen_id ( files_struct * fsp )
{
static uint64_t gen_id = 1 ;
/*
* A billion of 64 - bit increments per second gives us
* more than 500 years of runtime without wrap .
*/
2020-09-28 11:35:32 +03:00
gen_id + + ;
fh_set_gen_id ( fsp - > fh , gen_id ) ;
2019-09-16 13:54:32 +03:00
}
2001-10-21 01:59:34 +04:00
/****************************************************************************
Find first available file slot .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-06-19 08:31:51 +03:00
NTSTATUS fsp_bind_smb ( struct files_struct * fsp , struct smb_request * req )
{
struct smbXsrv_open * op = NULL ;
NTTIME now ;
NTSTATUS status ;
if ( req = = NULL ) {
DBG_DEBUG ( " INTERNAL_OPEN_ONLY, skipping smbXsrv_open \n " ) ;
return NT_STATUS_OK ;
}
now = timeval_to_nttime ( & fsp - > open_time ) ;
status = smbXsrv_open_create ( req - > xconn ,
fsp - > conn - > session_info ,
now ,
& op ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
fsp - > op = op ;
op - > compat = fsp ;
fsp - > fnum = op - > local_id ;
fsp - > mid = req - > mid ;
req - > chain_fsp = fsp ;
DBG_DEBUG ( " fsp [%s] mid [% " PRIu64 " ] \n " ,
fsp_str_dbg ( fsp ) , fsp - > mid ) ;
return NT_STATUS_OK ;
}
2008-10-09 17:38:53 +04:00
NTSTATUS file_new ( struct smb_request * req , connection_struct * conn ,
files_struct * * result )
1998-08-15 11:27:34 +04:00
{
2010-09-27 08:06:02 +04:00
struct smbd_server_connection * sconn = conn - > sconn ;
2005-09-30 21:13:37 +04:00
files_struct * fsp ;
2009-07-11 01:50:37 +04:00
NTSTATUS status ;
1998-08-15 11:27:34 +04:00
2012-06-07 18:13:36 +04:00
status = fsp_new ( conn , conn , & fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
GetTimeOfDay ( & fsp - > open_time ) ;
2020-06-19 08:31:51 +03:00
status = fsp_bind_smb ( fsp , req ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
file_free ( NULL , fsp ) ;
return status ;
2012-05-24 12:43:56 +04:00
}
2002-10-23 02:17:29 +04:00
2019-09-16 13:54:32 +03:00
fsp_set_gen_id ( fsp ) ;
2009-07-11 01:50:37 +04:00
/*
* Create an smb_filename with " " for the base_name . There are very
* few NULL checks , so make sure it ' s initialized with something . to
* be safe until an audit can be done .
*/
2020-05-03 16:07:45 +03:00
fsp - > fsp_name = synthetic_smb_fname ( fsp ,
" " ,
NULL ,
NULL ,
2020-04-30 12:48:32 +03:00
0 ,
2020-05-03 16:07:45 +03:00
0 ) ;
2013-04-15 13:03:44 +04:00
if ( fsp - > fsp_name = = NULL ) {
2012-05-24 13:22:11 +04:00
file_free ( NULL , fsp ) ;
2013-04-15 13:03:44 +04:00
return NT_STATUS_NO_MEMORY ;
2009-07-11 01:50:37 +04:00
}
2020-12-17 10:55:31 +03:00
DBG_INFO ( " new file %s \n " , fsp_fnum_dbg ( fsp ) ) ;
1998-08-17 07:52:05 +04:00
2008-09-27 05:39:03 +04:00
/* A new fsp invalidates the positive and
negative fsp_fi_cache as the new fsp is pushed
at the start of the list and we search from
a cache hit to the * end * of the list . */
2010-09-27 08:06:02 +04:00
ZERO_STRUCT ( sconn - > fsp_fi_cache ) ;
2006-07-11 22:01:26 +04:00
* result = fsp ;
return NT_STATUS_OK ;
1998-08-15 11:27:34 +04:00
}
2020-12-23 13:50:34 +03:00
NTSTATUS create_internal_fsp ( connection_struct * conn ,
const struct smb_filename * smb_fname ,
struct files_struct * * _fsp )
{
struct files_struct * fsp = NULL ;
NTSTATUS status ;
status = file_new ( NULL , conn , & fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
status = fsp_set_smb_fname ( fsp , smb_fname ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
file_free ( NULL , fsp ) ;
return status ;
}
* _fsp = fsp ;
return NT_STATUS_OK ;
}
2020-03-23 13:36:42 +03:00
/*
* Create an internal fsp for an * existing * directory .
*
* This should only be used by callers in the VFS that need to control the
2023-11-18 19:38:09 +03:00
* opening of the directory . Otherwise use open_internal_dirfsp ( ) .
2020-03-23 13:36:42 +03:00
*/
2020-05-15 16:14:26 +03:00
NTSTATUS create_internal_dirfsp ( connection_struct * conn ,
const struct smb_filename * smb_dname ,
struct files_struct * * _fsp )
2020-03-23 13:36:42 +03:00
{
struct files_struct * fsp = NULL ;
NTSTATUS status ;
2020-12-23 13:50:34 +03:00
status = create_internal_fsp ( conn , smb_dname , & fsp ) ;
2020-03-23 13:36:42 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
fsp - > access_mask = FILE_LIST_DIRECTORY ;
2020-04-02 19:21:11 +03:00
fsp - > fsp_flags . is_directory = true ;
2020-04-06 16:41:41 +03:00
fsp - > fsp_flags . is_dirfsp = true ;
2020-03-23 13:36:42 +03:00
* _fsp = fsp ;
return NT_STATUS_OK ;
}
2020-03-16 20:53:15 +03:00
/*
* Open an internal fsp for an * existing * directory .
*/
2020-05-15 16:14:26 +03:00
NTSTATUS open_internal_dirfsp ( connection_struct * conn ,
const struct smb_filename * smb_dname ,
2022-06-17 11:39:20 +03:00
int _open_flags ,
2020-05-15 16:14:26 +03:00
struct files_struct * * _fsp )
2020-03-16 20:53:15 +03:00
{
2022-06-17 11:39:20 +03:00
struct vfs_open_how how = { . flags = _open_flags , } ;
2020-03-16 20:53:15 +03:00
struct files_struct * fsp = NULL ;
NTSTATUS status ;
2020-05-15 16:14:26 +03:00
status = create_internal_dirfsp ( conn , smb_dname , & fsp ) ;
2020-03-16 20:53:15 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
# ifdef O_DIRECTORY
2022-06-17 11:39:20 +03:00
how . flags | = O_DIRECTORY ;
2020-03-16 20:53:15 +03:00
# endif
2022-06-17 11:39:20 +03:00
status = fd_openat ( conn - > cwd_fsp , fsp - > fsp_name , fsp , & how ) ;
2020-03-16 20:53:15 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_INFO ( " Could not open fd for %s (%s) \n " ,
smb_fname_str_dbg ( smb_dname ) ,
nt_errstr ( status ) ) ;
file_free ( NULL , fsp ) ;
return status ;
}
2022-03-18 22:02:35 +03:00
status = vfs_stat_fsp ( fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-03-18 22:00:15 +03:00
file_free ( NULL , fsp ) ;
2022-03-18 22:02:35 +03:00
return status ;
2020-05-15 16:25:07 +03:00
}
if ( ! S_ISDIR ( fsp - > fsp_name - > st . st_ex_mode ) ) {
DBG_ERR ( " %s is not a directory! \n " ,
smb_fname_str_dbg ( smb_dname ) ) ;
file_free ( NULL , fsp ) ;
return NT_STATUS_NOT_A_DIRECTORY ;
}
fsp - > file_id = vfs_file_id_from_sbuf ( conn , & fsp - > fsp_name - > st ) ;
2020-03-16 20:53:15 +03:00
* _fsp = fsp ;
return NT_STATUS_OK ;
}
2020-09-29 11:14:47 +03:00
/*
* The " link " in the name doesn ' t imply link in the filesystem
* sense . It ' s a object that " links " together an fsp and an smb_fname
* and the link allocated as talloc child of an fsp .
*
2021-11-25 18:52:41 +03:00
* The link is created for fsps that openat_pathref_fsp ( ) returns in
2021-11-05 20:52:40 +03:00
* smb_fname - > fsp . When this fsp is freed by file_free ( ) by some caller
2020-09-29 11:14:47 +03:00
* somewhere , the destructor fsp_smb_fname_link_destructor ( ) on the link object
* will use the link to reset the reference in smb_fname - > fsp that is about to
* go away .
*
* This prevents smb_fname_internal_fsp_destructor ( ) from seeing dangling fsp
* pointers .
*/
struct fsp_smb_fname_link {
struct fsp_smb_fname_link * * smb_fname_link ;
struct files_struct * * smb_fname_fsp ;
} ;
static int fsp_smb_fname_link_destructor ( struct fsp_smb_fname_link * link )
{
if ( link - > smb_fname_link = = NULL ) {
return 0 ;
}
* link - > smb_fname_link = NULL ;
* link - > smb_fname_fsp = NULL ;
return 0 ;
}
static NTSTATUS fsp_smb_fname_link ( struct files_struct * fsp ,
struct fsp_smb_fname_link * * smb_fname_link ,
struct files_struct * * smb_fname_fsp )
{
struct fsp_smb_fname_link * link = NULL ;
2020-12-16 16:03:36 +03:00
SMB_ASSERT ( * smb_fname_link = = NULL ) ;
SMB_ASSERT ( * smb_fname_fsp = = NULL ) ;
2020-09-29 11:14:47 +03:00
link = talloc_zero ( fsp , struct fsp_smb_fname_link ) ;
if ( link = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
link - > smb_fname_link = smb_fname_link ;
link - > smb_fname_fsp = smb_fname_fsp ;
* smb_fname_link = link ;
2020-12-16 16:03:36 +03:00
* smb_fname_fsp = fsp ;
2020-09-29 11:14:47 +03:00
talloc_set_destructor ( link , fsp_smb_fname_link_destructor ) ;
return NT_STATUS_OK ;
}
/*
* Free a link , carefully avoiding to trigger the link destructor
*/
static void destroy_fsp_smb_fname_link ( struct fsp_smb_fname_link * * _link )
{
struct fsp_smb_fname_link * link = * _link ;
if ( link = = NULL ) {
return ;
}
talloc_set_destructor ( link , NULL ) ;
TALLOC_FREE ( link ) ;
* _link = NULL ;
}
/*
* Talloc destructor set on an smb_fname set by openat_pathref_fsp ( ) used to
* close the embedded smb_fname - > fsp .
*/
static int smb_fname_fsp_destructor ( struct smb_filename * smb_fname )
{
struct files_struct * fsp = smb_fname - > fsp ;
2023-11-16 12:50:32 +03:00
struct files_struct * base_fsp = NULL ;
2020-09-29 11:14:47 +03:00
NTSTATUS status ;
2021-03-10 15:22:07 +03:00
int saved_errno = errno ;
2020-09-29 11:14:47 +03:00
destroy_fsp_smb_fname_link ( & smb_fname - > fsp_link ) ;
if ( fsp = = NULL ) {
2021-03-10 15:22:07 +03:00
errno = saved_errno ;
2020-09-29 11:14:47 +03:00
return 0 ;
}
2022-02-11 11:37:35 +03:00
if ( fsp_is_alternate_stream ( fsp ) ) {
2023-11-16 12:50:32 +03:00
base_fsp = fsp - > base_fsp ;
2020-09-29 11:14:47 +03:00
}
status = fd_close ( fsp ) ;
2022-07-08 18:08:05 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-07-07 15:40:28 +03:00
DBG_ERR ( " Closing fd for fsp [%s] failed: %s. "
" Please check your filesystem!!! \n " ,
fsp_str_dbg ( fsp ) , nt_errstr ( status ) ) ;
}
2020-09-29 11:14:47 +03:00
file_free ( NULL , fsp ) ;
smb_fname - > fsp = NULL ;
2023-11-16 12:50:32 +03:00
if ( base_fsp ! = NULL ) {
base_fsp - > stream_fsp = NULL ;
status = fd_close ( base_fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " Closing fd for base_fsp [%s] failed: %s. "
" Please check your filesystem!!! \n " ,
fsp_str_dbg ( base_fsp ) , nt_errstr ( status ) ) ;
}
file_free ( NULL , base_fsp ) ;
}
2021-03-10 15:22:07 +03:00
errno = saved_errno ;
2020-09-29 11:14:47 +03:00
return 0 ;
}
2022-06-14 18:05:09 +03:00
static NTSTATUS openat_pathref_fullname (
2022-06-14 18:17:54 +03:00
struct connection_struct * conn ,
2022-06-13 18:50:32 +03:00
const struct files_struct * dirfsp ,
2022-06-14 18:05:09 +03:00
struct smb_filename * * full_fname ,
2022-06-17 11:54:14 +03:00
struct smb_filename * smb_fname ,
const struct vfs_open_how * how )
2020-09-29 11:14:47 +03:00
{
struct files_struct * fsp = NULL ;
NTSTATUS status ;
DBG_DEBUG ( " smb_fname [%s] \n " , smb_fname_str_dbg ( smb_fname ) ) ;
2022-06-13 18:50:32 +03:00
SMB_ASSERT ( smb_fname - > fsp = = NULL ) ;
2020-12-17 10:59:50 +03:00
2020-09-29 11:14:47 +03:00
status = fsp_new ( conn , conn , & fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
GetTimeOfDay ( & fsp - > open_time ) ;
fsp_set_gen_id ( fsp ) ;
ZERO_STRUCT ( conn - > sconn - > fsp_fi_cache ) ;
fsp - > fsp_flags . is_pathref = true ;
2022-06-14 18:05:09 +03:00
status = fsp_attach_smb_fname ( fsp , full_fname ) ;
2020-09-29 11:14:47 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
2022-06-17 11:54:14 +03:00
status = fd_openat ( dirfsp , smb_fname , fsp , how ) ;
2020-09-29 11:14:47 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-03-15 00:52:11 +03:00
smb_fname - > st = fsp - > fsp_name - > st ;
2020-09-29 11:14:47 +03:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NOT_FOUND ) | |
2021-02-02 13:18:54 +03:00
NT_STATUS_EQUAL ( status , NT_STATUS_OBJECT_PATH_NOT_FOUND ) | |
NT_STATUS_EQUAL ( status , NT_STATUS_STOPPED_ON_SYMLINK ) )
2020-09-29 11:14:47 +03:00
{
/*
* streams_xattr return NT_STATUS_NOT_FOUND for
2022-04-01 10:07:47 +03:00
* opens of not yet existing streams .
2020-09-29 11:14:47 +03:00
*
* ELOOP maps to NT_STATUS_OBJECT_PATH_NOT_FOUND
* and this will result from a open request from
* a POSIX client on a symlink .
*
* NT_STATUS_OBJECT_NAME_NOT_FOUND is the simple
* ENOENT case .
2021-02-02 13:18:54 +03:00
*
* NT_STATUS_STOPPED_ON_SYMLINK is returned when trying
* to open a symlink , our callers are not interested in
* this .
2020-09-29 11:14:47 +03:00
*/
status = NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
2021-02-02 15:49:56 +03:00
goto fail ;
2020-09-29 11:14:47 +03:00
}
2021-07-07 13:40:05 +03:00
/*
* fd_openat ( ) has done an FSTAT on the handle
* so update the smb_fname stat info with " truth " .
* from the handle .
*/
smb_fname - > st = fsp - > fsp_name - > st ;
2022-03-08 14:57:13 +03:00
fsp - > fsp_flags . is_directory = S_ISDIR ( fsp - > fsp_name - > st . st_ex_mode ) ;
2020-09-29 11:14:47 +03:00
fsp - > file_id = vfs_file_id_from_sbuf ( conn , & fsp - > fsp_name - > st ) ;
status = fsp_smb_fname_link ( fsp ,
& smb_fname - > fsp_link ,
& smb_fname - > fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
DBG_DEBUG ( " fsp [%s]: OK \n " , fsp_str_dbg ( fsp ) ) ;
talloc_set_destructor ( smb_fname , smb_fname_fsp_destructor ) ;
return NT_STATUS_OK ;
fail :
DBG_DEBUG ( " Opening pathref for [%s] failed: %s \n " ,
smb_fname_str_dbg ( smb_fname ) ,
nt_errstr ( status ) ) ;
2022-06-13 18:50:32 +03:00
fd_close ( fsp ) ;
file_free ( NULL , fsp ) ;
return status ;
}
2020-12-21 18:35:11 +03:00
2022-06-13 18:50:32 +03:00
/*
* Open an internal O_PATH based fsp for smb_fname . If O_PATH is not
* available , open O_RDONLY as root . Both is done in fd_open ( ) - >
* non_widelink_open ( ) , triggered by setting fsp - > fsp_flags . is_pathref to
* true .
*/
NTSTATUS openat_pathref_fsp ( const struct files_struct * dirfsp ,
struct smb_filename * smb_fname )
{
connection_struct * conn = dirfsp - > conn ;
2022-06-14 18:05:09 +03:00
struct smb_filename * full_fname = NULL ;
2022-06-13 18:50:32 +03:00
struct smb_filename * base_fname = NULL ;
2022-06-17 11:54:14 +03:00
struct vfs_open_how how = { . flags = O_RDONLY | O_NONBLOCK , } ;
2022-06-13 18:50:32 +03:00
NTSTATUS status ;
2020-12-21 18:35:11 +03:00
2022-06-13 18:50:32 +03:00
DBG_DEBUG ( " smb_fname [%s] \n " , smb_fname_str_dbg ( smb_fname ) ) ;
if ( smb_fname - > fsp ! = NULL ) {
/* We already have one for this name. */
DBG_DEBUG ( " smb_fname [%s] already has a pathref fsp. \n " ,
smb_fname_str_dbg ( smb_fname ) ) ;
return NT_STATUS_OK ;
2020-09-29 11:14:47 +03:00
}
2022-06-13 18:50:32 +03:00
2022-09-02 12:46:53 +03:00
if ( is_named_stream ( smb_fname ) & &
( ( conn - > fs_capabilities & FILE_NAMED_STREAMS ) = = 0 ) ) {
DBG_DEBUG ( " stream open [%s] on non-stream share \n " ,
smb_fname_str_dbg ( smb_fname ) ) ;
return NT_STATUS_OBJECT_NAME_INVALID ;
}
if ( ! is_named_stream ( smb_fname ) ) {
2022-06-14 18:05:09 +03:00
/*
* openat_pathref_fullname ( ) will make " full_fname " a
* talloc child of the smb_fname - > fsp . Don ' t use
* talloc_tos ( ) to allocate it to avoid making the
* talloc stackframe pool long - lived .
*/
full_fname = full_path_from_dirfsp_atname (
conn ,
dirfsp ,
smb_fname ) ;
if ( full_fname = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
status = openat_pathref_fullname (
2024-10-08 18:42:11 +03:00
conn , dirfsp , & full_fname , smb_fname , & how ) ;
2022-06-14 18:05:09 +03:00
TALLOC_FREE ( full_fname ) ;
2022-06-13 18:50:32 +03:00
return status ;
}
/*
* stream open
*/
base_fname = cp_smb_filename_nostream ( conn , smb_fname ) ;
if ( base_fname = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2022-06-14 18:05:09 +03:00
full_fname = full_path_from_dirfsp_atname (
conn , /* no talloc_tos(), see comment above */
dirfsp ,
base_fname ) ;
if ( full_fname = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
status = openat_pathref_fullname (
2024-10-08 18:42:11 +03:00
conn , dirfsp , & full_fname , base_fname , & how ) ;
2022-06-14 18:05:09 +03:00
TALLOC_FREE ( full_fname ) ;
2022-06-13 18:50:32 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-12-05 18:33:37 +03:00
DBG_DEBUG ( " openat_pathref_fullname() failed: %s \n " ,
2022-06-13 18:50:32 +03:00
nt_errstr ( status ) ) ;
goto fail ;
}
2022-06-13 18:31:16 +03:00
status = open_stream_pathref_fsp ( & base_fname - > fsp , smb_fname ) ;
2022-06-13 18:50:32 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " open_stream_pathref_fsp failed: %s \n " ,
nt_errstr ( status ) ) ;
goto fail ;
}
smb_fname_fsp_unlink ( base_fname ) ;
fail :
TALLOC_FREE ( base_fname ) ;
2020-09-29 11:14:47 +03:00
return status ;
}
2024-10-11 15:56:21 +03:00
NTSTATUS open_rootdir_pathref_fsp ( connection_struct * conn ,
struct files_struct * * _fsp )
{
struct smb_filename slash = { . base_name = discard_const_p ( char , " / " ) } ;
struct vfs_open_how how = { . flags = O_RDONLY | O_DIRECTORY , } ;
struct files_struct * fsp = NULL ;
NTSTATUS status ;
int fd ;
status = fsp_new ( conn , conn , & fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
GetTimeOfDay ( & fsp - > open_time ) ;
fsp_set_gen_id ( fsp ) ;
ZERO_STRUCT ( conn - > sconn - > fsp_fi_cache ) ;
fsp - > fsp_flags . is_pathref = true ;
status = fsp_set_smb_fname ( fsp , & slash ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
fd = SMB_VFS_OPENAT ( conn ,
conn - > cwd_fsp ,
fsp - > fsp_name ,
fsp ,
& how ) ;
if ( fd = = - 1 ) {
status = map_nt_error_from_unix ( errno ) ;
goto fail ;
}
fsp_set_fd ( fsp , fd ) ;
status = vfs_stat_fsp ( fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " vfs_stat_fsp( \" / \" ) failed: %s \n " , nt_errstr ( status ) ) ;
goto close_fail ;
}
fsp - > fsp_flags . is_directory = S_ISDIR ( fsp - > fsp_name - > st . st_ex_mode ) ;
if ( ! fsp - > fsp_flags . is_directory ) {
DBG_DEBUG ( " \" / \" not a directory \n " ) ;
status = NT_STATUS_UNEXPECTED_IO_ERROR ;
goto close_fail ;
}
fsp - > file_id = vfs_file_id_from_sbuf ( conn , & fsp - > fsp_name - > st ) ;
* _fsp = fsp ;
return NT_STATUS_OK ;
close_fail :
fd_close ( fsp ) ;
fail :
file_free ( NULL , fsp ) ;
return status ;
}
2021-12-08 09:08:10 +03:00
/*
* Open a stream given an already opened base_fsp . Avoid
* non_widelink_open : This is only valid for the case where we have a
* valid non - cwd_fsp dirfsp that we can pass to SMB_VFS_OPENAT ( )
*/
NTSTATUS open_stream_pathref_fsp (
struct files_struct * * _base_fsp ,
struct smb_filename * smb_fname )
{
2022-06-13 18:31:16 +03:00
struct files_struct * base_fsp = * _base_fsp ;
2024-10-08 18:40:41 +03:00
struct files_struct * fsp = NULL ;
2022-06-13 18:31:16 +03:00
connection_struct * conn = base_fsp - > conn ;
struct smb_filename * base_fname = base_fsp - > fsp_name ;
2021-12-08 09:08:10 +03:00
struct smb_filename * full_fname = NULL ;
2022-06-17 11:54:14 +03:00
struct vfs_open_how how = { . flags = O_RDONLY | O_NONBLOCK , } ;
2021-12-08 09:08:10 +03:00
NTSTATUS status ;
2024-10-08 18:40:41 +03:00
int fd ;
2021-12-08 09:08:10 +03:00
SMB_ASSERT ( smb_fname - > fsp = = NULL ) ;
SMB_ASSERT ( is_named_stream ( smb_fname ) ) ;
2022-06-13 18:31:16 +03:00
full_fname = synthetic_smb_fname (
2022-06-14 18:17:54 +03:00
conn , /* no talloc_tos(), this will be long-lived */
2022-06-13 18:31:16 +03:00
base_fname - > base_name ,
smb_fname - > stream_name ,
& smb_fname - > st ,
smb_fname - > twrp ,
smb_fname - > flags ) ;
2021-12-08 09:08:10 +03:00
if ( full_fname = = NULL ) {
2022-06-14 18:17:54 +03:00
return NT_STATUS_NO_MEMORY ;
2021-12-08 09:08:10 +03:00
}
2024-10-08 18:40:41 +03:00
status = fsp_new ( conn , conn , & fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
GetTimeOfDay ( & fsp - > open_time ) ;
fsp_set_gen_id ( fsp ) ;
ZERO_STRUCT ( conn - > sconn - > fsp_fi_cache ) ;
fsp - > fsp_flags . is_pathref = true ;
status = fsp_attach_smb_fname ( fsp , & full_fname ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
fsp_set_base_fsp ( fsp , base_fsp ) ;
fd = SMB_VFS_OPENAT ( conn ,
NULL , /* stream open is relative to fsp->base_fsp */
smb_fname ,
fsp ,
& how ) ;
if ( fd = = - 1 ) {
status = map_nt_error_from_unix ( errno ) ;
goto fail ;
}
fsp_set_fd ( fsp , fd ) ;
status = vfs_stat_fsp ( fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " vfs_stat_fsp failed: %s \n " , nt_errstr ( status ) ) ;
fd_close ( fsp ) ;
goto fail ;
}
smb_fname - > st = fsp - > fsp_name - > st ;
fsp - > fsp_flags . is_directory = S_ISDIR ( fsp - > fsp_name - > st . st_ex_mode ) ;
fsp - > file_id = vfs_file_id_from_sbuf ( conn , & fsp - > fsp_name - > st ) ;
status = fsp_smb_fname_link ( fsp , & smb_fname - > fsp_link , & smb_fname - > fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
DBG_DEBUG ( " fsp [%s]: OK \n " , fsp_str_dbg ( fsp ) ) ;
talloc_set_destructor ( smb_fname , smb_fname_fsp_destructor ) ;
return NT_STATUS_OK ;
fail :
2022-06-14 18:17:54 +03:00
TALLOC_FREE ( full_fname ) ;
2024-10-08 18:40:41 +03:00
if ( fsp ! = NULL ) {
fsp_set_base_fsp ( fsp , NULL ) ;
fd_close ( fsp ) ;
file_free ( NULL , fsp ) ;
}
2021-12-08 09:08:10 +03:00
return status ;
}
2022-09-07 11:43:23 +03:00
NTSTATUS readlink_talloc (
2022-07-14 20:44:04 +03:00
TALLOC_CTX * mem_ctx ,
struct files_struct * dirfsp ,
struct smb_filename * smb_relname ,
char * * _substitute )
{
2022-12-04 14:14:12 +03:00
struct smb_filename null_fname = {
. base_name = discard_const_p ( char , " " ) ,
} ;
2022-09-07 11:39:26 +03:00
char buf [ PATH_MAX ] ;
2022-07-14 20:44:04 +03:00
ssize_t ret ;
char * substitute ;
NTSTATUS status ;
if ( smb_relname = = NULL ) {
/*
* We have a Linux O_PATH handle in dirfsp and want to
* read its value , essentially a freadlink
*/
2022-12-04 14:14:12 +03:00
smb_relname = & null_fname ;
2022-07-14 20:44:04 +03:00
}
ret = SMB_VFS_READLINKAT (
dirfsp - > conn , dirfsp , smb_relname , buf , sizeof ( buf ) ) ;
if ( ret < 0 ) {
status = map_nt_error_from_unix ( errno ) ;
DBG_DEBUG ( " SMB_VFS_READLINKAT() failed: %s \n " ,
strerror ( errno ) ) ;
return status ;
}
if ( ( size_t ) ret = = sizeof ( buf ) ) {
/*
2022-09-07 11:39:26 +03:00
* Do we need symlink targets longer than PATH_MAX ?
2022-07-14 20:44:04 +03:00
*/
DBG_DEBUG ( " Got full %zu bytes from readlink, too long \n " ,
sizeof ( buf ) ) ;
return NT_STATUS_BUFFER_OVERFLOW ;
}
substitute = talloc_strndup ( mem_ctx , buf , ret ) ;
if ( substitute = = NULL ) {
DBG_DEBUG ( " talloc_strndup() failed \n " ) ;
return NT_STATUS_NO_MEMORY ;
}
* _substitute = substitute ;
return NT_STATUS_OK ;
}
2024-02-04 18:07:22 +03:00
NTSTATUS read_symlink_reparse ( TALLOC_CTX * mem_ctx ,
struct files_struct * dirfsp ,
struct smb_filename * smb_relname ,
struct reparse_data_buffer * * _reparse )
2022-12-05 14:15:21 +03:00
{
2024-02-04 18:07:22 +03:00
struct reparse_data_buffer * reparse = NULL ;
struct symlink_reparse_struct * lnk = NULL ;
2022-12-05 14:15:21 +03:00
NTSTATUS status ;
2024-06-27 17:46:16 +03:00
reparse = talloc ( mem_ctx , struct reparse_data_buffer ) ;
2024-02-04 18:07:22 +03:00
if ( reparse = = NULL ) {
2022-12-05 14:15:21 +03:00
goto nomem ;
}
2024-02-04 18:07:22 +03:00
* reparse = ( struct reparse_data_buffer ) {
. tag = IO_REPARSE_TAG_SYMLINK ,
} ;
lnk = & reparse - > parsed . lnk ;
2022-12-05 14:15:21 +03:00
2024-02-04 18:07:22 +03:00
status = readlink_talloc ( reparse ,
dirfsp ,
smb_relname ,
& lnk - > substitute_name ) ;
2022-12-05 14:15:21 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " readlink_talloc failed: %s \n " , nt_errstr ( status ) ) ;
goto fail ;
}
2024-02-04 18:07:22 +03:00
if ( lnk - > substitute_name [ 0 ] = = ' / ' ) {
2024-02-04 13:45:22 +03:00
size_t len = fsp_fullbasepath ( dirfsp , NULL , 0 ) ;
char subdir_path [ len + 1 ] ;
2022-12-05 14:15:21 +03:00
char * abs_target_canon = NULL ;
const char * relative = NULL ;
bool in_share ;
2024-02-04 13:45:22 +03:00
fsp_fullbasepath ( dirfsp , subdir_path , sizeof ( subdir_path ) ) ;
2023-10-26 17:12:29 +03:00
2024-02-04 18:07:22 +03:00
abs_target_canon = canonicalize_absolute_path (
talloc_tos ( ) , lnk - > substitute_name ) ;
2022-12-05 14:15:21 +03:00
if ( abs_target_canon = = NULL ) {
goto nomem ;
}
2023-10-26 17:12:29 +03:00
in_share = subdir_of ( subdir_path ,
2024-02-04 13:45:22 +03:00
len ,
2022-12-05 14:15:21 +03:00
abs_target_canon ,
& relative ) ;
if ( in_share ) {
2024-02-04 18:07:22 +03:00
TALLOC_FREE ( lnk - > substitute_name ) ;
lnk - > substitute_name = talloc_strdup ( reparse ,
relative ) ;
if ( lnk - > substitute_name = = NULL ) {
2022-12-05 14:15:21 +03:00
goto nomem ;
}
}
}
2024-02-04 18:07:22 +03:00
if ( ! IS_DIRECTORY_SEP ( lnk - > substitute_name [ 0 ] ) ) {
lnk - > flags | = SYMLINK_FLAG_RELATIVE ;
2022-12-05 14:15:21 +03:00
}
2024-02-04 18:07:22 +03:00
* _reparse = reparse ;
2022-12-05 14:15:21 +03:00
return NT_STATUS_OK ;
nomem :
status = NT_STATUS_NO_MEMORY ;
fail :
2024-02-04 18:07:22 +03:00
TALLOC_FREE ( reparse ) ;
2022-12-05 14:15:21 +03:00
return status ;
}
2023-05-16 11:49:08 +03:00
static bool full_path_extend ( char * * dir , const char * atname )
{
talloc_asprintf_addbuf ( dir ,
" %s%s " ,
( * dir ) [ 0 ] = = ' \0 ' ? " " : " / " ,
atname ) ;
return ( * dir ) ! = NULL ;
}
2023-11-04 18:17:36 +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 ;
}
2023-10-18 12:50:20 +03:00
static int smb_vfs_openat_ci ( TALLOC_CTX * mem_ctx ,
bool case_sensitive ,
struct connection_struct * conn ,
struct files_struct * dirfsp ,
struct smb_filename * smb_fname_rel ,
files_struct * fsp ,
const struct vfs_open_how * how )
{
char * orig_base_name = smb_fname_rel - > base_name ;
DATA_BLOB cache_key = {
. data = NULL ,
} ;
DATA_BLOB cache_value = {
. data = NULL ,
} ;
NTSTATUS status ;
int fd ;
bool ok ;
fd = SMB_VFS_OPENAT ( conn , dirfsp , smb_fname_rel , fsp , how ) ;
if ( ( fd > = 0 ) | | case_sensitive ) {
return fd ;
}
if ( errno ! = ENOENT ) {
return - 1 ;
}
if ( ! lp_stat_cache ( ) ) {
goto lookup ;
}
ok = get_real_filename_cache_key ( mem_ctx ,
dirfsp ,
orig_base_name ,
& cache_key ) ;
if ( ! ok ) {
/*
* probably ENOMEM , just bail
*/
errno = ENOMEM ;
return - 1 ;
}
DO_PROFILE_INC ( statcache_lookups ) ;
ok = memcache_lookup ( NULL ,
GETREALFILENAME_CACHE ,
cache_key ,
& cache_value ) ;
if ( ! ok ) {
DO_PROFILE_INC ( statcache_misses ) ;
goto lookup ;
}
DO_PROFILE_INC ( statcache_hits ) ;
smb_fname_rel - > base_name = talloc_strndup ( mem_ctx ,
( char * ) cache_value . data ,
cache_value . length ) ;
if ( smb_fname_rel - > base_name = = NULL ) {
TALLOC_FREE ( cache_key . data ) ;
smb_fname_rel - > base_name = orig_base_name ;
errno = ENOMEM ;
return - 1 ;
}
if ( IS_VETO_PATH ( dirfsp - > conn , smb_fname_rel - > base_name ) ) {
DBG_DEBUG ( " veto files rejecting last component %s \n " ,
smb_fname_str_dbg ( smb_fname_rel ) ) ;
TALLOC_FREE ( cache_key . data ) ;
smb_fname_rel - > base_name = orig_base_name ;
errno = EPERM ;
return - 1 ;
}
fd = SMB_VFS_OPENAT ( conn , dirfsp , smb_fname_rel , fsp , how ) ;
if ( fd > = 0 ) {
TALLOC_FREE ( cache_key . data ) ;
return fd ;
}
memcache_delete ( NULL , GETREALFILENAME_CACHE , cache_key ) ;
/*
* For the " new filename " case we need to preserve the
* capitalization the client sent us , see
* https : //bugzilla.samba.org/show_bug.cgi?id=15481
*/
TALLOC_FREE ( smb_fname_rel - > base_name ) ;
smb_fname_rel - > base_name = orig_base_name ;
lookup :
status = get_real_filename_at ( dirfsp ,
orig_base_name ,
mem_ctx ,
& smb_fname_rel - > base_name ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " get_real_filename_at() failed: %s \n " ,
nt_errstr ( status ) ) ;
errno = ENOENT ;
return - 1 ;
}
if ( IS_VETO_PATH ( conn , smb_fname_rel - > base_name ) ) {
DBG_DEBUG ( " found veto files path component "
" %s => %s \n " ,
orig_base_name ,
smb_fname_rel - > base_name ) ;
TALLOC_FREE ( smb_fname_rel - > base_name ) ;
smb_fname_rel - > base_name = orig_base_name ;
errno = ENOENT ;
return - 1 ;
}
fd = SMB_VFS_OPENAT ( conn , dirfsp , smb_fname_rel , fsp , how ) ;
if ( ( fd > = 0 ) & & ( 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 fd ;
}
2024-02-04 20:07:19 +03:00
NTSTATUS openat_pathref_fsp_nosymlink (
TALLOC_CTX * mem_ctx ,
struct connection_struct * conn ,
struct files_struct * in_dirfsp ,
const char * path_in ,
NTTIME twrp ,
bool posix ,
struct smb_filename * * _smb_fname ,
struct reparse_data_buffer * * _symlink_err )
2022-07-14 20:44:04 +03:00
{
2023-05-16 12:13:35 +03:00
struct files_struct * dirfsp = in_dirfsp ;
2022-07-14 20:44:04 +03:00
struct smb_filename full_fname = {
. base_name = NULL ,
. twrp = twrp ,
2022-12-14 19:35:17 +03:00
. flags = posix ? SMB_FILENAME_POSIX_PATH : 0 ,
2022-07-14 20:44:04 +03:00
} ;
struct smb_filename rel_fname = {
. base_name = NULL ,
. twrp = twrp ,
2022-12-14 19:35:17 +03:00
. flags = full_fname . flags ,
2022-07-14 20:44:04 +03:00
} ;
struct smb_filename * result = NULL ;
2024-02-04 20:07:19 +03:00
struct reparse_data_buffer * symlink_err = NULL ;
2022-07-14 20:44:04 +03:00
struct files_struct * fsp = NULL ;
char * path = NULL , * next = NULL ;
2023-10-18 12:50:20 +03:00
bool ok , is_toplevel ;
2022-07-14 20:44:04 +03:00
int fd ;
NTSTATUS status ;
2022-06-03 16:53:29 +03:00
struct vfs_open_how how = {
2022-12-12 16:04:00 +03:00
. flags = O_NOFOLLOW | O_NONBLOCK ,
2022-06-03 16:53:29 +03:00
. mode = 0 ,
} ;
2022-07-14 20:44:04 +03:00
DBG_DEBUG ( " path_in=%s \n " , path_in ) ;
status = fsp_new ( conn , conn , & fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " fsp_new() failed: %s \n " , nt_errstr ( status ) ) ;
goto fail ;
}
2023-05-16 12:24:35 +03:00
GetTimeOfDay ( & fsp - > open_time ) ;
fsp_set_gen_id ( fsp ) ;
ZERO_STRUCT ( conn - > sconn - > fsp_fi_cache ) ;
2022-07-14 20:44:04 +03:00
fsp - > fsp_name = & full_fname ;
# ifdef O_PATH
/*
* Add O_PATH manually , doing this by setting
2022-08-04 17:44:44 +03:00
* fsp - > fsp_flags . is_pathref will make us become_root ( ) in the
* non - O_PATH case , which would cause a security problem .
2022-07-14 20:44:04 +03:00
*/
2022-06-03 16:53:29 +03:00
how . flags | = O_PATH ;
2022-07-14 20:44:04 +03:00
# else
# ifdef O_SEARCH
/*
* O_SEARCH just checks for the " x " bit . We are traversing
* directories , so we don ' t need the implicit O_RDONLY ( " r "
* permissions ) but only the " x " - permissions requested by
* O_SEARCH . We need either O_PATH or O_SEARCH to correctly
* function , without either we will incorrectly require also
* the " r " bit when traversing the directory hierarchy .
*/
2022-06-03 16:53:29 +03:00
how . flags | = O_SEARCH ;
2022-07-14 20:44:04 +03:00
# endif
# endif
2022-12-12 16:04:00 +03:00
is_toplevel = ( dirfsp = = dirfsp - > conn - > cwd_fsp ) ;
is_toplevel | = ISDOT ( dirfsp - > fsp_name - > base_name ) ;
full_fname . base_name =
talloc_strdup ( talloc_tos ( ) ,
is_toplevel ? " " : dirfsp - > fsp_name - > base_name ) ;
2022-07-14 20:44:04 +03:00
if ( full_fname . base_name = = NULL ) {
DBG_DEBUG ( " talloc_strdup() failed \n " ) ;
goto nomem ;
}
2022-08-10 23:01:10 +03:00
/*
* First split the path into individual components .
*/
2022-07-14 20:44:04 +03:00
path = path_to_strv ( talloc_tos ( ) , path_in ) ;
if ( path = = NULL ) {
DBG_DEBUG ( " path_to_strv() failed \n " ) ;
goto nomem ;
}
2022-08-10 23:01:10 +03:00
/*
* First we loop over all components
* in order to verify , there ' s no ' . ' or ' . . '
*/
2022-07-14 20:44:04 +03:00
rel_fname . base_name = path ;
2022-08-10 23:01:10 +03:00
while ( rel_fname . base_name ! = NULL ) {
2022-07-14 20:44:04 +03:00
2022-08-10 23:01:10 +03:00
next = strv_next ( path , rel_fname . base_name ) ;
2022-07-14 20:44:04 +03:00
2022-10-17 19:06:02 +03:00
/*
* Path sanitizing further up has cleaned or rejected
* empty path components . Assert this here .
*/
SMB_ASSERT ( rel_fname . base_name [ 0 ] ! = ' \0 ' ) ;
2022-09-05 16:31:19 +03:00
if ( ISDOT ( rel_fname . base_name ) | |
ISDOTDOT ( rel_fname . base_name ) ) {
2022-08-10 23:01:10 +03:00
DBG_DEBUG ( " %s contains a dot \n " , path_in ) ;
status = NT_STATUS_OBJECT_NAME_INVALID ;
goto fail ;
}
2022-08-11 19:55:56 +03:00
/* Check veto files. */
if ( IS_VETO_PATH ( conn , rel_fname . base_name ) ) {
DBG_DEBUG ( " %s contains veto files path component %s \n " ,
path_in , rel_fname . base_name ) ;
status = NT_STATUS_OBJECT_PATH_NOT_FOUND ;
goto fail ;
}
2022-08-10 23:01:10 +03:00
rel_fname . base_name = next ;
2022-07-14 20:44:04 +03:00
}
2022-07-14 20:44:04 +03:00
if ( conn - > open_how_resolve & VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS ) {
/*
* Try a direct openat2 with RESOLVE_NO_SYMLINKS to
* avoid the openat / close loop further down .
*/
rel_fname . base_name = discard_const_p ( char , path_in ) ;
how . resolve = VFS_OPEN_HOW_RESOLVE_NO_SYMLINKS ;
fd = SMB_VFS_OPENAT ( conn , dirfsp , & rel_fname , fsp , & how ) ;
if ( fd > = 0 ) {
fsp_set_fd ( fsp , fd ) ;
2022-12-12 16:04:00 +03:00
ok = full_path_extend ( & full_fname . base_name ,
rel_fname . base_name ) ;
if ( ! ok ) {
goto nomem ;
}
2022-07-14 20:44:04 +03:00
goto done ;
}
status = map_nt_error_from_unix ( errno ) ;
2022-09-05 16:31:19 +03:00
DBG_DEBUG ( " SMB_VFS_OPENAT(%s, %s, RESOLVE_NO_SYMLINKS) "
" returned %d %s => %s \n " ,
2022-07-14 20:44:04 +03:00
smb_fname_str_dbg ( dirfsp - > fsp_name ) , path_in ,
errno , strerror ( errno ) , nt_errstr ( status ) ) ;
SMB_ASSERT ( fd = = - 1 ) ;
switch ( errno ) {
case ENOSYS :
/*
* We got ENOSYS , so fallback to the old code
* if the kernel doesn ' t support openat2 ( ) yet .
*/
break ;
case ELOOP :
case ENOTDIR :
/*
* For ELOOP we also fallback in order to
* return the correct information with
* NT_STATUS_STOPPED_ON_SYMLINK .
*
* O_NOFOLLOW | O_DIRECTORY results in
* ENOTDIR instead of ELOOP for the final
* component .
*/
break ;
case ENOENT :
/*
* If we got ENOENT , the filesystem could
* be case sensitive . For now we only do
* the get_real_filename_at ( ) dance in
* the fallback loop below .
*/
break ;
default :
goto fail ;
}
/*
* Just fallback to the openat loop
*/
how . resolve = 0 ;
}
2022-08-10 23:01:10 +03:00
/*
* Now we loop over all components
* opening each one and using it
* as dirfd for the next one .
*
* It means we can detect symlinks
* within the path .
*/
rel_fname . base_name = path ;
next :
next = strv_next ( path , rel_fname . base_name ) ;
2023-10-18 12:50:20 +03:00
fd = smb_vfs_openat_ci ( talloc_tos ( ) ,
posix | | conn - > case_sensitive ,
conn ,
dirfsp ,
& rel_fname ,
fsp ,
& how ) ;
2022-07-14 20:44:04 +03:00
2022-12-12 16:04:00 +03:00
# ifndef O_PATH
if ( ( fd = = - 1 ) & & ( errno = = ELOOP ) ) {
int ret ;
2022-08-12 20:12:44 +03:00
2022-12-12 16:04:00 +03:00
/*
* openat ( ) hit a symlink . With O_PATH we open the
* symlink and get ENOTDIR in the next round , see
* below .
*/
2022-07-14 20:44:04 +03:00
2024-02-04 20:07:19 +03:00
status = read_symlink_reparse ( mem_ctx ,
dirfsp ,
& rel_fname ,
& symlink_err ) ;
2022-12-12 16:04:00 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2024-02-04 20:07:19 +03:00
DBG_DEBUG ( " read_symlink_reparse failed: %s \n " ,
2022-07-14 20:44:04 +03:00
nt_errstr ( status ) ) ;
2022-12-12 16:04:00 +03:00
goto fail ;
}
if ( next ! = NULL ) {
size_t parsed = next - path ;
size_t len = talloc_get_size ( path ) ;
2024-02-04 19:42:28 +03:00
size_t unparsed = len - parsed ;
if ( unparsed > UINT16_MAX ) {
status = NT_STATUS_BUFFER_OVERFLOW ;
goto fail ;
}
symlink_err - > parsed . lnk
. unparsed_path_length = unparsed ;
2022-12-12 16:04:00 +03:00
}
/*
* We know rel_fname is a symlink , now fill in the
* rest of the metadata for our callers .
*/
ret = SMB_VFS_FSTATAT ( conn ,
dirfsp ,
& rel_fname ,
2024-02-04 20:50:47 +03:00
& full_fname . st ,
2022-12-12 16:04:00 +03:00
AT_SYMLINK_NOFOLLOW ) ;
if ( ret = = - 1 ) {
status = map_nt_error_from_unix ( errno ) ;
DBG_DEBUG ( " SMB_VFS_FSTATAT(%s/%s) failed: %s \n " ,
fsp_str_dbg ( dirfsp ) ,
rel_fname . base_name ,
strerror ( errno ) ) ;
TALLOC_FREE ( symlink_err ) ;
goto fail ;
}
2024-02-04 20:50:47 +03:00
if ( ! S_ISLNK ( full_fname . st . st_ex_mode ) ) {
2022-07-14 20:44:04 +03:00
/*
2022-12-12 16:04:00 +03:00
* Hit a race : readlink_talloc ( ) worked before
* the fstatat ( ) , but rel_fname changed to
* something that ' s not a symlink .
2022-07-14 20:44:04 +03:00
*/
2022-12-12 16:04:00 +03:00
status = NT_STATUS_OBJECT_NAME_NOT_FOUND ;
TALLOC_FREE ( symlink_err ) ;
goto fail ;
2022-07-14 20:44:04 +03:00
}
2022-12-12 16:04:00 +03:00
status = NT_STATUS_STOPPED_ON_SYMLINK ;
goto fail ;
}
# endif
if ( ( fd = = - 1 ) & & ( errno = = ENOTDIR ) ) {
2024-02-04 19:42:28 +03:00
size_t parsed , len , unparsed ;
2022-12-12 16:04:00 +03:00
/*
* dirfsp does not point at a directory , try a
* freadlink .
*/
2024-02-04 20:07:19 +03:00
status = read_symlink_reparse ( mem_ctx ,
dirfsp ,
NULL ,
& symlink_err ) ;
2022-12-12 16:04:00 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2024-02-04 20:07:19 +03:00
DBG_DEBUG ( " read_symlink_reparse failed: %s \n " ,
2022-12-12 16:04:00 +03:00
nt_errstr ( status ) ) ;
status = NT_STATUS_NOT_A_DIRECTORY ;
goto fail ;
}
parsed = rel_fname . base_name - path ;
len = talloc_get_size ( path ) ;
2024-02-04 19:42:28 +03:00
unparsed = len - parsed ;
if ( unparsed > UINT16_MAX ) {
status = NT_STATUS_BUFFER_OVERFLOW ;
goto fail ;
}
2024-02-04 20:07:19 +03:00
symlink_err - > parsed . lnk . unparsed_path_length = unparsed ;
2022-12-12 16:04:00 +03:00
status = NT_STATUS_STOPPED_ON_SYMLINK ;
2022-07-14 20:44:04 +03:00
goto fail ;
}
if ( fd = = - 1 ) {
2024-06-07 21:35:47 +03:00
/*
* vfs_widelink widelink_openat will update stat for fsp
* and return ELOOP for non - existing link , we can report
* the link here and let calling code decide what to do .
*/
if ( ( errno = = ELOOP ) & & S_ISLNK ( fsp - > fsp_name - > st . st_ex_mode ) ) {
status = read_symlink_reparse ( mem_ctx ,
dirfsp ,
& rel_fname ,
& symlink_err ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
status = NT_STATUS_STOPPED_ON_SYMLINK ;
} else {
DBG_ERR ( " read_symlink_reparse failed: %s \n " ,
nt_errstr ( status ) ) ;
}
goto fail ;
}
2022-07-14 20:44:04 +03:00
status = map_nt_error_from_unix ( errno ) ;
DBG_DEBUG ( " SMB_VFS_OPENAT() failed: %s \n " ,
strerror ( errno ) ) ;
goto fail ;
}
fsp_set_fd ( fsp , fd ) ;
2023-05-16 11:49:08 +03:00
ok = full_path_extend ( & full_fname . base_name , rel_fname . base_name ) ;
if ( ! ok ) {
2022-07-14 20:44:04 +03:00
goto nomem ;
}
if ( next ! = NULL ) {
struct files_struct * tmp = NULL ;
2023-05-16 12:13:35 +03:00
if ( dirfsp ! = in_dirfsp ) {
2022-07-14 20:44:04 +03:00
fd_close ( dirfsp ) ;
}
tmp = dirfsp ;
dirfsp = fsp ;
2023-05-16 12:13:35 +03:00
if ( tmp = = in_dirfsp ) {
2022-07-14 20:44:04 +03:00
status = fsp_new ( conn , conn , & fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " fsp_new() failed: %s \n " ,
nt_errstr ( status ) ) ;
goto fail ;
}
fsp - > fsp_name = & full_fname ;
} else {
fsp = tmp ;
}
rel_fname . base_name = next ;
goto next ;
}
2023-05-16 12:13:35 +03:00
if ( dirfsp ! = in_dirfsp ) {
2022-07-14 20:44:04 +03:00
SMB_ASSERT ( fsp_get_pathref_fd ( dirfsp ) ! = - 1 ) ;
fd_close ( dirfsp ) ;
2023-01-12 22:20:08 +03:00
dirfsp - > fsp_name = NULL ;
2022-07-14 20:44:04 +03:00
file_free ( NULL , dirfsp ) ;
dirfsp = NULL ;
}
2022-07-14 20:44:04 +03:00
done :
2022-07-14 20:44:04 +03:00
fsp - > fsp_flags . is_pathref = true ;
fsp - > fsp_name = NULL ;
status = fsp_set_smb_fname ( fsp , & full_fname ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " fsp_set_smb_fname() failed: %s \n " ,
nt_errstr ( status ) ) ;
goto fail ;
}
status = vfs_stat_fsp ( fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " vfs_stat_fsp(%s) failed: %s \n " ,
fsp_str_dbg ( fsp ) ,
nt_errstr ( status ) ) ;
goto fail ;
}
2023-06-30 12:18:42 +03:00
if ( S_ISLNK ( fsp - > fsp_name - > st . st_ex_mode ) ) {
/*
* Last component was a symlink we opened with O_PATH , fail it
* here .
*/
2024-02-04 20:07:19 +03:00
status = read_symlink_reparse ( mem_ctx ,
fsp ,
NULL ,
& symlink_err ) ;
2023-06-30 12:18:42 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
status = NT_STATUS_STOPPED_ON_SYMLINK ;
goto fail ;
}
2022-07-28 00:36:33 +03:00
/*
* We must correctly set fsp - > file_id as code inside
* open . c will use this to check if delete_on_close
* has been set on the dirfsp .
*/
fsp - > file_id = vfs_file_id_from_sbuf ( conn , & fsp - > fsp_name - > st ) ;
2022-07-14 20:44:04 +03:00
result = cp_smb_filename ( mem_ctx , fsp - > fsp_name ) ;
if ( result = = NULL ) {
DBG_DEBUG ( " cp_smb_filename() failed \n " ) ;
goto nomem ;
}
2022-07-27 00:29:21 +03:00
status = fsp_smb_fname_link ( fsp ,
& result - > fsp_link ,
& result - > fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto fail ;
}
2022-07-14 20:44:04 +03:00
talloc_set_destructor ( result , smb_fname_fsp_destructor ) ;
* _smb_fname = result ;
DBG_DEBUG ( " returning %s \n " , smb_fname_str_dbg ( result ) ) ;
return NT_STATUS_OK ;
nomem :
status = NT_STATUS_NO_MEMORY ;
fail :
if ( fsp ! = NULL ) {
if ( fsp_get_pathref_fd ( fsp ) ! = - 1 ) {
fd_close ( fsp ) ;
}
file_free ( NULL , fsp ) ;
fsp = NULL ;
}
2023-05-16 12:13:35 +03:00
if ( ( dirfsp ! = NULL ) & & ( dirfsp ! = in_dirfsp ) ) {
2022-07-14 20:44:04 +03:00
SMB_ASSERT ( fsp_get_pathref_fd ( dirfsp ) ! = - 1 ) ;
fd_close ( dirfsp ) ;
2023-01-12 22:20:08 +03:00
dirfsp - > fsp_name = NULL ;
2022-07-14 20:44:04 +03:00
file_free ( NULL , dirfsp ) ;
dirfsp = NULL ;
}
2022-12-12 16:04:00 +03:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_STOPPED_ON_SYMLINK ) ) {
* _symlink_err = symlink_err ;
}
2022-07-14 20:44:04 +03:00
TALLOC_FREE ( path ) ;
return status ;
}
2023-10-18 12:50:20 +03:00
/*
* 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 .
*/
NTSTATUS openat_pathref_fsp_lcomp ( struct files_struct * dirfsp ,
struct smb_filename * smb_fname_rel ,
uint32_t ucf_flags )
{
struct connection_struct * conn = dirfsp - > conn ;
const char * orig_rel_base_name = smb_fname_rel - > base_name ;
struct files_struct * fsp = NULL ;
struct smb_filename * full_fname = NULL ;
struct vfs_open_how how = {
. flags = O_RDONLY | O_NONBLOCK | O_NOFOLLOW ,
} ;
NTSTATUS status ;
int ret , fd ;
/*
* Make sure we don ' t need of the all the magic in
* openat_pathref_fsp ( ) with regards non_widelink_open etc .
*/
SMB_ASSERT ( ( smb_fname_rel - > fsp = = NULL ) & &
2024-10-11 13:46:53 +03:00
( ( dirfsp ! = dirfsp - > conn - > cwd_fsp ) | |
ISDOT ( smb_fname_rel - > base_name ) ) & &
2023-10-18 12:50:20 +03:00
( strchr_m ( smb_fname_rel - > base_name , ' / ' ) = = NULL ) & &
! is_named_stream ( smb_fname_rel ) ) ;
SET_STAT_INVALID ( smb_fname_rel - > st ) ;
/* Check veto files - only looks at last component. */
if ( IS_VETO_PATH ( dirfsp - > conn , smb_fname_rel - > base_name ) ) {
DBG_DEBUG ( " veto files rejecting last component %s \n " ,
smb_fname_str_dbg ( smb_fname_rel ) ) ;
return NT_STATUS_NETWORK_OPEN_RESTRICTION ;
}
status = fsp_new ( conn , conn , & fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " fsp_new() failed: %s \n " , nt_errstr ( status ) ) ;
return status ;
}
GetTimeOfDay ( & fsp - > open_time ) ;
fsp_set_gen_id ( fsp ) ;
ZERO_STRUCT ( conn - > sconn - > fsp_fi_cache ) ;
fsp - > fsp_flags . is_pathref = true ;
full_fname = full_path_from_dirfsp_atname ( conn , dirfsp , smb_fname_rel ) ;
if ( full_fname = = NULL ) {
DBG_DEBUG ( " full_path_from_dirfsp_atname(%s/%s) failed \n " ,
dirfsp - > fsp_name - > base_name ,
smb_fname_rel - > base_name ) ;
file_free ( NULL , fsp ) ;
return NT_STATUS_NO_MEMORY ;
}
status = fsp_attach_smb_fname ( fsp , & full_fname ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " fsp_attach_smb_fname(fsp, %s) failed: %s \n " ,
smb_fname_str_dbg ( full_fname ) ,
nt_errstr ( status ) ) ;
file_free ( NULL , fsp ) ;
return status ;
}
fd = smb_vfs_openat_ci ( smb_fname_rel ,
( ucf_flags & UCF_POSIX_PATHNAMES ) | |
conn - > case_sensitive ,
conn ,
dirfsp ,
smb_fname_rel ,
fsp ,
& how ) ;
if ( ( fd = = - 1 ) & & ( errno = = ENOENT ) ) {
status = map_nt_error_from_unix ( errno ) ;
DBG_DEBUG ( " smb_vfs_openat(%s/%s) failed: %s \n " ,
dirfsp - > fsp_name - > base_name ,
smb_fname_rel - > base_name ,
strerror ( errno ) ) ;
file_free ( NULL , fsp ) ;
return status ;
}
if ( smb_fname_rel - > base_name ! = orig_rel_base_name ) {
struct smb_filename new_fullname = * smb_fname_rel ;
DBG_DEBUG ( " rel->base_name changed from %s to %s \n " ,
orig_rel_base_name ,
smb_fname_rel - > base_name ) ;
new_fullname . base_name = full_path_from_dirfsp_at_basename (
talloc_tos ( ) , dirfsp , new_fullname . base_name ) ;
if ( new_fullname . base_name = = NULL ) {
fd_close ( fsp ) ;
file_free ( NULL , fsp ) ;
return NT_STATUS_NO_MEMORY ;
}
status = fsp_set_smb_fname ( fsp , & new_fullname ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
fd_close ( fsp ) ;
file_free ( NULL , fsp ) ;
return status ;
}
}
fsp_set_fd ( fsp , fd ) ;
if ( fd > = 0 ) {
ret = SMB_VFS_FSTAT ( fsp , & fsp - > fsp_name - > st ) ;
} else {
ret = SMB_VFS_FSTATAT ( fsp - > conn ,
dirfsp ,
smb_fname_rel ,
& fsp - > fsp_name - > st ,
AT_SYMLINK_NOFOLLOW ) ;
}
if ( ret = = - 1 ) {
status = map_nt_error_from_unix ( errno ) ;
DBG_DEBUG ( " SMB_VFS_%sSTAT(%s/%s) failed: %s \n " ,
( fd > = 0 ) ? " F " : " " ,
dirfsp - > fsp_name - > base_name ,
smb_fname_rel - > base_name ,
2023-11-14 12:53:30 +03:00
strerror ( errno ) ) ;
2023-10-18 12:50:20 +03:00
fd_close ( fsp ) ;
file_free ( NULL , fsp ) ;
return status ;
}
fsp - > fsp_flags . is_directory = S_ISDIR ( fsp - > fsp_name - > st . st_ex_mode ) ;
2024-01-31 21:48:48 +03:00
fsp - > fsp_flags . posix_open =
( ( smb_fname_rel - > flags & SMB_FILENAME_POSIX_PATH ) ! = 0 ) ;
2023-10-18 12:50:20 +03:00
fsp - > file_id = vfs_file_id_from_sbuf ( conn , & fsp - > fsp_name - > st ) ;
smb_fname_rel - > st = fsp - > fsp_name - > st ;
status = fsp_smb_fname_link ( fsp ,
& smb_fname_rel - > fsp_link ,
& smb_fname_rel - > fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " fsp_smb_fname_link() failed: %s \n " ,
nt_errstr ( status ) ) ;
fd_close ( fsp ) ;
file_free ( NULL , fsp ) ;
return status ;
}
DBG_DEBUG ( " fsp [%s]: OK, fd=%d \n " , fsp_str_dbg ( fsp ) , fd ) ;
talloc_set_destructor ( smb_fname_rel , smb_fname_fsp_destructor ) ;
return NT_STATUS_OK ;
}
2020-11-23 07:59:02 +03:00
void smb_fname_fsp_unlink ( struct smb_filename * smb_fname )
{
talloc_set_destructor ( smb_fname , NULL ) ;
smb_fname - > fsp = NULL ;
destroy_fsp_smb_fname_link ( & smb_fname - > fsp_link ) ;
}
2020-11-23 08:00:40 +03:00
/*
* Move any existing embedded fsp refs from the src name to the
* destination . It ' s safe to call this on src smb_fname ' s that have no embedded
* pathref fsp .
*/
NTSTATUS move_smb_fname_fsp_link ( struct smb_filename * smb_fname_dst ,
struct smb_filename * smb_fname_src )
{
NTSTATUS status ;
2020-12-16 16:03:36 +03:00
/*
* The target should always not be linked yet !
*/
SMB_ASSERT ( smb_fname_dst - > fsp = = NULL ) ;
SMB_ASSERT ( smb_fname_dst - > fsp_link = = NULL ) ;
2020-11-23 08:00:40 +03:00
if ( smb_fname_src - > fsp = = NULL ) {
return NT_STATUS_OK ;
}
2020-12-16 16:03:36 +03:00
status = fsp_smb_fname_link ( smb_fname_src - > fsp ,
2020-11-23 08:00:40 +03:00
& smb_fname_dst - > fsp_link ,
& smb_fname_dst - > fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
2020-12-17 13:05:21 +03:00
talloc_set_destructor ( smb_fname_dst , smb_fname_fsp_destructor ) ;
smb_fname_fsp_unlink ( smb_fname_src ) ;
2020-11-23 08:00:40 +03:00
return NT_STATUS_OK ;
}
2022-04-08 12:44:28 +03:00
static int fsp_ref_no_close_destructor ( struct smb_filename * smb_fname )
{
destroy_fsp_smb_fname_link ( & smb_fname - > fsp_link ) ;
return 0 ;
}
NTSTATUS reference_smb_fname_fsp_link ( struct smb_filename * smb_fname_dst ,
const struct smb_filename * smb_fname_src )
{
NTSTATUS status ;
/*
* The target should always not be linked yet !
*/
SMB_ASSERT ( smb_fname_dst - > fsp = = NULL ) ;
SMB_ASSERT ( smb_fname_dst - > fsp_link = = NULL ) ;
if ( smb_fname_src - > fsp = = NULL ) {
return NT_STATUS_OK ;
}
status = fsp_smb_fname_link ( smb_fname_src - > fsp ,
& smb_fname_dst - > fsp_link ,
& smb_fname_dst - > fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
talloc_set_destructor ( smb_fname_dst , fsp_ref_no_close_destructor ) ;
return NT_STATUS_OK ;
}
2021-01-21 17:04:57 +03:00
/**
* Create an smb_fname and open smb_fname - > fsp pathref
* */
NTSTATUS synthetic_pathref ( TALLOC_CTX * mem_ctx ,
struct files_struct * dirfsp ,
const char * base_name ,
const char * stream_name ,
const SMB_STRUCT_STAT * psbuf ,
NTTIME twrp ,
uint32_t flags ,
struct smb_filename * * _smb_fname )
{
struct smb_filename * smb_fname = NULL ;
NTSTATUS status ;
smb_fname = synthetic_smb_fname ( mem_ctx ,
base_name ,
stream_name ,
psbuf ,
twrp ,
flags ) ;
if ( smb_fname = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
status = openat_pathref_fsp ( dirfsp , smb_fname ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-11-16 00:29:46 +03:00
DBG_NOTICE ( " opening [%s] failed \n " ,
2021-01-21 17:04:57 +03:00
smb_fname_str_dbg ( smb_fname ) ) ;
TALLOC_FREE ( smb_fname ) ;
return status ;
}
* _smb_fname = smb_fname ;
return NT_STATUS_OK ;
}
2021-01-21 19:05:17 +03:00
/**
* Turn a path into a parent pathref and atname
*
* This returns the parent pathref in _parent and the name relative to it . If
* smb_fname was a pathref ( ie smb_fname - > fsp ! = NULL ) , then _atname will be a
* pathref as well , ie _atname - > fsp will point at the same fsp as
* smb_fname - > fsp .
* */
NTSTATUS parent_pathref ( TALLOC_CTX * mem_ctx ,
struct files_struct * dirfsp ,
const struct smb_filename * smb_fname ,
struct smb_filename * * _parent ,
struct smb_filename * * _atname )
{
struct smb_filename * parent = NULL ;
struct smb_filename * atname = NULL ;
NTSTATUS status ;
2021-05-26 21:22:00 +03:00
status = SMB_VFS_PARENT_PATHNAME ( dirfsp - > conn ,
mem_ctx ,
smb_fname ,
& parent ,
& atname ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2021-01-21 19:05:17 +03:00
}
2021-04-06 21:53:40 +03:00
/*
* We know that the parent name must
* exist , and the name has been canonicalized
* even if this was a POSIX pathname .
* Ensure that we follow symlinks for
* the parent . See the torture test
* POSIX - SYMLINK - PARENT for details .
*/
parent - > flags & = ~ SMB_FILENAME_POSIX_PATH ;
2021-01-21 19:05:17 +03:00
status = openat_pathref_fsp ( dirfsp , parent ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( parent ) ;
return status ;
}
2022-04-08 12:44:28 +03:00
status = reference_smb_fname_fsp_link ( atname , smb_fname ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( parent ) ;
return status ;
2021-01-21 19:05:17 +03:00
}
2022-04-08 12:44:28 +03:00
2021-01-21 19:05:17 +03:00
* _parent = parent ;
* _atname = atname ;
return NT_STATUS_OK ;
}
2022-08-17 21:43:47 +03:00
static bool close_file_in_loop ( struct files_struct * fsp ,
enum file_close_type close_type )
1998-08-15 11:27:34 +04:00
{
2022-02-11 11:37:35 +03:00
if ( fsp_is_alternate_stream ( fsp ) ) {
2022-02-02 10:58:15 +03:00
/*
* This is a stream , it can ' t be a base
*/
SMB_ASSERT ( fsp - > stream_fsp = = NULL ) ;
SMB_ASSERT ( fsp - > base_fsp - > stream_fsp = = fsp ) ;
/*
* Remove the base < - > stream link so that
* close_file_free ( ) does not close fsp - > base_fsp as
* well . This would destroy walking the linked list of
* fsps .
*/
fsp - > base_fsp - > stream_fsp = NULL ;
fsp - > base_fsp = NULL ;
2010-03-22 11:11:24 +03:00
2022-08-17 21:43:47 +03:00
close_file_free ( NULL , & fsp , close_type ) ;
2022-02-02 10:58:15 +03:00
return NULL ;
}
if ( fsp - > stream_fsp ! = NULL ) {
/*
* This is the base of a stream .
*/
SMB_ASSERT ( fsp - > stream_fsp - > base_fsp = = fsp ) ;
/*
* Remove the base < - > stream link . This will make fsp
* look like a normal fsp for the next round .
*/
fsp - > stream_fsp - > base_fsp = NULL ;
fsp - > stream_fsp = NULL ;
/*
* Have us called back a second time . In the second
* round , " fsp " now looks like a normal fsp .
*/
2022-02-02 14:27:50 +03:00
return false ;
2022-02-02 10:58:15 +03:00
}
2022-08-17 21:43:47 +03:00
close_file_free ( NULL , & fsp , close_type ) ;
2022-02-02 14:27:50 +03:00
return true ;
}
/****************************************************************************
Close all open files for a connection .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
struct file_close_conn_state {
struct connection_struct * conn ;
2022-08-17 21:43:47 +03:00
enum file_close_type close_type ;
2022-02-02 14:27:50 +03:00
bool fsp_left_behind ;
} ;
static struct files_struct * file_close_conn_fn (
struct files_struct * fsp ,
void * private_data )
{
struct file_close_conn_state * state = private_data ;
bool did_close ;
if ( fsp - > conn ! = state - > conn ) {
return NULL ;
}
if ( fsp - > op ! = NULL & & fsp - > op - > global - > durable ) {
/*
* A tree disconnect closes a durable handle
*/
fsp - > op - > global - > durable = false ;
}
2022-08-17 21:43:47 +03:00
did_close = close_file_in_loop ( fsp , state - > close_type ) ;
2022-02-02 14:27:50 +03:00
if ( ! did_close ) {
state - > fsp_left_behind = true ;
}
2022-02-02 10:58:15 +03:00
return NULL ;
}
2022-08-17 21:39:36 +03:00
void file_close_conn ( connection_struct * conn , enum file_close_type close_type )
2022-02-02 10:58:15 +03:00
{
2022-08-17 21:43:47 +03:00
struct file_close_conn_state state = { . conn = conn ,
. close_type = close_type } ;
2022-02-02 10:58:15 +03:00
files_forall ( conn - > sconn , file_close_conn_fn , & state ) ;
if ( state . fsp_left_behind ) {
state . fsp_left_behind = false ;
files_forall ( conn - > sconn , file_close_conn_fn , & state ) ;
SMB_ASSERT ( ! state . fsp_left_behind ) ;
1998-08-16 08:08:47 +04:00
}
1998-08-15 11:27:34 +04:00
}
/****************************************************************************
2001-10-21 01:59:34 +04:00
Initialise file structures .
1998-08-15 11:27:34 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-09-30 05:49:24 +04:00
2012-05-24 14:20:30 +04:00
static int files_max_open_fds ;
bool file_init_global ( void )
1998-08-15 11:27:34 +04:00
{
2012-05-24 14:20:30 +04:00
int request_max = lp_max_open_files ( ) ;
1998-10-16 10:16:10 +04:00
int real_lim ;
2012-05-24 14:20:30 +04:00
int real_max ;
if ( files_max_open_fds ! = 0 ) {
return true ;
}
1998-09-30 05:49:24 +04:00
1998-10-16 10:16:10 +04:00
/*
* Set the max_open files to be the requested
* max plus a fudgefactor to allow for the extra
* fd ' s we need such as log files etc . . .
*/
2012-05-24 14:20:30 +04:00
real_lim = set_maxfiles ( request_max + MAX_OPEN_FUDGEFACTOR ) ;
1998-10-16 10:16:10 +04:00
2012-05-24 14:20:30 +04:00
real_max = real_lim - MAX_OPEN_FUDGEFACTOR ;
1998-10-16 10:16:10 +04:00
2012-05-24 14:20:30 +04:00
if ( real_max + FILE_HANDLE_OFFSET + MAX_OPEN_PIPES > 65536 ) {
real_max = 65536 - FILE_HANDLE_OFFSET - MAX_OPEN_PIPES ;
}
2002-10-23 02:17:29 +04:00
2012-05-24 14:20:30 +04:00
if ( real_max ! = request_max ) {
DEBUG ( 1 , ( " file_init_global: Information only: requested %d "
2010-08-09 17:57:38 +04:00
" open files, %d are available. \n " ,
2012-05-24 14:20:30 +04:00
request_max , real_max ) ) ;
1998-10-16 10:16:10 +04:00
}
1998-09-30 05:49:24 +04:00
2012-05-24 14:20:30 +04:00
SMB_ASSERT ( real_max > 100 ) ;
2002-10-23 02:17:29 +04:00
2012-05-24 14:20:30 +04:00
files_max_open_fds = real_max ;
return true ;
}
2010-03-22 11:11:24 +03:00
2012-05-24 14:20:30 +04:00
bool file_init ( struct smbd_server_connection * sconn )
{
bool ok ;
ok = file_init_global ( ) ;
if ( ! ok ) {
return false ;
}
sconn - > real_max_open_files = files_max_open_fds ;
2010-09-27 07:50:22 +04:00
return true ;
1998-08-15 11:27:34 +04:00
}
/****************************************************************************
2001-10-21 01:59:34 +04:00
Close files open by a specified vuid .
1998-08-15 11:27:34 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-10-21 01:59:34 +04:00
2022-02-02 10:58:15 +03:00
struct file_close_user_state {
uint64_t vuid ;
bool fsp_left_behind ;
} ;
static struct files_struct * file_close_user_fn (
struct files_struct * fsp ,
void * private_data )
{
struct file_close_user_state * state = private_data ;
bool did_close ;
if ( fsp - > vuid ! = state - > vuid ) {
return NULL ;
}
2022-08-17 21:43:47 +03:00
did_close = close_file_in_loop ( fsp , SHUTDOWN_CLOSE ) ;
2022-02-02 10:58:15 +03:00
if ( ! did_close ) {
state - > fsp_left_behind = true ;
}
return NULL ;
}
2012-06-05 20:23:14 +04:00
void file_close_user ( struct smbd_server_connection * sconn , uint64_t vuid )
1998-08-15 11:27:34 +04:00
{
2022-02-02 10:58:15 +03:00
struct file_close_user_state state = { . vuid = vuid } ;
1998-08-16 08:08:47 +04:00
2022-02-02 10:58:15 +03:00
files_forall ( sconn , file_close_user_fn , & state ) ;
if ( state . fsp_left_behind ) {
state . fsp_left_behind = false ;
files_forall ( sconn , file_close_user_fn , & state ) ;
SMB_ASSERT ( ! state . fsp_left_behind ) ;
1998-08-15 11:27:34 +04:00
}
}
2009-05-18 11:46:05 +04:00
/*
* Walk the files table until " fn " returns non - NULL
*/
2010-03-22 11:16:57 +03:00
struct files_struct * files_forall (
2010-09-27 05:53:00 +04:00
struct smbd_server_connection * sconn ,
2009-05-18 11:46:05 +04:00
struct files_struct * ( * fn ) ( struct files_struct * fsp ,
void * private_data ) ,
void * private_data )
{
struct files_struct * fsp , * next ;
2010-09-27 05:53:00 +04:00
for ( fsp = sconn - > files ; fsp ; fsp = next ) {
2009-05-18 11:46:05 +04:00
struct files_struct * ret ;
next = fsp - > next ;
ret = fn ( fsp , private_data ) ;
if ( ret ! = NULL ) {
return ret ;
}
}
return NULL ;
}
2001-10-21 01:59:34 +04:00
/****************************************************************************
Find a fsp given a file descriptor .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-09-27 06:54:29 +04:00
files_struct * file_find_fd ( struct smbd_server_connection * sconn , int fd )
2001-10-21 01:59:34 +04:00
{
int count = 0 ;
files_struct * fsp ;
2010-09-27 06:54:29 +04:00
for ( fsp = sconn - > files ; fsp ; fsp = fsp - > next , count + + ) {
2020-10-05 08:51:50 +03:00
if ( fsp_get_pathref_fd ( fsp ) = = fd ) {
2001-10-21 01:59:34 +04:00
if ( count > 10 ) {
2010-09-27 06:54:29 +04:00
DLIST_PROMOTE ( sconn - > files , fsp ) ;
2001-10-21 01:59:34 +04:00
}
return fsp ;
}
}
return NULL ;
}
1998-08-15 11:27:34 +04:00
/****************************************************************************
2001-10-21 01:59:34 +04:00
Find a fsp given a device , inode and file_id .
1998-08-15 11:27:34 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-09-23 05:48:45 +04:00
2010-09-27 06:00:31 +04:00
files_struct * file_find_dif ( struct smbd_server_connection * sconn ,
struct file_id id , unsigned long gen_id )
1998-08-15 11:27:34 +04:00
{
1998-08-17 10:47:53 +04:00
int count = 0 ;
1998-08-16 08:08:47 +04:00
files_struct * fsp ;
2012-06-13 14:11:51 +04:00
if ( gen_id = = 0 ) {
return NULL ;
}
2020-11-19 13:09:05 +03:00
for ( fsp = sconn - > files ; fsp ; fsp = fsp - > next , count + + ) {
/*
* We can have a fsp - > fh - > fd = = - 1 here as it could be a stat
* open .
*/
2020-11-19 13:10:35 +03:00
if ( ! file_id_equal ( & fsp - > file_id , & id ) ) {
continue ;
}
2020-11-19 13:21:42 +03:00
if ( ! fsp - > fsp_flags . is_fsa ) {
continue ;
}
2020-11-19 13:10:35 +03:00
if ( fh_get_gen_id ( fsp - > fh ) ! = gen_id ) {
continue ;
}
if ( count > 10 ) {
DLIST_PROMOTE ( sconn - > files , fsp ) ;
}
return fsp ;
1998-08-16 08:08:47 +04:00
}
1998-08-15 11:27:34 +04:00
return NULL ;
}
1998-10-23 07:34:50 +04:00
/****************************************************************************
Find the first fsp given a device and inode .
2005-03-10 04:30:14 +03:00
We use a singleton cache here to speed up searching from getfilepathinfo
calls .
1998-10-23 07:34:50 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-09-27 06:05:25 +04:00
files_struct * file_find_di_first ( struct smbd_server_connection * sconn ,
2020-11-19 13:35:23 +03:00
struct file_id id ,
bool need_fsa )
1998-10-23 07:34:50 +04:00
{
2004-10-19 02:01:10 +04:00
files_struct * fsp ;
1998-10-23 07:34:50 +04:00
2010-09-27 08:06:02 +04:00
if ( file_id_equal ( & sconn - > fsp_fi_cache . id , & id ) ) {
2005-03-10 04:30:14 +03:00
/* Positive or negative cache hit. */
2010-09-27 08:06:02 +04:00
return sconn - > fsp_fi_cache . fsp ;
2005-03-10 04:30:14 +03:00
}
2010-09-27 08:06:02 +04:00
sconn - > fsp_fi_cache . id = id ;
2005-03-10 04:30:14 +03:00
2010-09-27 06:05:25 +04:00
for ( fsp = sconn - > files ; fsp ; fsp = fsp - > next ) {
2020-11-19 13:35:23 +03:00
if ( need_fsa & & ! fsp - > fsp_flags . is_fsa ) {
continue ;
}
2008-09-25 01:28:18 +04:00
if ( file_id_equal ( & fsp - > file_id , & id ) ) {
2005-03-10 04:30:14 +03:00
/* Setup positive cache. */
2010-09-27 08:06:02 +04:00
sconn - > fsp_fi_cache . fsp = fsp ;
2004-10-19 02:01:10 +04:00
return fsp ;
2005-03-10 04:30:14 +03:00
}
2004-10-19 02:01:10 +04:00
}
1998-10-23 07:34:50 +04:00
2005-03-10 04:30:14 +03:00
/* Setup negative cache. */
2010-09-27 08:06:02 +04:00
sconn - > fsp_fi_cache . fsp = NULL ;
2004-10-19 02:01:10 +04:00
return NULL ;
1998-10-23 07:34:50 +04:00
}
/****************************************************************************
Find the next fsp having the same device and inode .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-11-19 13:35:23 +03:00
files_struct * file_find_di_next ( files_struct * start_fsp ,
bool need_fsa )
1998-10-23 07:34:50 +04:00
{
2004-10-19 02:01:10 +04:00
files_struct * fsp ;
1998-10-23 07:34:50 +04:00
2004-10-19 02:01:10 +04:00
for ( fsp = start_fsp - > next ; fsp ; fsp = fsp - > next ) {
2020-11-19 13:35:23 +03:00
if ( need_fsa & & ! fsp - > fsp_flags . is_fsa ) {
continue ;
}
2008-09-25 01:28:18 +04:00
if ( file_id_equal ( & fsp - > file_id , & start_fsp - > file_id ) ) {
2004-10-19 02:01:10 +04:00
return fsp ;
2007-05-29 13:30:34 +04:00
}
2004-10-19 02:01:10 +04:00
}
1998-10-23 07:34:50 +04:00
2004-10-19 02:01:10 +04:00
return NULL ;
1998-10-23 07:34:50 +04:00
}
1998-08-16 08:08:47 +04:00
2014-10-29 01:31:46 +03:00
struct files_struct * file_find_one_fsp_from_lease_key (
struct smbd_server_connection * sconn ,
const struct smb2_lease_key * lease_key )
{
struct files_struct * fsp ;
for ( fsp = sconn - > files ; fsp ; fsp = fsp - > next ) {
if ( ( fsp - > lease ! = NULL ) & &
( fsp - > lease - > lease . lease_key . data [ 0 ] = =
lease_key - > data [ 0 ] ) & &
( fsp - > lease - > lease . lease_key . data [ 1 ] = =
lease_key - > data [ 1 ] ) ) {
return fsp ;
}
}
return NULL ;
}
2009-02-26 22:42:23 +03:00
/****************************************************************************
2024-10-06 17:37:47 +03:00
Find any fsp open with a pathname below that of an already open path ,
ignoring POSIX opens .
2009-02-26 22:42:23 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool file_find_subpath ( files_struct * dir_fsp )
{
files_struct * fsp ;
size_t dlen ;
2009-08-07 02:53:33 +04:00
char * d_fullname = NULL ;
2009-07-11 01:50:37 +04:00
d_fullname = talloc_asprintf ( talloc_tos ( ) , " %s/%s " ,
dir_fsp - > conn - > connectpath ,
dir_fsp - > fsp_name - > base_name ) ;
2009-02-26 22:42:23 +03:00
if ( ! d_fullname ) {
2009-10-08 02:43:43 +04:00
return false ;
2009-02-26 22:42:23 +03:00
}
dlen = strlen ( d_fullname ) ;
2010-09-27 06:12:15 +04:00
for ( fsp = dir_fsp - > conn - > sconn - > files ; fsp ; fsp = fsp - > next ) {
2009-02-26 22:42:23 +03:00
char * d1_fullname ;
if ( fsp = = dir_fsp ) {
continue ;
}
2024-10-06 17:37:47 +03:00
if ( fsp - > fsp_flags . posix_open ) {
continue ;
}
2009-02-26 22:42:23 +03:00
d1_fullname = talloc_asprintf ( talloc_tos ( ) ,
" %s/%s " ,
fsp - > conn - > connectpath ,
2009-07-11 01:50:37 +04:00
fsp - > fsp_name - > base_name ) ;
2009-02-26 22:42:23 +03:00
2009-10-08 02:39:38 +04:00
/*
* If the open file has a path that is a longer
* component , then it ' s a subpath .
*/
if ( strnequal ( d_fullname , d1_fullname , dlen ) & &
( d1_fullname [ dlen ] = = ' / ' ) ) {
2009-02-26 22:42:23 +03:00
TALLOC_FREE ( d1_fullname ) ;
2009-10-08 02:39:38 +04:00
TALLOC_FREE ( d_fullname ) ;
return true ;
2009-02-26 22:42:23 +03:00
}
TALLOC_FREE ( d1_fullname ) ;
2009-10-08 02:39:38 +04:00
}
2009-02-26 22:42:23 +03:00
TALLOC_FREE ( d_fullname ) ;
2009-10-08 02:43:43 +04:00
return false ;
2009-02-26 22:42:23 +03:00
}
1998-08-16 08:08:47 +04:00
/****************************************************************************
2001-10-21 01:59:34 +04:00
Free up a fsp .
1998-08-16 08:08:47 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-10-21 01:59:34 +04:00
2020-12-21 14:41:06 +03:00
static void fsp_free ( files_struct * fsp )
2012-06-07 18:31:14 +04:00
{
struct smbd_server_connection * sconn = fsp - > conn - > sconn ;
2016-03-17 01:57:33 +03:00
if ( fsp = = sconn - > fsp_fi_cache . fsp ) {
ZERO_STRUCT ( sconn - > fsp_fi_cache ) ;
}
2012-06-07 18:31:14 +04:00
DLIST_REMOVE ( sconn - > files , fsp ) ;
SMB_ASSERT ( sconn - > num_files > 0 ) ;
sconn - > num_files - - ;
TALLOC_FREE ( fsp - > fake_file_handle ) ;
2020-09-28 11:37:36 +03:00
if ( fh_get_refcount ( fsp - > fh ) = = 1 ) {
2012-06-07 18:31:14 +04:00
TALLOC_FREE ( fsp - > fh ) ;
} else {
2020-09-28 11:37:36 +03:00
size_t new_refcount = fh_get_refcount ( fsp - > fh ) - 1 ;
fh_set_refcount ( fsp - > fh , new_refcount ) ;
2012-06-07 18:31:14 +04:00
}
2014-10-29 01:31:46 +03:00
if ( fsp - > lease ! = NULL ) {
if ( fsp - > lease - > ref_count = = 1 ) {
TALLOC_FREE ( fsp - > lease ) ;
} else {
fsp - > lease - > ref_count - - ;
}
}
2012-06-07 18:31:14 +04:00
fsp - > conn - > num_files_open - - ;
2020-06-10 16:21:35 +03:00
if ( fsp - > fsp_name ! = NULL & &
fsp - > fsp_name - > fsp_link ! = NULL )
{
/*
* Free fsp_link of fsp - > fsp_name . To do this in the correct
* talloc destructor order we have to do it here . The
* talloc_free ( ) of the link should set the fsp pointer to NULL .
*/
TALLOC_FREE ( fsp - > fsp_name - > fsp_link ) ;
SMB_ASSERT ( fsp - > fsp_name - > fsp = = NULL ) ;
}
2012-06-07 18:31:14 +04:00
/* this is paranoia, just in case someone tries to reuse the
information */
ZERO_STRUCTP ( fsp ) ;
/* fsp->fsp_name is a talloc child and is free'd automatically. */
TALLOC_FREE ( fsp ) ;
}
2022-02-09 19:23:03 +03:00
/*
* Rundown of all smb - related sub - structures of an fsp
*/
void fsp_unbind_smb ( struct smb_request * req , files_struct * fsp )
1998-08-15 11:27:34 +04:00
{
2020-06-13 17:21:22 +03:00
if ( fsp = = fsp - > conn - > cwd_fsp ) {
return ;
}
2007-01-31 17:42:56 +03:00
if ( fsp - > notify ) {
2016-06-13 19:06:08 +03:00
size_t len = fsp_fullbasepath ( fsp , NULL , 0 ) ;
char fullpath [ len + 1 ] ;
fsp_fullbasepath ( fsp , fullpath , sizeof ( fullpath ) ) ;
notify_remove ( fsp - > conn - > sconn - > notify_ctx , fsp , fullpath ) ;
2007-01-31 17:42:56 +03:00
TALLOC_FREE ( fsp - > notify ) ;
}
2007-01-17 19:23:45 +03:00
2008-03-12 17:39:38 +03:00
/* Ensure this event will never fire. */
TALLOC_FREE ( fsp - > update_write_time_event ) ;
2012-06-08 19:51:47 +04:00
if ( fsp - > op ! = NULL ) {
fsp - > op - > compat = NULL ;
2012-05-24 12:43:56 +04:00
}
2012-06-08 19:51:47 +04:00
TALLOC_FREE ( fsp - > op ) ;
1998-08-16 08:08:47 +04:00
2008-10-09 18:55:56 +04:00
if ( ( req ! = NULL ) & & ( fsp = = req - > chain_fsp ) ) {
req - > chain_fsp = NULL ;
2004-10-19 02:01:10 +04:00
}
1998-08-17 07:52:05 +04:00
2010-06-09 08:20:07 +04:00
/*
* Clear all possible chained fsp
* pointers in the SMB2 request queue .
*/
2017-06-20 21:20:38 +03:00
remove_smb2_chained_fsp ( fsp ) ;
2022-02-09 19:23:03 +03:00
}
void file_free ( struct smb_request * req , files_struct * fsp )
{
struct smbd_server_connection * sconn = fsp - > conn - > sconn ;
uint64_t fnum = fsp - > fnum ;
fsp_unbind_smb ( req , fsp ) ;
2010-06-09 08:20:07 +04:00
2007-10-16 00:59:37 +04:00
/* Drop all remaining extensions. */
2012-06-04 19:55:28 +04:00
vfs_remove_all_fsp_extensions ( fsp ) ;
2007-10-16 00:59:37 +04:00
2012-06-07 18:31:14 +04:00
fsp_free ( fsp ) ;
2012-06-07 18:27:24 +04:00
2021-11-05 20:52:56 +03:00
DBG_INFO ( " freed files structure % " PRIu64 " (%zu used) \n " ,
fnum ,
sconn - > num_files ) ;
1998-08-15 11:27:34 +04:00
}
1998-08-17 07:52:05 +04:00
2006-07-11 22:01:26 +04:00
/****************************************************************************
2010-09-27 04:32:27 +04:00
Get an fsp from a packet given a 16 bit fnum .
2006-07-11 22:01:26 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2015-05-03 07:01:14 +03:00
files_struct * file_fsp ( struct smb_request * req , uint16_t fid )
2006-07-11 22:01:26 +04:00
{
2012-06-08 19:51:47 +04:00
struct smbXsrv_open * op ;
NTSTATUS status ;
NTTIME now = 0 ;
2006-07-11 22:01:26 +04:00
files_struct * fsp ;
2010-09-27 06:46:18 +04:00
if ( req = = NULL ) {
/*
* We should never get here . req = = NULL could in theory
* only happen from internal opens with a non - zero
* root_dir_fid . Internal opens just don ' t do that , at
* least they are not supposed to do so . And if they
* start to do so , they better fake up a smb_request
* from which we get the right smbd_server_conn . While
* this should never happen , let ' s return NULL here .
*/
return NULL ;
}
if ( req - > chain_fsp ! = NULL ) {
2020-04-03 09:06:27 +03:00
if ( req - > chain_fsp - > fsp_flags . closing ) {
2020-03-12 01:16:35 +03:00
return NULL ;
}
2008-10-09 18:55:56 +04:00
return req - > chain_fsp ;
2006-07-11 22:01:26 +04:00
}
2014-06-11 16:25:07 +04:00
if ( req - > xconn = = NULL ) {
2012-06-08 19:51:47 +04:00
return NULL ;
}
now = timeval_to_nttime ( & req - > request_time ) ;
2014-06-11 16:25:07 +04:00
status = smb1srv_open_lookup ( req - > xconn ,
2012-06-08 19:51:47 +04:00
fid , now , & op ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return NULL ;
}
fsp = op - > compat ;
2012-07-23 14:07:42 +04:00
if ( fsp = = NULL ) {
return NULL ;
2006-07-11 22:01:26 +04:00
}
2012-07-23 14:07:42 +04:00
2020-04-03 09:06:27 +03:00
if ( fsp - > fsp_flags . closing ) {
2020-03-12 01:16:35 +03:00
return NULL ;
}
2012-07-23 14:07:42 +04:00
req - > chain_fsp = fsp ;
2023-10-31 12:05:30 +03:00
fsp - > fsp_name - > st . cached_dos_attributes = FILE_ATTRIBUTE_INVALID ;
2006-07-11 22:01:26 +04:00
return fsp ;
}
2013-01-15 20:23:04 +04:00
struct files_struct * file_fsp_get ( struct smbd_smb2_request * smb2req ,
uint64_t persistent_id ,
uint64_t volatile_id )
2012-06-08 12:11:57 +04:00
{
2012-06-08 19:51:47 +04:00
struct smbXsrv_open * op ;
NTSTATUS status ;
NTTIME now = 0 ;
2012-06-08 12:11:57 +04:00
struct files_struct * fsp ;
2012-06-08 19:51:47 +04:00
now = timeval_to_nttime ( & smb2req - > request_time ) ;
2012-06-08 12:11:57 +04:00
2014-06-11 16:25:07 +04:00
status = smb2srv_open_lookup ( smb2req - > xconn ,
2012-06-08 19:51:47 +04:00
persistent_id , volatile_id ,
now , & op ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2012-06-08 12:11:57 +04:00
return NULL ;
}
2012-06-13 14:13:01 +04:00
2012-06-08 19:51:47 +04:00
fsp = op - > compat ;
if ( fsp = = NULL ) {
2012-06-13 14:13:01 +04:00
return NULL ;
}
2012-06-08 12:11:57 +04:00
if ( smb2req - > tcon = = NULL ) {
return NULL ;
}
2012-03-27 13:09:05 +04:00
if ( smb2req - > tcon - > compat ! = fsp - > conn ) {
2012-06-08 12:11:57 +04:00
return NULL ;
}
if ( smb2req - > session = = NULL ) {
return NULL ;
}
2019-12-29 16:33:00 +03:00
if ( smb2req - > session - > global - > session_wire_id ! = fsp - > vuid ) {
2012-06-08 12:11:57 +04:00
return NULL ;
}
2020-04-03 09:06:27 +03:00
if ( fsp - > fsp_flags . closing ) {
2020-03-12 01:16:35 +03:00
return NULL ;
}
2023-10-31 12:05:30 +03:00
fsp - > fsp_name - > st . cached_dos_attributes = FILE_ATTRIBUTE_INVALID ;
2022-08-24 12:40:41 +03:00
2013-01-15 20:23:04 +04:00
return fsp ;
}
struct files_struct * file_fsp_smb2 ( struct smbd_smb2_request * smb2req ,
uint64_t persistent_id ,
uint64_t volatile_id )
{
struct files_struct * fsp ;
if ( smb2req - > compat_chain_fsp ! = NULL ) {
2020-04-03 09:06:27 +03:00
if ( smb2req - > compat_chain_fsp - > fsp_flags . closing ) {
2020-03-12 01:16:35 +03:00
return NULL ;
}
2022-08-24 12:40:41 +03:00
smb2req - > compat_chain_fsp - > fsp_name - > st . cached_dos_attributes =
2023-10-31 12:05:30 +03:00
FILE_ATTRIBUTE_INVALID ;
2013-01-15 20:23:04 +04:00
return smb2req - > compat_chain_fsp ;
}
fsp = file_fsp_get ( smb2req , persistent_id , volatile_id ) ;
if ( fsp = = NULL ) {
return NULL ;
}
2012-06-08 12:11:57 +04:00
smb2req - > compat_chain_fsp = fsp ;
return fsp ;
}
2011-01-26 00:49:01 +03:00
/**
* Return a jenkins hash of a pathname on a connection .
*/
NTSTATUS file_name_hash ( connection_struct * conn ,
const char * name , uint32_t * p_name_hash )
{
2013-12-14 13:45:42 +04:00
char tmpbuf [ PATH_MAX ] ;
2013-12-06 18:34:05 +04:00
char * fullpath , * to_free ;
2014-12-05 16:38:48 +03:00
ssize_t len ;
2015-03-12 18:00:58 +03:00
TDB_DATA key ;
2011-01-26 00:49:01 +03:00
/* Set the hash of the full pathname. */
2013-12-06 18:34:05 +04:00
2021-06-23 09:39:41 +03:00
if ( name [ 0 ] = = ' / ' ) {
strlcpy ( tmpbuf , name , sizeof ( tmpbuf ) ) ;
fullpath = tmpbuf ;
len = strlen ( fullpath ) ;
to_free = NULL ;
} else {
len = full_path_tos ( conn - > connectpath ,
name ,
tmpbuf ,
sizeof ( tmpbuf ) ,
& fullpath ,
& to_free ) ;
}
2013-12-06 18:34:05 +04:00
if ( len = = - 1 ) {
2011-01-26 00:49:01 +03:00
return NT_STATUS_NO_MEMORY ;
}
2015-03-12 18:00:58 +03:00
key = ( TDB_DATA ) { . dptr = ( uint8_t * ) fullpath , . dsize = len + 1 } ;
* p_name_hash = tdb_jenkins_hash ( & key ) ;
2011-01-26 00:49:01 +03:00
DEBUG ( 10 , ( " file_name_hash: %s hash 0x%x \n " ,
2013-12-06 18:34:05 +04:00
fullpath ,
2011-01-26 00:49:01 +03:00
( unsigned int ) * p_name_hash ) ) ;
2013-12-06 18:34:05 +04:00
TALLOC_FREE ( to_free ) ;
2011-01-26 00:49:01 +03:00
return NT_STATUS_OK ;
}
2020-12-16 17:01:38 +03:00
static NTSTATUS fsp_attach_smb_fname ( struct files_struct * fsp ,
struct smb_filename * * _smb_fname )
2009-07-11 01:50:37 +04:00
{
2023-06-08 13:19:00 +03:00
TALLOC_CTX * frame = talloc_stackframe ( ) ;
2022-06-14 17:52:39 +03:00
struct smb_filename * smb_fname_new = talloc_move ( fsp , _smb_fname ) ;
2020-12-16 16:15:11 +03:00
const char * name_str = NULL ;
uint32_t name_hash = 0 ;
2020-11-23 08:23:12 +03:00
NTSTATUS status ;
2009-07-11 01:50:37 +04:00
2020-12-16 16:15:11 +03:00
name_str = smb_fname_str_dbg ( smb_fname_new ) ;
if ( name_str = = NULL ) {
2023-06-08 13:19:00 +03:00
TALLOC_FREE ( frame ) ;
2020-12-16 16:15:11 +03:00
return NT_STATUS_NO_MEMORY ;
}
status = file_name_hash ( fsp - > conn ,
name_str ,
& name_hash ) ;
2023-06-08 13:19:00 +03:00
TALLOC_FREE ( frame ) ;
name_str = NULL ;
2020-12-16 16:15:11 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2020-11-23 08:23:12 +03:00
}
2020-12-16 16:15:11 +03:00
status = fsp_smb_fname_link ( fsp ,
& smb_fname_new - > fsp_link ,
& smb_fname_new - > fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
}
fsp - > name_hash = name_hash ;
2009-07-11 01:50:37 +04:00
fsp - > fsp_name = smb_fname_new ;
2023-10-31 12:05:30 +03:00
fsp - > fsp_name - > st . cached_dos_attributes = FILE_ATTRIBUTE_INVALID ;
2020-12-16 17:01:38 +03:00
* _smb_fname = NULL ;
return NT_STATUS_OK ;
}
/**
* The only way that the fsp - > fsp_name field should ever be set .
*/
NTSTATUS fsp_set_smb_fname ( struct files_struct * fsp ,
const struct smb_filename * smb_fname_in )
{
struct smb_filename * smb_fname_old = fsp - > fsp_name ;
struct smb_filename * smb_fname_new = NULL ;
NTSTATUS status ;
smb_fname_new = cp_smb_filename ( fsp , smb_fname_in ) ;
if ( smb_fname_new = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
status = fsp_attach_smb_fname ( fsp , & smb_fname_new ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( smb_fname_new ) ;
return status ;
}
2009-07-11 01:50:37 +04:00
2020-12-16 16:15:11 +03:00
if ( smb_fname_old ! = NULL ) {
smb_fname_fsp_unlink ( smb_fname_old ) ;
TALLOC_FREE ( smb_fname_old ) ;
}
return NT_STATUS_OK ;
2005-07-08 08:51:27 +04:00
}
2014-11-05 08:46:14 +03:00
2016-06-13 17:12:54 +03:00
size_t fsp_fullbasepath ( struct files_struct * fsp , char * buf , size_t buflen )
{
2019-05-21 16:36:45 +03:00
int len = 0 ;
2024-02-04 12:57:28 +03:00
if ( buf = = NULL ) {
/*
* susv4 allows buf = = NULL if buflen = = 0 for snprintf .
*/
SMB_ASSERT ( buflen = = 0 ) ;
2019-05-21 16:36:45 +03:00
}
2016-06-13 17:12:54 +03:00
2024-02-04 13:10:18 +03:00
if ( ISDOT ( fsp - > fsp_name - > base_name ) ) {
len = snprintf ( buf , buflen , " %s " , fsp - > conn - > connectpath ) ;
} else {
len = snprintf ( buf ,
buflen ,
" %s/%s " ,
fsp - > conn - > connectpath ,
fsp - > fsp_name - > base_name ) ;
}
SMB_ASSERT ( len > 0 ) ;
2016-06-13 17:12:54 +03:00
return len ;
}
2020-12-21 17:44:22 +03:00
void fsp_set_base_fsp ( struct files_struct * fsp , struct files_struct * base_fsp )
{
2020-12-23 13:50:34 +03:00
SMB_ASSERT ( fsp - > stream_fsp = = NULL ) ;
2020-12-21 17:44:22 +03:00
if ( base_fsp ! = NULL ) {
SMB_ASSERT ( base_fsp - > base_fsp = = NULL ) ;
2020-12-23 13:50:34 +03:00
SMB_ASSERT ( base_fsp - > stream_fsp = = NULL ) ;
}
if ( fsp - > base_fsp ! = NULL ) {
SMB_ASSERT ( fsp - > base_fsp - > stream_fsp = = fsp ) ;
fsp - > base_fsp - > stream_fsp = NULL ;
2020-12-21 17:44:22 +03:00
}
fsp - > base_fsp = base_fsp ;
2020-12-23 13:50:34 +03:00
if ( fsp - > base_fsp ! = NULL ) {
fsp - > base_fsp - > stream_fsp = fsp ;
}
2020-12-21 17:44:22 +03:00
}
2022-02-11 11:37:35 +03:00
bool fsp_is_alternate_stream ( const struct files_struct * fsp )
{
return ( fsp - > base_fsp ! = NULL ) ;
}
2022-02-11 11:45:30 +03:00
struct files_struct * metadata_fsp ( struct files_struct * fsp )
{
if ( fsp_is_alternate_stream ( fsp ) ) {
return fsp - > base_fsp ;
}
return fsp ;
}
2022-05-11 19:09:10 +03:00
static bool fsp_generic_ask_sharemode ( struct files_struct * fsp )
{
if ( fsp = = NULL ) {
return false ;
}
2024-10-04 20:22:39 +03:00
if ( fsp - > fsp_flags . posix_open ) {
2022-05-11 19:09:10 +03:00
/* Always use filesystem for UNIX mtime query. */
return false ;
}
return true ;
}
bool fsp_search_ask_sharemode ( struct files_struct * fsp )
{
if ( ! fsp_generic_ask_sharemode ( fsp ) ) {
return false ;
}
return lp_smbd_search_ask_sharemode ( SNUM ( fsp - > conn ) ) ;
}
bool fsp_getinfo_ask_sharemode ( struct files_struct * fsp )
{
if ( ! fsp_generic_ask_sharemode ( fsp ) ) {
return false ;
}
return lp_smbd_getinfo_ask_sharemode ( SNUM ( fsp - > conn ) ) ;
}