1998-08-17 17:11:34 +04:00
/*
2002-01-30 09:08:46 +03:00
Unix SMB / CIFS implementation .
1998-08-17 17:11:34 +04:00
oplock processing
Copyright ( C ) Andrew Tridgell 1992 - 1998
2001-10-21 01:59:34 +04:00
Copyright ( C ) Jeremy Allison 1998 - 2001
2005-09-30 21:13:37 +04:00
Copyright ( C ) Volker Lendecke 2005
2011-08-02 18:44:53 +04:00
1998-08-17 17:11: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-17 17:11:34 +04:00
( at your option ) any later version .
2011-08-02 18:44:53 +04:00
1998-08-17 17:11: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 .
2011-08-02 18:44:53 +04:00
1998-08-17 17:11: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-17 17:11:34 +04:00
*/
2006-05-05 06:06:37 +04:00
# define DBGC_CLASS DBGC_LOCKING
1998-08-17 17:11:34 +04:00
# include "includes.h"
2017-01-01 23:00:55 +03:00
# include "lib/util/server_id.h"
2020-10-28 14:09:39 +03:00
# include "locking/share_mode_lock.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"
2011-03-24 17:31:06 +03:00
# include "messages.h"
2019-04-08 17:21:41 +03:00
# include "locking/leases_db.h"
2017-05-26 12:57:08 +03:00
# include "../librpc/gen_ndr/ndr_open_files.h"
2024-09-16 03:36:00 +03:00
# include "lib/util/tevent_ntstatus.h"
2024-10-17 20:18:57 +03:00
# include "source3/smbd/dir.h"
2000-06-10 17:38:07 +04:00
2009-01-23 12:08:44 +03:00
/*
* helper function used by the kernel oplock backends to post the break message
*/
void break_kernel_oplock ( struct messaging_context * msg_ctx , files_struct * fsp )
2001-09-06 02:45:48 +04:00
{
2024-04-30 12:41:50 +03:00
struct oplock_break_message msg = {
. id = fsp - > file_id ,
. share_file_id = fh_get_gen_id ( fsp - > fh ) ,
} ;
enum ndr_err_code ndr_err ;
uint8_t msgbuf [ 33 ] ;
DATA_BLOB blob = { . data = msgbuf , . length = sizeof ( msgbuf ) } ;
2000-06-09 10:58:06 +04:00
2009-01-23 12:08:44 +03:00
/* Don't need to be root here as we're only ever
sending to ourselves . */
2002-07-15 14:35:28 +04:00
2024-04-30 12:41:50 +03:00
ndr_err = ndr_push_struct_into_fixed_blob (
& blob ,
& msg ,
( ndr_push_flags_fn_t ) ndr_push_oplock_break_message ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DBG_WARNING ( " ndr_push_oplock_break_message failed: %s \n " ,
ndr_errstr ( ndr_err ) ) ;
return ;
}
messaging_send ( msg_ctx ,
messaging_server_id ( msg_ctx ) ,
MSG_SMB_KERNEL_BREAK ,
& blob ) ;
1998-09-23 05:48:45 +04:00
}
1999-12-13 16:27:58 +03:00
/****************************************************************************
2011-01-31 23:00:15 +03:00
Attempt to set an oplock on a file . Succeeds if kernel oplocks are
2013-09-03 17:31:27 +04:00
disabled ( just sets flags ) .
1999-12-13 16:27:58 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-10-21 01:59:34 +04:00
2013-09-03 17:57:11 +04:00
NTSTATUS set_file_oplock ( files_struct * fsp )
1999-12-13 16:27:58 +03:00
{
2012-05-25 01:15:08 +04:00
struct smbd_server_connection * sconn = fsp - > conn - > sconn ;
2012-05-25 01:33:32 +04:00
struct kernel_oplocks * koplocks = sconn - > oplocks . kernel_ops ;
2018-01-03 20:52:33 +03:00
bool use_kernel = lp_kernel_oplocks ( SNUM ( fsp - > conn ) ) & &
( koplocks ! = NULL ) ;
2019-11-04 09:39:48 +03:00
struct file_id_buf buf ;
2012-03-30 18:11:08 +04:00
2022-09-03 03:46:34 +03:00
smb_vfs_assert_allowed ( ) ;
2018-08-24 08:12:23 +03:00
if ( fsp - > oplock_type = = LEVEL_II_OPLOCK & & use_kernel ) {
DEBUG ( 10 , ( " Refusing level2 oplock, kernel oplocks "
" don't support them \n " ) ) ;
return NT_STATUS_NOT_SUPPORTED ;
2009-04-08 10:29:23 +04:00
}
2011-01-31 23:00:15 +03:00
2008-12-18 04:23:13 +03:00
if ( ( fsp - > oplock_type ! = NO_OPLOCK ) & &
2012-03-30 18:11:08 +04:00
use_kernel & &
2013-09-03 17:57:11 +04:00
! koplocks - > ops - > set_oplock ( koplocks , fsp , fsp - > oplock_type ) )
2012-06-08 19:47:31 +04:00
{
return map_nt_error_from_unix ( errno ) ;
2006-01-13 01:17:54 +03:00
}
1999-12-13 16:27:58 +03:00
2000-06-09 10:58:06 +04:00
fsp - > sent_oplock_break = NO_BREAK_SENT ;
2013-09-03 17:57:11 +04:00
if ( fsp - > oplock_type = = LEVEL_II_OPLOCK ) {
2012-05-25 01:15:08 +04:00
sconn - > oplocks . level_II_open + + ;
2008-12-18 04:23:13 +03:00
} else if ( EXCLUSIVE_OPLOCK_TYPE ( fsp - > oplock_type ) ) {
2012-05-25 01:15:08 +04:00
sconn - > oplocks . exclusive_open + + ;
2006-01-13 01:17:54 +03:00
}
1999-12-13 16:27:58 +03:00
2019-09-16 13:50:02 +03:00
DBG_INFO ( " granted oplock on file %s, %s/% " PRIu64 " , "
" tv_sec = %x, tv_usec = %x \n " ,
fsp_str_dbg ( fsp ) ,
2019-11-03 20:52:07 +03:00
file_id_str_buf ( fsp - > file_id , & buf ) ,
2020-09-28 11:35:32 +03:00
fh_get_gen_id ( fsp - > fh ) ,
2019-09-16 13:50:02 +03:00
( int ) fsp - > open_time . tv_sec ,
( int ) fsp - > open_time . tv_usec ) ;
1998-08-17 17:11:34 +04:00
2012-06-08 19:47:31 +04:00
return NT_STATUS_OK ;
1998-08-17 17:11:34 +04:00
}
2018-12-14 15:05:50 +03:00
static void release_fsp_kernel_oplock ( files_struct * fsp )
{
struct smbd_server_connection * sconn = fsp - > conn - > sconn ;
struct kernel_oplocks * koplocks = sconn - > oplocks . kernel_ops ;
bool use_kernel ;
2022-09-03 03:46:34 +03:00
smb_vfs_assert_allowed ( ) ;
2018-12-14 15:05:50 +03:00
if ( koplocks = = NULL ) {
return ;
}
use_kernel = lp_kernel_oplocks ( SNUM ( fsp - > conn ) ) ;
if ( ! use_kernel ) {
return ;
}
if ( fsp - > oplock_type = = NO_OPLOCK ) {
return ;
}
if ( fsp - > oplock_type = = LEASE_OPLOCK ) {
/*
* For leases we don ' t touch kernel oplocks at all
*/
return ;
}
koplocks - > ops - > release_oplock ( koplocks , fsp , NO_OPLOCK ) ;
}
1999-12-13 16:27:58 +03:00
/****************************************************************************
Attempt to release an oplock on a file . Decrements oplock count .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2022-08-30 10:48:40 +03:00
void release_file_oplock ( files_struct * fsp )
1999-12-13 16:27:58 +03:00
{
2012-05-25 01:15:08 +04:00
struct smbd_server_connection * sconn = fsp - > conn - > sconn ;
2018-12-14 15:05:50 +03:00
release_fsp_kernel_oplock ( fsp ) ;
2000-06-09 10:58:06 +04:00
2006-01-13 01:17:54 +03:00
if ( fsp - > oplock_type = = LEVEL_II_OPLOCK ) {
2012-05-25 01:15:08 +04:00
sconn - > oplocks . level_II_open - - ;
2006-01-13 01:17:54 +03:00
} else if ( EXCLUSIVE_OPLOCK_TYPE ( fsp - > oplock_type ) ) {
2012-05-25 01:15:08 +04:00
sconn - > oplocks . exclusive_open - - ;
2006-01-13 01:17:54 +03:00
}
2005-09-30 21:13:37 +04:00
2012-05-25 01:15:08 +04:00
SMB_ASSERT ( sconn - > oplocks . exclusive_open > = 0 ) ;
SMB_ASSERT ( sconn - > oplocks . level_II_open > = 0 ) ;
2008-12-18 04:23:13 +03:00
2013-09-11 20:07:33 +04:00
fsp - > oplock_type = NO_OPLOCK ;
2000-06-09 10:58:06 +04:00
fsp - > sent_oplock_break = NO_BREAK_SENT ;
2008-12-18 04:23:13 +03:00
2007-06-22 21:19:08 +04:00
TALLOC_FREE ( fsp - > oplock_timeout ) ;
1999-12-13 16:27:58 +03:00
}
/****************************************************************************
Attempt to downgrade an oplock on a file . Doesn ' t decrement oplock count .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void downgrade_file_oplock ( files_struct * fsp )
{
2012-05-25 01:15:08 +04:00
struct smbd_server_connection * sconn = fsp - > conn - > sconn ;
2012-05-25 01:33:32 +04:00
struct kernel_oplocks * koplocks = sconn - > oplocks . kernel_ops ;
2018-01-03 20:52:33 +03:00
bool use_kernel = lp_kernel_oplocks ( SNUM ( fsp - > conn ) ) & &
( koplocks ! = NULL ) ;
2012-05-25 01:15:08 +04:00
2022-09-03 03:46:34 +03:00
smb_vfs_assert_allowed ( ) ;
2009-01-28 03:13:35 +03:00
if ( ! EXCLUSIVE_OPLOCK_TYPE ( fsp - > oplock_type ) ) {
DEBUG ( 0 , ( " trying to downgrade an already-downgraded oplock! \n " ) ) ;
return ;
}
2018-01-03 20:52:33 +03:00
if ( use_kernel ) {
2009-01-10 00:07:58 +03:00
koplocks - > ops - > release_oplock ( koplocks , fsp , LEVEL_II_OPLOCK ) ;
2006-01-13 01:17:54 +03:00
}
2000-06-10 17:38:07 +04:00
fsp - > oplock_type = LEVEL_II_OPLOCK ;
2012-05-25 01:15:08 +04:00
sconn - > oplocks . exclusive_open - - ;
sconn - > oplocks . level_II_open + + ;
2000-06-10 17:38:07 +04:00
fsp - > sent_oplock_break = NO_BREAK_SENT ;
2013-09-04 15:57:00 +04:00
TALLOC_FREE ( fsp - > oplock_timeout ) ;
1999-12-13 16:27:58 +03:00
}
2020-07-02 15:45:59 +03:00
uint32_t get_lease_type ( struct share_mode_entry * e , struct file_id id )
2014-10-29 01:31:46 +03:00
{
2020-07-02 15:47:12 +03:00
struct GUID_txt_buf guid_strbuf ;
struct file_id_buf file_id_strbuf ;
2020-07-02 15:08:44 +03:00
NTSTATUS status ;
uint32_t current_state ;
if ( e - > op_type ! = LEASE_OPLOCK ) {
return map_oplock_to_lease_type ( e - > op_type ) ;
2014-10-29 01:31:46 +03:00
}
2020-07-02 15:08:44 +03:00
status = leases_db_get ( & e - > client_guid ,
& e - > lease_key ,
& id ,
& current_state ,
NULL , /* breaking */
NULL , /* breaking_to_requested */
NULL , /* breaking_to_required */
NULL , /* lease_version */
NULL ) ; /* epoch */
2020-07-02 15:47:12 +03:00
if ( NT_STATUS_IS_OK ( status ) ) {
return current_state ;
}
if ( share_entry_stale_pid ( e ) ) {
return 0 ;
}
DBG_ERR ( " leases_db_get for client_guid [%s] "
2024-10-02 08:46:48 +03:00
" lease_key [% " PRIx64 " /% " PRIx64 " ] "
2020-07-02 15:47:12 +03:00
" file_id [%s] failed: %s \n " ,
GUID_buf_string ( & e - > client_guid , & guid_strbuf ) ,
e - > lease_key . data [ 0 ] ,
e - > lease_key . data [ 1 ] ,
file_id_str_buf ( id , & file_id_strbuf ) ,
nt_errstr ( status ) ) ;
smb_panic ( " leases_db_get() failed " ) ;
2014-10-29 01:31:46 +03:00
}
1999-12-13 16:27:58 +03:00
/****************************************************************************
Remove a file oplock . Copes with level II and exclusive .
2000-11-16 03:59:18 +03:00
Locks then unlocks the share mode lock . Client can decide to go directly
to none even if a " break-to-level II " was sent .
1999-12-13 16:27:58 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-10-19 04:40:25 +04:00
bool remove_oplock ( files_struct * fsp )
1999-12-13 16:27:58 +03:00
{
2007-10-19 04:40:25 +04:00
bool ret ;
2005-09-30 21:13:37 +04:00
struct share_mode_lock * lck ;
2000-06-10 17:38:07 +04:00
2016-08-11 00:35:42 +03:00
DBG_DEBUG ( " remove_oplock called for %s \n " , fsp_str_dbg ( fsp ) ) ;
2013-09-13 16:13:51 +04:00
2000-06-10 17:38:07 +04:00
/* Remove the oplock flag from the sharemode. */
2012-02-19 17:23:56 +04:00
lck = get_existing_share_mode_lock ( talloc_tos ( ) , fsp - > file_id ) ;
2005-09-30 21:13:37 +04:00
if ( lck = = NULL ) {
2016-08-11 00:35:42 +03:00
DBG_ERR ( " failed to lock share entry for "
" file %s \n " , fsp_str_dbg ( fsp ) ) ;
return false ;
2000-06-10 17:38:07 +04:00
}
2013-09-11 20:07:33 +04:00
2019-08-05 19:18:41 +03:00
ret = remove_share_oplock ( lck , fsp ) ;
if ( ! ret ) {
2019-11-04 09:39:48 +03:00
struct file_id_buf buf ;
2019-08-05 19:18:41 +03:00
DBG_ERR ( " failed to remove share oplock for "
" file %s, %s, %s \n " ,
fsp_str_dbg ( fsp ) , fsp_fnum_dbg ( fsp ) ,
2019-11-04 09:39:48 +03:00
file_id_str_buf ( fsp - > file_id , & buf ) ) ;
2019-08-05 19:18:41 +03:00
}
release_file_oplock ( fsp ) ;
2014-10-29 01:27:09 +03:00
2006-02-20 20:59:58 +03:00
TALLOC_FREE ( lck ) ;
2005-09-30 21:13:37 +04:00
return ret ;
}
1999-12-13 16:27:58 +03:00
2005-09-30 21:13:37 +04:00
/*
* Deal with a reply when a break - to - level II was sent .
*/
2007-10-19 04:40:25 +04:00
bool downgrade_oplock ( files_struct * fsp )
2005-09-30 21:13:37 +04:00
{
2007-10-19 04:40:25 +04:00
bool ret ;
2005-09-30 21:13:37 +04:00
struct share_mode_lock * lck ;
1999-12-13 16:27:58 +03:00
2013-09-13 16:13:51 +04:00
DEBUG ( 10 , ( " downgrade_oplock called for %s \n " ,
fsp_str_dbg ( fsp ) ) ) ;
2012-02-19 17:23:56 +04:00
lck = get_existing_share_mode_lock ( talloc_tos ( ) , fsp - > file_id ) ;
2005-09-30 21:13:37 +04:00
if ( lck = = NULL ) {
DEBUG ( 0 , ( " downgrade_oplock: failed to lock share entry for "
2009-07-11 01:50:37 +04:00
" file %s \n " , fsp_str_dbg ( fsp ) ) ) ;
2005-09-30 21:13:37 +04:00
return False ;
2000-06-10 17:38:07 +04:00
}
2005-09-30 21:13:37 +04:00
ret = downgrade_share_oplock ( lck , fsp ) ;
if ( ! ret ) {
2019-11-04 09:39:48 +03:00
struct file_id_buf idbuf ;
DBG_ERR ( " failed to downgrade share oplock "
" for file %s, %s, file_id %s \n " ,
fsp_str_dbg ( fsp ) ,
fsp_fnum_dbg ( fsp ) ,
file_id_str_buf ( fsp - > file_id , & idbuf ) ) ;
2005-09-30 21:13:37 +04:00
}
downgrade_file_oplock ( fsp ) ;
2013-09-11 20:07:33 +04:00
2006-02-20 20:59:58 +03:00
TALLOC_FREE ( lck ) ;
2000-06-10 17:38:07 +04:00
return ret ;
1998-09-23 05:48:45 +04:00
}
2014-10-29 01:31:46 +03:00
static void lease_timeout_handler ( struct tevent_context * ctx ,
struct tevent_timer * te ,
struct timeval now ,
void * private_data )
{
struct fsp_lease * lease =
talloc_get_type_abort ( private_data ,
struct fsp_lease ) ;
struct files_struct * fsp ;
struct share_mode_lock * lck ;
uint16_t old_epoch = lease - > lease . lease_epoch ;
fsp = file_find_one_fsp_from_lease_key ( lease - > sconn ,
& lease - > lease . lease_key ) ;
if ( fsp = = NULL ) {
/* race? */
TALLOC_FREE ( lease - > timeout ) ;
return ;
}
2018-09-13 19:56:54 +03:00
/*
* Paranoia check : There can only be one fsp_lease per lease
* key
*/
SMB_ASSERT ( fsp - > lease = = lease ) ;
2014-10-29 01:31:46 +03:00
lck = get_existing_share_mode_lock (
talloc_tos ( ) , fsp - > file_id ) ;
if ( lck = = NULL ) {
/* race? */
TALLOC_FREE ( lease - > timeout ) ;
return ;
}
2019-05-24 16:58:09 +03:00
fsp_lease_update ( fsp ) ;
2014-10-29 01:31:46 +03:00
if ( lease - > lease . lease_epoch ! = old_epoch ) {
/*
* If the epoch changed we need to wait for
* the next timeout to happen .
*/
DEBUG ( 10 , ( " lease break timeout race (epoch) for file %s - ignoring \n " ,
fsp_str_dbg ( fsp ) ) ) ;
TALLOC_FREE ( lck ) ;
return ;
}
if ( ! ( lease - > lease . lease_flags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS ) ) {
/*
* If the epoch changed we need to wait for
* the next timeout to happen .
*/
DEBUG ( 10 , ( " lease break timeout race (flags) for file %s - ignoring \n " ,
fsp_str_dbg ( fsp ) ) ) ;
TALLOC_FREE ( lck ) ;
return ;
}
DEBUG ( 1 , ( " lease break timed out for file %s -- replying anyway \n " ,
fsp_str_dbg ( fsp ) ) ) ;
2020-06-02 17:33:23 +03:00
( void ) downgrade_lease ( lease - > sconn - > client ,
2014-10-29 01:31:46 +03:00
1 ,
& fsp - > file_id ,
& lease - > lease . lease_key ,
SMB2_LEASE_NONE ) ;
TALLOC_FREE ( lck ) ;
}
2019-05-24 16:58:09 +03:00
bool fsp_lease_update ( struct files_struct * fsp )
2014-10-29 01:31:46 +03:00
{
2019-05-24 16:58:09 +03:00
const struct GUID * client_guid = fsp_client_guid ( fsp ) ;
struct fsp_lease * lease = fsp - > lease ;
2019-04-10 17:38:28 +03:00
uint32_t current_state ;
bool breaking ;
uint16_t lease_version , epoch ;
NTSTATUS status ;
status = leases_db_get ( client_guid ,
2019-07-04 09:48:05 +03:00
& lease - > lease . lease_key ,
2019-05-24 16:58:09 +03:00
& fsp - > file_id ,
2019-04-10 17:38:28 +03:00
& current_state ,
& breaking ,
NULL , /* breaking_to_requested */
NULL , /* breaking_to_required */
& lease_version ,
& epoch ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_WARNING ( " Could not find lease entry: %s \n " ,
nt_errstr ( status ) ) ;
2014-10-29 01:31:46 +03:00
TALLOC_FREE ( lease - > timeout ) ;
lease - > lease . lease_state = SMB2_LEASE_NONE ;
lease - > lease . lease_epoch + = 1 ;
lease - > lease . lease_flags = 0 ;
return false ;
}
DEBUG ( 10 , ( " %s: refresh lease state \n " , __func__ ) ) ;
/* Ensure we're in sync with current lease state. */
2019-04-10 17:38:28 +03:00
if ( lease - > lease . lease_epoch ! = epoch ) {
2014-10-29 01:31:46 +03:00
DEBUG ( 10 , ( " %s: cancel outdated timeout \n " , __func__ ) ) ;
TALLOC_FREE ( lease - > timeout ) ;
}
2019-04-10 17:38:28 +03:00
lease - > lease . lease_epoch = epoch ;
lease - > lease . lease_state = current_state ;
2014-10-29 01:31:46 +03:00
2019-04-10 17:38:28 +03:00
if ( breaking ) {
2014-10-29 01:31:46 +03:00
lease - > lease . lease_flags | = SMB2_LEASE_FLAG_BREAK_IN_PROGRESS ;
if ( lease - > timeout = = NULL ) {
struct timeval t = timeval_current_ofs ( OPLOCK_BREAK_TIMEOUT , 0 ) ;
DEBUG ( 10 , ( " %s: setup timeout handler \n " , __func__ ) ) ;
2018-12-27 17:24:37 +03:00
lease - > timeout = tevent_add_timer ( lease - > sconn - > ev_ctx ,
2014-10-29 01:31:46 +03:00
lease , t ,
lease_timeout_handler ,
lease ) ;
if ( lease - > timeout = = NULL ) {
DEBUG ( 0 , ( " %s: Could not add lease timeout handler \n " ,
__func__ ) ) ;
}
}
} else {
lease - > lease . lease_flags & = ~ SMB2_LEASE_FLAG_BREAK_IN_PROGRESS ;
TALLOC_FREE ( lease - > timeout ) ;
}
return true ;
}
struct downgrade_lease_additional_state {
struct tevent_immediate * im ;
2020-06-02 17:33:23 +03:00
struct smbXsrv_client * client ;
2014-10-29 01:31:46 +03:00
uint32_t break_flags ;
struct smb2_lease_key lease_key ;
uint32_t break_from ;
uint32_t break_to ;
uint16_t new_epoch ;
} ;
static void downgrade_lease_additional_trigger ( struct tevent_context * ev ,
struct tevent_immediate * im ,
void * private_data )
{
struct downgrade_lease_additional_state * state =
talloc_get_type_abort ( private_data ,
struct downgrade_lease_additional_state ) ;
NTSTATUS status ;
2020-06-02 17:50:22 +03:00
status = smbd_smb2_send_lease_break ( state - > client ,
2014-10-29 01:31:46 +03:00
state - > new_epoch ,
state - > break_flags ,
& state - > lease_key ,
state - > break_from ,
state - > break_to ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2020-06-02 17:33:23 +03:00
smbd_server_disconnect_client ( state - > client ,
nt_errstr ( status ) ) ;
2014-10-29 01:31:46 +03:00
}
2020-11-24 18:57:12 +03:00
TALLOC_FREE ( state ) ;
2014-10-29 01:31:46 +03:00
}
2019-05-24 17:50:30 +03:00
struct fsps_lease_update_state {
const struct file_id * id ;
2014-10-29 01:31:46 +03:00
const struct smb2_lease_key * key ;
} ;
2019-05-24 17:50:30 +03:00
static struct files_struct * fsps_lease_update_fn (
struct files_struct * fsp , void * private_data )
2014-10-29 01:31:46 +03:00
{
2019-05-24 17:50:30 +03:00
struct fsps_lease_update_state * state =
( struct fsps_lease_update_state * ) private_data ;
2014-10-29 01:31:46 +03:00
if ( fsp - > oplock_type ! = LEASE_OPLOCK ) {
return NULL ;
}
if ( ! smb2_lease_key_equal ( & fsp - > lease - > lease . lease_key , state - > key ) ) {
return NULL ;
}
2019-05-24 17:50:30 +03:00
if ( ! file_id_equal ( & fsp - > file_id , state - > id ) ) {
2014-10-29 01:31:46 +03:00
return NULL ;
}
2019-05-24 16:58:09 +03:00
fsp_lease_update ( fsp ) ;
2014-10-29 01:31:46 +03:00
return NULL ;
}
2019-05-24 17:50:30 +03:00
static void fsps_lease_update ( struct smbd_server_connection * sconn ,
const struct file_id * id ,
const struct smb2_lease_key * key )
{
struct fsps_lease_update_state state = { . id = id , . key = key } ;
files_forall ( sconn , fsps_lease_update_fn , & state ) ;
}
2020-06-02 17:33:23 +03:00
NTSTATUS downgrade_lease ( struct smbXsrv_client * client ,
2018-09-14 17:41:25 +03:00
uint32_t num_file_ids ,
const struct file_id * ids ,
const struct smb2_lease_key * key ,
uint32_t lease_state )
2018-09-14 14:18:50 +03:00
{
2020-07-08 11:15:56 +03:00
struct smbd_server_connection * sconn = client - > sconn ;
2018-09-19 13:05:57 +03:00
const struct GUID * client_guid = NULL ;
2018-09-14 17:41:25 +03:00
struct share_mode_lock * lck ;
const struct file_id id = ids [ 0 ] ;
2018-09-19 13:05:57 +03:00
uint32_t current_state , breaking_to_requested , breaking_to_required ;
bool breaking ;
uint16_t lease_version , epoch ;
2018-09-14 17:41:25 +03:00
NTSTATUS status ;
2018-09-19 13:05:57 +03:00
uint32_t i ;
2019-11-04 09:39:48 +03:00
struct file_id_buf idbuf ;
2018-09-14 17:41:25 +03:00
2019-11-04 09:39:48 +03:00
DBG_DEBUG ( " Downgrading %s to % " PRIu32 " \n " ,
file_id_str_buf ( id , & idbuf ) ,
lease_state ) ;
2018-09-14 14:18:50 +03:00
2018-09-14 17:41:25 +03:00
lck = get_existing_share_mode_lock ( talloc_tos ( ) , id ) ;
if ( lck = = NULL ) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
2018-09-14 14:18:50 +03:00
2020-07-08 11:15:56 +03:00
client_guid = & sconn - > client - > global - > client_guid ;
2018-09-19 13:05:57 +03:00
status = leases_db_get ( client_guid ,
key ,
& id ,
& current_state ,
& breaking ,
& breaking_to_requested ,
& breaking_to_required ,
& lease_version ,
& epoch ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_WARNING ( " leases_db_get returned %s \n " ,
nt_errstr ( status ) ) ;
TALLOC_FREE ( lck ) ;
return status ;
2018-09-14 14:18:50 +03:00
}
2018-09-19 13:05:57 +03:00
if ( ! breaking ) {
2018-09-14 14:18:50 +03:00
DBG_WARNING ( " Attempt to break from % " PRIu32 " to % " PRIu32 " - "
" but we're not in breaking state \n " ,
2018-09-19 13:05:57 +03:00
current_state , lease_state ) ;
2018-09-14 17:41:25 +03:00
TALLOC_FREE ( lck ) ;
2018-09-14 14:18:50 +03:00
return NT_STATUS_UNSUCCESSFUL ;
}
/*
2018-09-19 13:05:57 +03:00
* Can ' t upgrade anything : breaking_to_requested ( and current_state )
2018-09-14 14:18:50 +03:00
* must be a strict bitwise superset of new_lease_state
*/
2018-09-19 13:05:57 +03:00
if ( ( lease_state & breaking_to_requested ) ! = lease_state ) {
2018-09-14 14:18:50 +03:00
DBG_WARNING ( " Attempt to upgrade from % " PRIu32 " to % " PRIu32 " "
" - expected % " PRIu32 " \n " ,
2018-09-19 13:05:57 +03:00
current_state , lease_state ,
breaking_to_requested ) ;
2018-09-14 17:41:25 +03:00
TALLOC_FREE ( lck ) ;
2018-09-14 14:18:50 +03:00
return NT_STATUS_REQUEST_NOT_ACCEPTED ;
}
2018-09-19 13:05:57 +03:00
if ( current_state ! = lease_state ) {
current_state = lease_state ;
2018-09-14 14:18:50 +03:00
}
2018-09-14 17:41:25 +03:00
status = NT_STATUS_OK ;
2018-09-19 13:05:57 +03:00
if ( ( lease_state & ~ breaking_to_required ) ! = 0 ) {
2018-09-16 12:25:14 +03:00
struct downgrade_lease_additional_state * state ;
2018-09-14 17:41:25 +03:00
2018-09-14 14:18:50 +03:00
DBG_INFO ( " lease state % " PRIu32 " not fully broken from "
" % " PRIu32 " to % " PRIu32 " \n " ,
2018-09-14 17:41:25 +03:00
lease_state ,
2018-09-19 13:05:57 +03:00
current_state ,
breaking_to_required ) ;
2018-09-14 17:41:25 +03:00
2018-09-19 13:05:57 +03:00
breaking_to_requested = breaking_to_required ;
2018-09-14 17:41:25 +03:00
2018-09-19 13:05:57 +03:00
if ( current_state & ( SMB2_LEASE_WRITE | SMB2_LEASE_HANDLE ) ) {
2018-09-14 14:18:50 +03:00
/*
* Here we break in steps , as windows does
* see the breaking3 and v2_breaking3 tests .
*/
2018-09-19 13:05:57 +03:00
breaking_to_requested | = SMB2_LEASE_READ ;
2018-09-14 14:18:50 +03:00
}
2020-06-02 17:33:23 +03:00
state = talloc_zero ( client ,
2014-10-29 01:31:46 +03:00
struct downgrade_lease_additional_state ) ;
if ( state = = NULL ) {
TALLOC_FREE ( lck ) ;
return NT_STATUS_NO_MEMORY ;
}
state - > im = tevent_create_immediate ( state ) ;
if ( state - > im = = NULL ) {
TALLOC_FREE ( state ) ;
TALLOC_FREE ( lck ) ;
return NT_STATUS_NO_MEMORY ;
}
2020-06-02 17:33:23 +03:00
state - > client = client ;
2018-09-19 13:05:57 +03:00
state - > lease_key = * key ;
state - > break_from = current_state ;
state - > break_to = breaking_to_requested ;
if ( lease_version > 1 ) {
state - > new_epoch = epoch ;
2014-10-29 01:31:46 +03:00
}
2018-09-19 13:05:57 +03:00
if ( current_state & ( SMB2_LEASE_WRITE | SMB2_LEASE_HANDLE ) ) {
2018-09-14 17:03:57 +03:00
state - > break_flags =
SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED ;
} else {
2014-10-29 01:31:46 +03:00
/*
* This is an async break without
* SMB2_NOTIFY_BREAK_LEASE_FLAG_ACK_REQUIRED
*
* we need to store NONE state in the
* database .
*/
2018-09-19 13:05:57 +03:00
current_state = 0 ;
breaking_to_requested = 0 ;
breaking_to_required = 0 ;
breaking = false ;
2019-04-08 17:21:41 +03:00
{
NTSTATUS set_status ;
set_status = leases_db_set (
2020-07-08 11:15:56 +03:00
& sconn - > client - > global - > client_guid ,
2019-04-08 17:21:41 +03:00
key ,
2018-09-19 13:05:57 +03:00
current_state ,
breaking ,
breaking_to_requested ,
breaking_to_required ,
lease_version ,
epoch ) ;
2019-04-08 17:21:41 +03:00
if ( ! NT_STATUS_IS_OK ( set_status ) ) {
DBG_DEBUG ( " leases_db_set failed: %s \n " ,
nt_errstr ( set_status ) ) ;
return set_status ;
}
}
2014-10-29 01:31:46 +03:00
}
2018-03-22 12:54:41 +03:00
tevent_schedule_immediate ( state - > im ,
2020-07-08 11:15:56 +03:00
client - > raw_ev_ctx ,
2014-10-29 01:31:46 +03:00
downgrade_lease_additional_trigger ,
state ) ;
2018-09-16 12:25:14 +03:00
status = NT_STATUS_OPLOCK_BREAK_IN_PROGRESS ;
2018-09-14 17:41:25 +03:00
} else {
DBG_DEBUG ( " breaking from % " PRIu32 " to % " PRIu32 " - "
" expected % " PRIu32 " \n " ,
2018-09-19 13:05:57 +03:00
current_state ,
2018-09-14 17:41:25 +03:00
lease_state ,
2018-09-19 13:05:57 +03:00
breaking_to_requested ) ;
2018-09-14 17:41:25 +03:00
2018-09-19 13:05:57 +03:00
breaking_to_requested = 0 ;
breaking_to_required = 0 ;
breaking = false ;
2014-10-29 01:31:46 +03:00
}
2019-04-08 17:21:41 +03:00
{
NTSTATUS set_status ;
set_status = leases_db_set (
2018-09-19 13:05:57 +03:00
client_guid ,
2019-04-08 17:21:41 +03:00
key ,
2018-09-19 13:05:57 +03:00
current_state ,
breaking ,
breaking_to_requested ,
breaking_to_required ,
lease_version ,
epoch ) ;
2019-04-08 17:21:41 +03:00
if ( ! NT_STATUS_IS_OK ( set_status ) ) {
DBG_DEBUG ( " leases_db_set failed: %s \n " ,
nt_errstr ( set_status ) ) ;
2018-09-19 13:05:57 +03:00
TALLOC_FREE ( lck ) ;
2019-04-08 17:21:41 +03:00
return set_status ;
}
}
2019-11-04 09:39:48 +03:00
DBG_DEBUG ( " Downgrading %s to % " PRIu32 " => %s \n " ,
file_id_str_buf ( id , & idbuf ) ,
lease_state ,
nt_errstr ( status ) ) ;
2018-09-16 12:25:14 +03:00
2019-12-13 16:40:03 +03:00
share_mode_wakeup_waiters ( id ) ;
2018-09-19 18:42:51 +03:00
2019-05-24 17:50:30 +03:00
fsps_lease_update ( sconn , & id , key ) ;
2014-10-29 01:31:46 +03:00
TALLOC_FREE ( lck ) ;
2019-11-04 09:39:48 +03:00
DBG_DEBUG ( " Downgrading %s to % " PRIu32 " => %s \n " ,
file_id_str_buf ( id , & idbuf ) ,
lease_state ,
nt_errstr ( status ) ) ;
2014-10-29 01:31:46 +03:00
/*
* Dynamic share case . Ensure other opens are copies .
* This will only be breaking to NONE .
*/
for ( i = 1 ; i < num_file_ids ; i + + ) {
lck = get_existing_share_mode_lock ( talloc_tos ( ) , ids [ i ] ) ;
if ( lck = = NULL ) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
2019-05-24 17:50:30 +03:00
fsps_lease_update ( sconn , & ids [ i ] , key ) ;
2014-10-29 01:31:46 +03:00
2019-11-04 09:39:48 +03:00
DBG_DEBUG ( " Downgrading %s to % " PRIu32 " => %s \n " ,
file_id_str_buf ( ids [ i ] , & idbuf ) ,
lease_state ,
nt_errstr ( status ) ) ;
2014-10-29 01:31:46 +03:00
TALLOC_FREE ( lck ) ;
}
return status ;
}
2013-08-19 16:28:24 +04:00
# define SMB1_BREAK_MESSAGE_LENGTH (smb_size + 8*2)
2001-10-21 01:59:34 +04:00
1999-12-13 16:27:58 +03:00
/****************************************************************************
Function to do the waiting before sending a local break .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-07-17 13:56:25 +04:00
static void wait_before_sending_break ( void )
1999-12-13 16:27:58 +03:00
{
2006-04-15 08:07:10 +04:00
long wait_time = ( long ) lp_oplock_break_wait_time ( ) ;
1999-12-13 16:27:58 +03:00
2006-04-15 08:07:10 +04:00
if ( wait_time ) {
smb_msleep ( wait_time ) ;
2001-10-21 01:59:34 +04:00
}
1999-12-13 16:27:58 +03:00
}
/****************************************************************************
Ensure that we have a valid oplock .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-10-21 01:59:34 +04:00
2010-10-03 19:18:26 +04:00
static files_struct * initial_break_processing (
struct smbd_server_connection * sconn , struct file_id id ,
unsigned long file_id )
1998-08-17 17:11:34 +04:00
{
2001-10-21 01:59:34 +04:00
files_struct * fsp = NULL ;
2019-11-04 09:39:48 +03:00
struct file_id_buf idbuf ;
2001-10-21 01:59:34 +04:00
2019-11-04 09:39:48 +03:00
DBG_NOTICE ( " called for %s/%u \n "
" Current oplocks_open (exclusive = %d, levelII = %d) \n " ,
file_id_str_buf ( id , & idbuf ) ,
( int ) file_id ,
sconn - > oplocks . exclusive_open ,
sconn - > oplocks . level_II_open ) ;
2001-10-21 01:59:34 +04:00
/*
* We need to search the file open table for the
* entry containing this dev and inode , and ensure
* we have an oplock on it .
*/
2010-10-03 19:18:26 +04:00
fsp = file_find_dif ( sconn , id , file_id ) ;
2001-10-21 01:59:34 +04:00
if ( fsp = = NULL ) {
/* The file could have been closed in the meantime - return success. */
2019-11-04 09:39:48 +03:00
DBG_NOTICE ( " cannot find open file "
" with file_id %s gen_id = %lu, allowing break to "
" succeed. \n " ,
file_id_str_buf ( id , & idbuf ) ,
file_id ) ;
2001-10-21 01:59:34 +04:00
return NULL ;
}
/* Ensure we have an oplock on the file */
/*
* There is a potential race condition in that an oplock could
* have been broken due to another udp request , and yet there are
* still oplock break messages being sent in the udp message
* queue for this file . So return true if we don ' t have an oplock ,
* as we may have just freed it .
*/
if ( fsp - > oplock_type = = NO_OPLOCK ) {
2019-09-16 13:50:02 +03:00
DBG_NOTICE ( " file %s (file_id = %s gen_id = % " PRIu64 " ) "
" has no oplock. "
" Allowing break to succeed regardless. \n " ,
fsp_str_dbg ( fsp ) ,
2019-11-04 09:39:48 +03:00
file_id_str_buf ( id , & idbuf ) ,
2020-09-28 11:35:32 +03:00
fh_get_gen_id ( fsp - > fh ) ) ;
2001-10-21 01:59:34 +04:00
return NULL ;
}
return fsp ;
1999-12-13 16:27:58 +03:00
}
2013-02-18 12:56:41 +04:00
static void oplock_timeout_handler ( struct tevent_context * ctx ,
2013-02-18 13:18:29 +04:00
struct tevent_timer * te ,
2009-01-05 12:22:50 +03:00
struct timeval now ,
2005-09-30 21:13:37 +04:00
void * private_data )
1999-12-13 16:27:58 +03:00
{
2006-08-08 13:56:38 +04:00
files_struct * fsp = ( files_struct * ) private_data ;
2004-06-12 03:54:52 +04:00
2013-09-04 16:20:00 +04:00
SMB_ASSERT ( fsp - > sent_oplock_break ! = NO_BREAK_SENT ) ;
2007-06-22 21:19:08 +04:00
/* Remove the timed event handler. */
TALLOC_FREE ( fsp - > oplock_timeout ) ;
2009-07-11 01:50:37 +04:00
DEBUG ( 0 , ( " Oplock break failed for file %s -- replying anyway \n " ,
fsp_str_dbg ( fsp ) ) ) ;
2005-09-30 21:13:37 +04:00
remove_oplock ( fsp ) ;
1999-12-13 16:27:58 +03:00
}
2006-01-28 02:30:30 +03:00
/*******************************************************************
Add a timeout handler waiting for the client reply .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void add_oplock_timeout_handler ( files_struct * fsp )
{
if ( fsp - > oplock_timeout ! = NULL ) {
DEBUG ( 0 , ( " Logic problem -- have an oplock event hanging "
" around \n " ) ) ;
}
fsp - > oplock_timeout =
2018-12-27 17:23:52 +03:00
tevent_add_timer ( fsp - > conn - > sconn - > ev_ctx , fsp ,
2011-12-12 16:51:27 +04:00
timeval_current_ofs ( OPLOCK_BREAK_TIMEOUT , 0 ) ,
oplock_timeout_handler , fsp ) ;
2006-01-28 02:30:30 +03:00
if ( fsp - > oplock_timeout = = NULL ) {
DEBUG ( 0 , ( " Could not add oplock timeout handler \n " ) ) ;
}
}
/*******************************************************************
This handles the generic oplock break message from another smbd .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-05-15 00:31:28 +04:00
static void process_oplock_break_message ( struct messaging_context * msg_ctx ,
void * private_data ,
uint32_t msg_type ,
struct server_id src ,
DATA_BLOB * data )
1999-12-13 16:27:58 +03:00
{
2024-04-29 12:38:18 +03:00
struct oplock_break_message msg ;
2019-05-16 16:38:26 +03:00
enum ndr_err_code ndr_err ;
2005-09-30 21:13:37 +04:00
files_struct * fsp ;
2012-03-30 18:11:08 +04:00
bool use_kernel ;
2011-12-13 16:18:47 +04:00
struct smbd_server_connection * sconn =
2011-12-14 12:50:24 +04:00
talloc_get_type_abort ( private_data ,
2011-12-13 16:18:47 +04:00
struct smbd_server_connection ) ;
2012-06-02 15:39:33 +04:00
struct server_id self = messaging_server_id ( sconn - > msg_ctx ) ;
2012-05-25 01:33:32 +04:00
struct kernel_oplocks * koplocks = sconn - > oplocks . kernel_ops ;
2014-10-29 01:31:46 +03:00
uint16_t break_from ;
2013-09-27 03:15:31 +04:00
uint16_t break_to ;
2014-10-29 01:31:46 +03:00
bool break_needed = true ;
2001-10-21 01:59:34 +04:00
2022-09-03 03:46:34 +03:00
smb_vfs_assert_allowed ( ) ;
2024-04-29 12:38:18 +03:00
ndr_err = ndr_pull_struct_blob_all_noalloc (
data , & msg , ( ndr_pull_flags_fn_t ) ndr_pull_oplock_break_message ) ;
2019-05-16 16:38:26 +03:00
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DBG_DEBUG ( " ndr_pull_oplock_break_message failed: %s \n " ,
ndr_errstr ( ndr_err ) ) ;
2005-09-30 21:13:37 +04:00
return ;
2001-10-21 01:59:34 +04:00
}
2019-05-16 16:38:26 +03:00
if ( DEBUGLEVEL > = 10 ) {
struct server_id_buf buf ;
DBG_DEBUG ( " Got break message from %s \n " ,
server_id_str_buf ( src , & buf ) ) ;
2024-04-29 12:38:18 +03:00
NDR_PRINT_DEBUG ( oplock_break_message , & msg ) ;
2019-05-16 16:38:26 +03:00
}
2001-10-21 01:59:34 +04:00
2024-04-29 12:38:18 +03:00
break_to = msg . break_to ;
fsp = initial_break_processing ( sconn , msg . id , msg . share_file_id ) ;
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
if ( fsp = = NULL ) {
2010-01-19 18:51:33 +03:00
/* We hit a race here. Break messages are sent, and before we
2013-04-12 16:03:08 +04:00
* get to process this message , we have closed the file . */
2005-09-30 21:13:37 +04:00
DEBUG ( 3 , ( " Did not find fsp \n " ) ) ;
return ;
2001-10-21 01:59:34 +04:00
}
2014-10-29 01:31:46 +03:00
break_from = fsp_lease_type ( fsp ) ;
if ( fsp - > oplock_type ! = LEASE_OPLOCK ) {
if ( fsp - > sent_oplock_break ! = NO_BREAK_SENT ) {
/*
* Nothing to do anymore
*/
DEBUG ( 10 , ( " fsp->sent_oplock_break = %d \n " ,
fsp - > sent_oplock_break ) ) ;
return ;
}
}
if ( ! ( global_client_caps & CAP_LEVEL_II_OPLOCKS ) ) {
DEBUG ( 10 , ( " client_caps without level2 oplocks \n " ) ) ;
break_to & = ~ SMB2_LEASE_READ ;
}
2018-01-03 20:52:33 +03:00
use_kernel = lp_kernel_oplocks ( SNUM ( fsp - > conn ) ) & &
( koplocks ! = NULL ) ;
2018-08-24 08:12:23 +03:00
if ( use_kernel ) {
2014-10-29 01:31:46 +03:00
DEBUG ( 10 , ( " Kernel oplocks don't allow level2 \n " ) ) ;
break_to & = ~ SMB2_LEASE_READ ;
}
if ( ! lp_level2_oplocks ( SNUM ( fsp - > conn ) ) ) {
DEBUG ( 10 , ( " no level2 oplocks by config \n " ) ) ;
break_to & = ~ SMB2_LEASE_READ ;
}
if ( fsp - > oplock_type = = LEASE_OPLOCK ) {
2018-09-18 16:18:15 +03:00
const struct GUID * client_guid = fsp_client_guid ( fsp ) ;
2014-10-29 01:31:46 +03:00
struct share_mode_lock * lck ;
2018-09-18 16:18:15 +03:00
uint32_t current_state ;
uint32_t breaking_to_requested , breaking_to_required ;
bool breaking ;
uint16_t lease_version , epoch ;
NTSTATUS status ;
2014-10-29 01:31:46 +03:00
lck = get_existing_share_mode_lock (
talloc_tos ( ) , fsp - > file_id ) ;
if ( lck = = NULL ) {
/*
* We hit a race here . Break messages are sent , and
* before we get to process this message , we have closed
* the file .
*/
DEBUG ( 3 , ( " Did not find share_mode \n " ) ) ;
return ;
}
2018-09-18 16:18:15 +03:00
status = leases_db_get ( client_guid ,
& fsp - > lease - > lease . lease_key ,
2019-05-16 16:38:26 +03:00
& fsp - > file_id ,
2018-09-18 16:18:15 +03:00
& current_state ,
& breaking ,
& breaking_to_requested ,
& breaking_to_required ,
& lease_version ,
& epoch ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_WARNING ( " leases_db_get returned %s \n " ,
nt_errstr ( status ) ) ;
TALLOC_FREE ( lck ) ;
return ;
}
2019-04-08 17:21:41 +03:00
2018-09-18 16:18:15 +03:00
break_from = current_state ;
break_to & = current_state ;
if ( breaking ) {
break_to & = breaking_to_required ;
if ( breaking_to_required ! = break_to ) {
/*
* Note we don ' t increment the epoch
* here , which might be a bug in
* Windows too . . .
*/
breaking_to_required = break_to ;
}
break_needed = false ;
} else if ( current_state = = break_to ) {
break_needed = false ;
} else if ( current_state = = SMB2_LEASE_READ ) {
current_state = SMB2_LEASE_NONE ;
/* Need to increment the epoch */
epoch + = 1 ;
} else {
breaking = true ;
breaking_to_required = break_to ;
breaking_to_requested = break_to ;
/* Need to increment the epoch */
epoch + = 1 ;
}
2019-04-08 17:21:41 +03:00
2018-09-18 16:18:15 +03:00
{
NTSTATUS set_status ;
set_status = leases_db_set (
client_guid ,
& fsp - > lease - > lease . lease_key ,
current_state ,
breaking ,
breaking_to_requested ,
breaking_to_required ,
lease_version ,
epoch ) ;
if ( ! NT_STATUS_IS_OK ( set_status ) ) {
DBG_DEBUG ( " leases_db_set failed: %s \n " ,
nt_errstr ( set_status ) ) ;
return ;
2019-04-08 17:21:41 +03:00
}
2018-09-18 16:18:15 +03:00
}
2019-04-08 17:21:41 +03:00
2018-09-18 16:18:15 +03:00
/* Ensure we're in sync with current lease state. */
2019-05-24 16:58:09 +03:00
fsp_lease_update ( fsp ) ;
2018-09-18 16:18:15 +03:00
2014-10-29 01:31:46 +03:00
TALLOC_FREE ( lck ) ;
}
if ( ! break_needed ) {
DEBUG ( 10 , ( " %s: skip break \n " , __func__ ) ) ;
2005-09-30 21:13:37 +04:00
return ;
2001-10-21 01:59:34 +04:00
}
2019-11-19 16:23:52 +03:00
if ( break_from = = SMB2_LEASE_NONE ) {
2019-11-04 09:39:48 +03:00
struct file_id_buf idbuf ;
DBG_NOTICE ( " Already downgraded oplock to none on %s: %s \n " ,
file_id_str_buf ( fsp - > file_id , & idbuf ) ,
fsp_str_dbg ( fsp ) ) ;
2005-09-30 21:13:37 +04:00
return ;
2005-07-17 13:56:25 +04:00
}
2001-10-21 01:59:34 +04:00
2014-10-29 01:31:46 +03:00
DEBUG ( 10 , ( " break_from=%u, break_to=%u \n " ,
( unsigned ) break_from , ( unsigned ) break_to ) ) ;
2012-03-30 18:11:08 +04:00
2019-11-19 16:23:52 +03:00
if ( break_from = = break_to ) {
2019-11-04 09:39:48 +03:00
struct file_id_buf idbuf ;
DBG_NOTICE ( " Already downgraded oplock to %u on %s: %s \n " ,
( unsigned ) break_to ,
file_id_str_buf ( fsp - > file_id , & idbuf ) ,
fsp_str_dbg ( fsp ) ) ;
2014-10-29 01:31:46 +03:00
return ;
2005-09-30 21:13:37 +04:00
}
2001-10-21 01:59:34 +04:00
2010-04-08 06:00:44 +04:00
/* Need to wait before sending a break
message if we sent ourselves this message . */
2019-10-09 22:38:04 +03:00
if ( server_id_equal ( & self , & src ) ) {
2005-09-30 21:13:37 +04:00
wait_before_sending_break ( ) ;
}
2001-10-21 01:59:34 +04:00
2022-03-15 23:38:03 +03:00
# if defined(WITH_SMB1SERVER)
2024-02-13 15:05:42 +03:00
if ( conn_using_smb2 ( sconn ) ) {
2022-03-15 23:38:03 +03:00
# endif
2014-10-29 01:31:46 +03:00
send_break_message_smb2 ( fsp , break_from , break_to ) ;
2022-03-15 23:38:03 +03:00
# if defined(WITH_SMB1SERVER)
2010-04-08 06:00:44 +04:00
} else {
2014-10-29 01:31:46 +03:00
send_break_message_smb1 ( fsp , ( break_to & SMB2_LEASE_READ ) ?
OPLOCKLEVEL_II : OPLOCKLEVEL_NONE ) ;
2003-07-17 01:06:21 +04:00
}
2022-03-15 23:38:03 +03:00
# endif
2003-07-17 04:58:14 +04:00
2014-10-29 01:31:46 +03:00
if ( ( break_from = = SMB2_LEASE_READ ) & &
( break_to = = SMB2_LEASE_NONE ) ) {
2013-10-22 15:33:42 +04:00
/*
* This is an async break without a reply and thus no timeout
2014-10-29 01:31:46 +03:00
*
* leases are handled above .
2013-10-22 15:33:42 +04:00
*/
2014-10-29 01:31:46 +03:00
if ( fsp - > oplock_type ! = LEASE_OPLOCK ) {
remove_oplock ( fsp ) ;
}
return ;
}
if ( fsp - > oplock_type = = LEASE_OPLOCK ) {
2013-10-22 15:33:42 +04:00
return ;
}
2014-10-29 01:31:46 +03:00
fsp - > sent_oplock_break = ( break_to & SMB2_LEASE_READ ) ?
LEVEL_II_BREAK_SENT : BREAK_TO_NONE_SENT ;
2006-01-28 02:30:30 +03:00
add_oplock_timeout_handler ( fsp ) ;
2005-09-30 21:13:37 +04:00
}
2001-10-21 01:59:34 +04:00
2006-01-28 02:30:30 +03:00
/*******************************************************************
This handles the kernel oplock break message .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2007-05-15 00:31:28 +04:00
static void process_kernel_oplock_break ( struct messaging_context * msg_ctx ,
void * private_data ,
uint32_t msg_type ,
struct server_id src ,
DATA_BLOB * data )
2005-09-30 21:13:37 +04:00
{
2024-04-30 12:41:50 +03:00
struct oplock_break_message msg ;
enum ndr_err_code ndr_err ;
2019-11-04 09:39:48 +03:00
struct file_id_buf idbuf ;
2005-09-30 21:13:37 +04:00
files_struct * fsp ;
2011-12-13 16:18:47 +04:00
struct smbd_server_connection * sconn =
2011-12-14 12:50:24 +04:00
talloc_get_type_abort ( private_data ,
2011-12-13 16:18:47 +04:00
struct smbd_server_connection ) ;
2015-04-28 14:30:58 +03:00
struct server_id_buf tmp ;
2011-12-13 16:18:47 +04:00
2024-04-30 12:41:50 +03:00
ndr_err = ndr_pull_struct_blob_all_noalloc (
data ,
& msg ,
( ndr_pull_flags_fn_t ) ndr_pull_oplock_break_message ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DBG_DEBUG ( " ndr_pull_oplock_break_message failed: %s \n " ,
ndr_errstr ( ndr_err ) ) ;
2005-09-30 21:13:37 +04:00
return ;
2001-10-21 01:59:34 +04:00
}
2019-11-04 09:39:48 +03:00
DBG_DEBUG ( " Got kernel oplock break message from pid %s: %s/%u \n " ,
server_id_str_buf ( src , & tmp ) ,
2024-04-30 12:41:50 +03:00
file_id_str_buf ( msg . id , & idbuf ) ,
( unsigned int ) msg . share_file_id ) ;
2006-01-13 01:17:54 +03:00
2024-04-30 12:41:50 +03:00
fsp = initial_break_processing ( sconn , msg . id , msg . share_file_id ) ;
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
if ( fsp = = NULL ) {
DEBUG ( 3 , ( " Got a kernel oplock break message for a file "
" I don't know about \n " ) ) ;
return ;
2001-10-21 01:59:34 +04:00
}
2005-09-30 21:13:37 +04:00
if ( fsp - > sent_oplock_break ! = NO_BREAK_SENT ) {
/* This is ok, kernel oplocks come in completely async */
DEBUG ( 3 , ( " Got a kernel oplock request while waiting for a "
" break reply \n " ) ) ;
return ;
2001-10-21 01:59:34 +04:00
}
2022-03-15 23:38:03 +03:00
# if defined(WITH_SMB1SERVER)
2024-02-13 15:05:42 +03:00
if ( conn_using_smb2 ( sconn ) ) {
2022-03-15 23:38:03 +03:00
# endif
2014-10-29 01:31:46 +03:00
send_break_message_smb2 ( fsp , 0 , OPLOCKLEVEL_NONE ) ;
2022-03-15 23:38:03 +03:00
# if defined(WITH_SMB1SERVER)
2010-04-08 06:00:44 +04:00
} else {
2010-04-10 06:26:34 +04:00
send_break_message_smb1 ( fsp , OPLOCKLEVEL_NONE ) ;
2001-10-21 01:59:34 +04:00
}
2022-03-15 23:38:03 +03:00
# endif
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
fsp - > sent_oplock_break = BREAK_TO_NONE_SENT ;
2006-01-28 02:30:30 +03:00
add_oplock_timeout_handler ( fsp ) ;
2005-09-30 21:13:37 +04:00
}
1998-08-17 17:11:34 +04:00
2014-10-29 01:31:46 +03:00
static void send_break_to_none ( struct messaging_context * msg_ctx ,
2018-02-07 14:28:13 +03:00
const struct file_id * id ,
2014-10-29 01:31:46 +03:00
const struct share_mode_entry * e )
{
2019-05-16 16:34:37 +03:00
NTSTATUS status ;
status = send_break_message ( msg_ctx , id , e , OPLOCK_NONE ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " send_break_message failed: %s \n " ,
nt_errstr ( status ) ) ;
}
2014-10-29 01:31:46 +03:00
}
2019-06-30 10:50:46 +03:00
struct break_to_none_state {
struct smbd_server_connection * sconn ;
struct file_id id ;
struct smb2_lease_key lease_key ;
struct GUID client_guid ;
2022-08-15 23:45:17 +03:00
size_t num_read_leases ;
2022-08-15 18:42:33 +03:00
uint32_t total_lease_types ;
2019-06-30 10:50:46 +03:00
} ;
2014-10-29 01:31:46 +03:00
2019-09-10 22:21:01 +03:00
static bool do_break_lease_to_none ( struct share_mode_entry * e ,
2018-09-17 18:01:38 +03:00
void * private_data )
{
2019-06-30 10:50:46 +03:00
struct break_to_none_state * state = private_data ;
2018-09-17 18:01:38 +03:00
uint32_t current_state = 0 ;
bool our_own ;
NTSTATUS status ;
DBG_DEBUG ( " lease_key=% " PRIu64 " /% " PRIu64 " \n " ,
e - > lease_key . data [ 0 ] ,
e - > lease_key . data [ 1 ] ) ;
status = leases_db_get ( & e - > client_guid ,
& e - > lease_key ,
& state - > id ,
& current_state ,
NULL , /* breaking */
NULL , /* breaking_to_requested */
NULL , /* breaking_to_required */
NULL , /* lease_version */
NULL ) ; /* epoch */
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_WARNING ( " leases_db_get failed: %s \n " ,
nt_errstr ( status ) ) ;
return false ;
}
2022-08-15 18:42:33 +03:00
state - > total_lease_types | = current_state ;
2018-09-17 18:01:38 +03:00
if ( ( current_state & SMB2_LEASE_READ ) = = 0 ) {
return false ;
}
2022-08-15 23:45:17 +03:00
state - > num_read_leases + = 1 ;
2018-09-17 18:01:38 +03:00
our_own = smb2_lease_equal ( & state - > client_guid ,
& state - > lease_key ,
& e - > client_guid ,
& e - > lease_key ) ;
if ( our_own ) {
DEBUG ( 10 , ( " Don't break our own lease \n " ) ) ;
return false ;
}
DBG_DEBUG ( " Breaking % " PRIu64 " /% " PRIu64 " to none \n " ,
e - > lease_key . data [ 0 ] ,
e - > lease_key . data [ 1 ] ) ;
send_break_to_none ( state - > sconn - > msg_ctx , & state - > id , e ) ;
return false ;
}
2019-08-13 16:21:59 +03:00
static bool do_break_oplock_to_none ( struct share_mode_entry * e ,
bool * modified ,
void * private_data )
{
struct break_to_none_state * state = private_data ;
if ( e - > op_type = = LEASE_OPLOCK ) {
/*
* Already being taken care of
*/
return false ;
}
/*
* As there could have been multiple writes waiting at the
* lock_share_entry gate we may not be the first to
* enter . Hence the state of the op_types in the share mode
* entries may be partly NO_OPLOCK and partly LEVEL_II
* oplock . It will do no harm to re - send break messages to
* those smbd ' s that are still waiting their turn to remove
* their LEVEL_II state , and also no harm to ignore existing
* NO_OPLOCK states . JRA .
*/
DBG_DEBUG ( " e->op_type == %d \n " , e - > op_type ) ;
2022-08-15 18:42:33 +03:00
state - > total_lease_types | = map_oplock_to_lease_type ( e - > op_type ) ;
2019-08-13 16:21:59 +03:00
if ( e - > op_type = = NO_OPLOCK ) {
return false ;
}
2022-08-15 23:45:17 +03:00
state - > num_read_leases + = 1 ;
2019-08-13 16:21:59 +03:00
/* Paranoia .... */
SMB_ASSERT ( ! EXCLUSIVE_OPLOCK_TYPE ( e - > op_type ) ) ;
send_break_to_none ( state - > sconn - > msg_ctx , & state - > id , e ) ;
return false ;
}
2024-09-04 18:58:45 +03:00
struct dirlease_break_state {
struct smbd_server_connection * sconn ;
struct file_id file_id ;
struct smb2_lease_key parent_lease_key ;
uint32_t total_lease_types ;
} ;
static bool do_dirlease_break_to_none ( struct share_mode_entry * e ,
void * private_data )
{
struct dirlease_break_state * state = private_data ;
uint32_t current_state = 0 ;
NTSTATUS status ;
DBG_DEBUG ( " lease_key=% " PRIu64 " /% " PRIu64 " \n " ,
e - > lease_key . data [ 0 ] ,
e - > lease_key . data [ 1 ] ) ;
status = leases_db_get ( & e - > client_guid ,
& e - > lease_key ,
& state - > file_id ,
& current_state ,
NULL , /* breaking */
NULL , /* breaking_to_requested */
NULL , /* breaking_to_required */
NULL , /* lease_version */
NULL ) ; /* epoch */
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_WARNING ( " leases_db_get failed: %s \n " ,
nt_errstr ( status ) ) ;
return false ;
}
if ( share_entry_stale_pid ( e ) ) {
return false ;
}
state - > total_lease_types | = current_state ;
if ( smb2_lease_key_equal ( & state - > parent_lease_key , & e - > lease_key ) ) {
return false ;
}
if ( ( current_state & ( SMB2_LEASE_READ | SMB2_LEASE_HANDLE ) ) = = 0 ) {
return false ;
}
DBG_DEBUG ( " Breaking % " PRIu64 " /% " PRIu64 " to none \n " ,
e - > lease_key . data [ 0 ] ,
e - > lease_key . data [ 1 ] ) ;
send_break_to_none ( state - > sconn - > msg_ctx , & state - > file_id , e ) ;
return false ;
}
void contend_dirleases ( struct connection_struct * conn ,
const struct smb_filename * smb_fname ,
const struct smb2_lease * lease )
{
struct dirlease_break_state state = {
. sconn = conn - > sconn ,
} ;
struct share_mode_lock * lck = NULL ;
struct smb_filename * parent_fname = NULL ;
uint32_t access_mask , share_mode ;
NTSTATUS status ;
int ret ;
bool ok ;
if ( lease ! = NULL ) {
DBG_DEBUG ( " Parent leasekey % " PRIx64 " /% " PRIx64 " \n " ,
lease - > parent_lease_key . data [ 0 ] ,
lease - > parent_lease_key . data [ 1 ] ) ;
state . parent_lease_key = lease - > parent_lease_key ;
}
status = SMB_VFS_PARENT_PATHNAME ( conn ,
talloc_tos ( ) ,
smb_fname ,
& parent_fname ,
NULL ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " parent_smb_fname() for [%s] failed: %s \n " ,
smb_fname_str_dbg ( smb_fname ) , strerror ( errno ) ) ;
return ;
}
ret = SMB_VFS_STAT ( conn , parent_fname ) ;
if ( ret ! = 0 ) {
DBG_ERR ( " Failed to stat [%s]: %s \n " ,
smb_fname_str_dbg ( parent_fname ) , strerror ( errno ) ) ;
TALLOC_FREE ( parent_fname ) ;
return ;
}
state . file_id = vfs_file_id_from_sbuf ( conn , & parent_fname - > st ) ;
TALLOC_FREE ( parent_fname ) ;
lck = get_existing_share_mode_lock ( talloc_tos ( ) , state . file_id ) ;
if ( lck = = NULL ) {
/*
* No sharemode db entry - > no leases .
*/
return ;
}
ok = share_mode_forall_leases ( lck , do_dirlease_break_to_none , & state ) ;
if ( ! ok ) {
DBG_WARNING ( " share_mode_forall_leases failed \n " ) ;
}
/*
* While we ' re at it , update lease type .
*/
share_mode_flags_get ( lck ,
& access_mask ,
& share_mode ,
NULL ) ;
share_mode_flags_set ( lck ,
access_mask ,
share_mode ,
state . total_lease_types ,
NULL ) ;
TALLOC_FREE ( lck ) ;
}
2019-06-30 10:50:46 +03:00
/****************************************************************************
This function is called on any file modification or lock request . If a file
is level 2 oplocked then it must tell all other level 2 holders to break to
none .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void contend_level2_oplocks_begin_default ( files_struct * fsp ,
enum level2_contention_type type )
2012-01-13 16:26:41 +04:00
{
2019-06-30 10:50:46 +03:00
struct break_to_none_state state = {
. sconn = fsp - > conn - > sconn , . id = fsp - > file_id ,
} ;
struct share_mode_lock * lck = NULL ;
2022-08-18 13:52:54 +03:00
uint32_t fsp_lease = fsp_lease_type ( fsp ) ;
2019-06-30 10:50:46 +03:00
bool ok , has_read_lease ;
2012-01-13 16:26:41 +04:00
2019-06-30 10:50:46 +03:00
/*
* If this file is level II oplocked then we need
* to grab the shared memory lock and inform all
* other files with a level II lock that they need
* to flush their read caches . We keep the lock over
* the shared memory area whilst doing this .
*/
2022-08-18 13:52:54 +03:00
if ( fsp_lease & SMB2_LEASE_WRITE ) {
2019-06-30 10:50:46 +03:00
/*
* There can ' t be any level2 oplocks , we ' re alone .
*/
return ;
}
has_read_lease = file_has_read_lease ( fsp ) ;
if ( ! has_read_lease ) {
DEBUG ( 10 , ( " No read oplocks around \n " ) ) ;
return ;
}
if ( fsp - > oplock_type = = LEASE_OPLOCK ) {
state . client_guid = * fsp_client_guid ( fsp ) ;
state . lease_key = fsp - > lease - > lease . lease_key ;
DEBUG ( 10 , ( " Breaking through lease key % " PRIu64 " /% " PRIu64 " \n " ,
state . lease_key . data [ 0 ] ,
state . lease_key . data [ 1 ] ) ) ;
}
lck = get_existing_share_mode_lock ( talloc_tos ( ) , fsp - > file_id ) ;
2012-01-13 16:26:41 +04:00
if ( lck = = NULL ) {
2019-11-04 09:39:48 +03:00
struct file_id_buf idbuf ;
2019-06-30 10:50:46 +03:00
DBG_WARNING ( " failed to lock share mode entry for file %s. \n " ,
2019-11-04 09:39:48 +03:00
file_id_str_buf ( state . id , & idbuf ) ) ;
2019-06-30 10:50:46 +03:00
return ;
2012-01-13 16:26:41 +04:00
}
2000-11-16 03:59:18 +03:00
2014-10-29 01:31:46 +03:00
/*
* Walk leases and oplocks separately : We have to send one break per
* lease . If we have multiple share_mode_entry having a common lease ,
* we would break the lease twice if we don ' t walk the leases list
* separately .
*/
2019-06-30 10:50:46 +03:00
ok = share_mode_forall_leases ( lck , do_break_lease_to_none , & state ) ;
2018-09-17 18:01:38 +03:00
if ( ! ok ) {
DBG_WARNING ( " share_mode_forall_leases failed \n " ) ;
2014-10-29 01:31:46 +03:00
}
2005-09-30 21:13:37 +04:00
2019-08-13 16:21:59 +03:00
ok = share_mode_forall_entries ( lck , do_break_oplock_to_none , & state ) ;
if ( ! ok ) {
DBG_WARNING ( " share_mode_forall_entries failed \n " ) ;
2004-06-08 20:14:31 +04:00
}
2022-08-15 18:42:33 +03:00
{
2019-06-30 10:50:46 +03:00
/*
2022-08-15 18:42:33 +03:00
* Lazy update here . It might be that all leases
* have gone in the meantime .
2019-06-30 10:50:46 +03:00
*/
2020-11-05 17:39:51 +03:00
uint32_t acc , sh , ls ;
share_mode_flags_get ( lck , & acc , & sh , & ls ) ;
2022-08-15 18:42:33 +03:00
ls = state . total_lease_types ;
2020-11-05 17:39:51 +03:00
share_mode_flags_set ( lck , acc , sh , ls , NULL ) ;
2019-06-30 10:50:46 +03:00
}
2006-01-28 02:30:30 +03:00
2006-02-20 20:59:58 +03:00
TALLOC_FREE ( lck ) ;
2004-06-08 20:14:31 +04:00
}
2011-05-31 07:18:37 +04:00
void smbd_contend_level2_oplocks_begin ( files_struct * fsp ,
2009-02-03 22:56:35 +03:00
enum level2_contention_type type )
{
contend_level2_oplocks_begin_default ( fsp , type ) ;
}
2011-05-31 07:18:37 +04:00
void smbd_contend_level2_oplocks_end ( files_struct * fsp ,
2009-02-03 22:56:35 +03:00
enum level2_contention_type type )
{
2018-07-26 14:08:06 +03:00
return ;
2009-02-03 22:56:35 +03:00
}
2004-06-08 20:14:31 +04:00
/****************************************************************************
Setup oplocks for this process .
1998-09-23 05:48:45 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-10-21 01:59:34 +04:00
2011-12-13 16:13:53 +04:00
bool init_oplocks ( struct smbd_server_connection * sconn )
1998-09-18 21:50:18 +04:00
{
2006-07-19 14:42:50 +04:00
DEBUG ( 3 , ( " init_oplocks: initializing messages. \n " ) ) ;
2000-06-10 17:38:07 +04:00
2011-12-13 16:18:47 +04:00
messaging_register ( sconn - > msg_ctx , sconn , MSG_SMB_BREAK_REQUEST ,
2007-05-15 00:31:28 +04:00
process_oplock_break_message ) ;
2011-12-13 16:18:47 +04:00
messaging_register ( sconn - > msg_ctx , sconn , MSG_SMB_KERNEL_BREAK ,
2007-05-15 00:31:28 +04:00
process_kernel_oplock_break ) ;
2012-03-30 17:51:25 +04:00
return true ;
}
void init_kernel_oplocks ( struct smbd_server_connection * sconn )
{
2012-05-25 01:33:32 +04:00
struct kernel_oplocks * koplocks = sconn - > oplocks . kernel_ops ;
2012-03-30 17:51:25 +04:00
/* only initialize once */
if ( koplocks = = NULL ) {
2018-11-20 17:57:09 +03:00
# ifdef HAVE_KERNEL_OPLOCKS_LINUX
2011-12-13 16:38:41 +04:00
koplocks = linux_init_kernel_oplocks ( sconn ) ;
2000-06-10 17:38:07 +04:00
# endif
2012-05-25 01:33:32 +04:00
sconn - > oplocks . kernel_ops = koplocks ;
2000-06-10 17:38:07 +04:00
}
1998-09-18 21:50:18 +04:00
}
2024-09-16 03:36:00 +03:00
2024-10-17 20:18:57 +03:00
struct pending_hlease_break {
struct pending_hlease_break * prev ;
struct pending_hlease_break * next ;
struct server_id pid ;
struct file_id id ;
uint64_t share_file_id ;
uint16_t break_to ;
} ;
2024-09-16 03:36:00 +03:00
struct delay_for_handle_lease_break_state {
TALLOC_CTX * mem_ctx ;
struct tevent_context * ev ;
struct timeval timeout ;
2024-10-17 20:18:57 +03:00
bool recursive ;
bool recursive_h_leases_break ;
2024-09-16 03:36:00 +03:00
struct files_struct * fsp ;
struct share_mode_lock * lck ;
bool delay ;
2024-10-17 20:18:57 +03:00
struct pending_hlease_break * breaks ;
struct file_id break_id ;
bool found_open ;
uint32_t num_watches ;
2024-09-16 03:36:00 +03:00
} ;
static void delay_for_handle_lease_break_cleanup ( struct tevent_req * req ,
enum tevent_req_state req_state )
{
struct delay_for_handle_lease_break_state * state =
tevent_req_data ( req , struct delay_for_handle_lease_break_state ) ;
if ( req_state = = TEVENT_REQ_DONE ) {
return ;
}
TALLOC_FREE ( state - > lck ) ;
}
static void delay_for_handle_lease_break_check ( struct tevent_req * req ) ;
struct tevent_req * delay_for_handle_lease_break_send (
TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct timeval timeout ,
struct files_struct * fsp ,
2024-10-17 20:18:57 +03:00
bool recursive ,
2024-09-16 03:36:00 +03:00
struct share_mode_lock * * lck )
{
struct tevent_req * req = NULL ;
struct delay_for_handle_lease_break_state * state = NULL ;
req = tevent_req_create (
mem_ctx , & state , struct delay_for_handle_lease_break_state ) ;
if ( req = = NULL ) {
return NULL ;
}
tevent_req_set_cleanup_fn ( req , delay_for_handle_lease_break_cleanup ) ;
* state = ( struct delay_for_handle_lease_break_state ) {
. mem_ctx = mem_ctx ,
. ev = ev ,
. timeout = timeout ,
2024-10-17 20:18:57 +03:00
. recursive = recursive ,
. recursive_h_leases_break = recursive ,
2024-09-16 03:36:00 +03:00
. fsp = fsp ,
. lck = talloc_move ( state , lck ) ,
} ;
delay_for_handle_lease_break_check ( req ) ;
if ( ! tevent_req_is_in_progress ( req ) ) {
return tevent_req_post ( req , ev ) ;
}
/* Ensure we can't be closed in flight. */
if ( ! aio_add_req_to_fsp ( fsp , req ) ) {
tevent_req_nterror ( req , NT_STATUS_NO_MEMORY ) ;
return tevent_req_post ( req , ev ) ;
}
return req ;
}
static bool delay_for_handle_lease_break_fn ( struct share_mode_entry * e ,
void * private_data )
{
struct delay_for_handle_lease_break_state * state = talloc_get_type_abort (
private_data , struct delay_for_handle_lease_break_state ) ;
struct files_struct * fsp = state - > fsp ;
struct server_id_buf buf ;
uint32_t lease_type ;
bool ours , stale ;
if ( fsp - > lease ! = NULL ) {
ours = smb2_lease_equal ( fsp_client_guid ( fsp ) ,
& fsp - > lease - > lease . lease_key ,
& e - > client_guid ,
& e - > lease_key ) ;
if ( ours ) {
return false ;
}
}
lease_type = get_lease_type ( e , fsp - > file_id ) ;
if ( ( lease_type & SMB2_LEASE_HANDLE ) = = 0 ) {
return false ;
}
stale = share_entry_stale_pid ( e ) ;
if ( stale ) {
return false ;
}
state - > delay = true ;
DBG_DEBUG ( " Breaking h-lease on [%s] pid [%s] \n " ,
fsp_str_dbg ( fsp ) ,
server_id_str_buf ( e - > pid , & buf ) ) ;
send_break_message ( fsp - > conn - > sconn - > msg_ctx ,
& fsp - > file_id ,
e ,
lease_type & ~ SMB2_LEASE_HANDLE ) ;
return false ;
}
static void delay_for_handle_lease_break_fsp_done ( struct tevent_req * subreq ) ;
static void delay_for_handle_lease_break_fsp_check ( struct tevent_req * req )
{
struct delay_for_handle_lease_break_state * state = tevent_req_data (
req , struct delay_for_handle_lease_break_state ) ;
struct tevent_req * subreq = NULL ;
bool ok ;
DBG_DEBUG ( " fsp [%s] \n " , fsp_str_dbg ( state - > fsp ) ) ;
ok = share_mode_forall_leases ( state - > lck ,
delay_for_handle_lease_break_fn ,
state ) ;
if ( ! ok ) {
tevent_req_nterror ( req , NT_STATUS_INTERNAL_ERROR ) ;
return ;
}
if ( state - > delay ) {
DBG_DEBUG ( " Delaying fsp [%s] \n " , fsp_str_dbg ( state - > fsp ) ) ;
subreq = share_mode_watch_send ( state ,
state - > ev ,
2024-10-13 12:57:45 +03:00
& state - > fsp - > file_id ,
2024-09-16 03:36:00 +03:00
( struct server_id ) { 0 } ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq ,
delay_for_handle_lease_break_fsp_done ,
req ) ;
if ( ! tevent_req_set_endtime ( subreq , state - > ev , state - > timeout ) ) {
tevent_req_nterror ( req , NT_STATUS_NO_MEMORY ) ;
return ;
}
return ;
}
}
static void delay_for_handle_lease_break_fsp_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct delay_for_handle_lease_break_state * state = tevent_req_data (
req , struct delay_for_handle_lease_break_state ) ;
NTSTATUS status ;
DBG_DEBUG ( " Watch returned for fsp [%s] \n " , fsp_str_dbg ( state - > fsp ) ) ;
status = share_mode_watch_recv ( subreq , NULL , NULL ) ;
TALLOC_FREE ( subreq ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " share_mode_watch_recv returned %s \n " ,
nt_errstr ( status ) ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_IO_TIMEOUT ) ) {
/*
* The sharemode - watch timer fired because a client
* didn ' t respond to the lease break .
*/
status = NT_STATUS_ACCESS_DENIED ;
}
tevent_req_nterror ( req , status ) ;
return ;
}
state - > lck = get_existing_share_mode_lock ( state , state - > fsp - > file_id ) ;
if ( state - > lck = = NULL ) {
tevent_req_nterror ( req , NT_STATUS_UNSUCCESSFUL ) ;
return ;
}
/*
* This could potentially end up looping for some if a client
* aggressively reaquires H - leases on the file , but we have a
* timeout on the tevent req as upper bound .
*/
delay_for_handle_lease_break_check ( req ) ;
}
2024-10-17 20:18:57 +03:00
static int delay_for_handle_lease_break_below_fn ( struct share_mode_data * d ,
struct share_mode_entry * e ,
void * private_data )
{
struct tevent_req * req = talloc_get_type_abort (
private_data , struct tevent_req ) ;
struct delay_for_handle_lease_break_state * state = tevent_req_data (
req , struct delay_for_handle_lease_break_state ) ;
struct pending_hlease_break * b = NULL ;
struct file_id_buf fid_buf ;
const char * fid_bufp = NULL ;
struct server_id_buf sid_buf ;
uint32_t lease = 0 ;
bool stale ;
if ( DEBUGLVL ( DBGLVL_DEBUG ) ) {
fid_bufp = file_id_str_buf ( d - > id , & fid_buf ) ;
}
DBG_DEBUG ( " Breaking [%s] file-id [%s] \n " ,
state - > recursive_h_leases_break ? " yes " : " no " ,
fid_bufp ) ;
stale = share_entry_stale_pid ( e ) ;
if ( stale ) {
return 0 ;
}
if ( state - > recursive_h_leases_break ) {
lease = get_lease_type ( e , d - > id ) ;
}
if ( ( lease & SMB2_LEASE_HANDLE ) = = 0 ) {
if ( e - > flags & SHARE_MODE_FLAG_POSIX_OPEN ) {
DBG_DEBUG ( " POSIX open file-id [%s] \n " , fid_bufp ) ;
/* Ignore POSIX opens. */
return 0 ;
}
state - > found_open = true ;
DBG_DEBUG ( " Unbreakable open [%s] \n " , fid_bufp ) ;
if ( ! state - > recursive_h_leases_break ) {
/* Second round, stop */
DBG_DEBUG ( " Stopping \n " ) ;
return - 1 ;
}
return 0 ;
}
lease & = ~ SMB2_LEASE_HANDLE ;
b = talloc_zero ( state , struct pending_hlease_break ) ;
if ( b = = NULL ) {
DBG_ERR ( " talloc_zero failed \n " ) ;
return - 1 ;
}
b - > id = d - > id ;
b - > break_to = lease ;
b - > pid = e - > pid ;
b - > share_file_id = e - > share_file_id ;
DLIST_ADD_END ( state - > breaks , b ) ;
DBG_DEBUG ( " Queued h-lease break on file-id [%s] pid [%s] \n " ,
fid_bufp ,
server_id_str_buf ( b - > pid , & sid_buf ) ) ;
state - > delay = true ;
return 0 ;
}
static void delay_for_handle_lease_break_below_send_breaks (
struct tevent_req * req ) ;
static void delay_for_handle_lease_break_below_check ( struct tevent_req * req )
{
struct delay_for_handle_lease_break_state * state = tevent_req_data (
req , struct delay_for_handle_lease_break_state ) ;
int ret ;
DBG_DEBUG ( " fsp [%s] \n " , fsp_str_dbg ( state - > fsp ) ) ;
if ( ! state - > recursive ) {
return ;
}
if ( ! S_ISDIR ( state - > fsp - > fsp_name - > st . st_ex_mode ) ) {
return ;
}
if ( ! lp_strict_rename ( SNUM ( state - > fsp - > conn ) ) ) {
/*
* This will also not do h - lease breaks
*/
state - > found_open = file_find_subpath ( state - > fsp ) ;
return ;
}
ret = opens_below_forall ( state - > fsp - > conn ,
state - > fsp - > fsp_name ,
delay_for_handle_lease_break_below_fn ,
req ) ;
if ( ret = = - 1 ) {
tevent_req_nterror ( req , NT_STATUS_INTERNAL_ERROR ) ;
return ;
}
if ( ! state - > delay ) {
DBG_DEBUG ( " No delay for [%s] \n " , fsp_str_dbg ( state - > fsp ) ) ;
return ;
}
/*
* Ignore any opens without h - lease in the first round of listing opens
*/
state - > found_open = false ;
delay_for_handle_lease_break_below_send_breaks ( req ) ;
return ;
}
static void delay_for_handle_lease_break_below_done ( struct tevent_req * subreq ) ;
static void delay_for_handle_lease_break_below_send_breaks (
struct tevent_req * req )
{
struct delay_for_handle_lease_break_state * state = tevent_req_data (
req , struct delay_for_handle_lease_break_state ) ;
struct messaging_context * msg_ctx = state - > fsp - > conn - > sconn - > msg_ctx ;
struct pending_hlease_break * b = NULL ;
struct file_id last_file_id ;
struct tevent_req * subreq = NULL ;
NTSTATUS status ;
DBG_DEBUG ( " Sending breaks \n " ) ;
if ( state - > breaks = = NULL ) {
return ;
}
for ( b = state - > breaks , last_file_id = b - > id ; b ! = NULL ; b = b - > next ) {
struct share_mode_entry e ;
struct file_id_buf fid_buf ;
struct server_id_buf sid_buf ;
if ( ! file_id_equal ( & b - > id , & last_file_id ) ) {
break ;
}
e = ( struct share_mode_entry ) {
. share_file_id = b - > share_file_id ,
. pid = b - > pid ,
} ;
status = send_break_message ( msg_ctx ,
& b - > id ,
& e ,
b - > break_to ) ;
if ( tevent_req_nterror ( req , status ) ) {
DBG_ERR ( " send_break_message failed \n " ) ;
return ;
}
DLIST_REMOVE ( state - > breaks , b ) ;
DBG_DEBUG ( " Sent h-lease break on file-id [%s] pid [%s] \n " ,
file_id_str_buf ( b - > id , & fid_buf ) ,
server_id_str_buf ( b - > pid , & sid_buf ) ) ;
subreq = share_mode_watch_send ( state ,
state - > ev ,
& b - > id ,
( struct server_id ) { 0 } ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
}
tevent_req_set_callback ( subreq ,
delay_for_handle_lease_break_below_done ,
req ) ;
if ( ! tevent_req_set_endtime ( subreq , state - > ev , state - > timeout ) ) {
tevent_req_oom ( req ) ;
return ;
}
state - > num_watches + + ;
}
state - > break_id = last_file_id ;
DBG_DEBUG ( " Stopped sending breaks \n " ) ;
}
static void delay_for_handle_lease_break_below_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct delay_for_handle_lease_break_state * state = tevent_req_data (
req , struct delay_for_handle_lease_break_state ) ;
struct share_mode_lock * lck = NULL ;
struct file_id_buf fid_buf ;
const char * fid_bufp = NULL ;
NTSTATUS status ;
if ( DEBUGLVL ( DBGLVL_DEBUG ) ) {
fid_bufp = file_id_str_buf ( state - > break_id , & fid_buf ) ;
}
DBG_DEBUG ( " Watch finished for file-id [%s] \n " , fid_bufp ) ;
status = share_mode_watch_recv ( subreq , NULL , NULL ) ;
TALLOC_FREE ( subreq ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " share_mode_watch_recv returned %s \n " ,
nt_errstr ( status ) ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_IO_TIMEOUT ) ) {
/*
* The sharemode - watch timer fired because a client
* didn ' t respond to the lease break .
*/
status = NT_STATUS_ACCESS_DENIED ;
}
tevent_req_nterror ( req , status ) ;
return ;
}
state - > num_watches - - ;
if ( state - > num_watches > 0 ) {
return ;
}
/*
* If the client just sends a break ACK , but doesn ' t close the file ,
* Windows server directly returns NT_STATUS_ACCESS_DENIED .
*/
DBG_DEBUG ( " Checking for remaining opens on [%s] \n " , fid_bufp ) ;
lck = fetch_share_mode_unlocked ( state , state - > break_id ) ;
if ( lck ! = NULL ) {
bool has_nonposix_open ;
has_nonposix_open = has_nonposix_opens ( lck ) ;
TALLOC_FREE ( lck ) ;
if ( has_nonposix_open ) {
DBG_DEBUG ( " Found open \n " ) ;
tevent_req_nterror ( req , NT_STATUS_ACCESS_DENIED ) ;
return ;
}
}
if ( state - > breaks ! = NULL ) {
delay_for_handle_lease_break_below_send_breaks ( req ) ;
return ;
}
/*
* We ' ve sent lease breaks recursively once , don ' t do that again . So we
* do a recursive scan a second time to check for new opens and if there
* are any , with or without h - leases , just fail with
* NT_STATUS_ACCESS_DENIED .
*/
state - > recursive_h_leases_break = false ;
state - > lck = get_existing_share_mode_lock ( state , state - > fsp - > file_id ) ;
if ( state - > lck = = NULL ) {
tevent_req_nterror ( req , NT_STATUS_UNSUCCESSFUL ) ;
return ;
}
delay_for_handle_lease_break_check ( req ) ;
}
2024-09-16 03:36:00 +03:00
static void delay_for_handle_lease_break_check ( struct tevent_req * req )
{
struct delay_for_handle_lease_break_state * state = tevent_req_data (
req , struct delay_for_handle_lease_break_state ) ;
state - > delay = false ;
DBG_DEBUG ( " fsp [%s] \n " , fsp_str_dbg ( state - > fsp ) ) ;
delay_for_handle_lease_break_fsp_check ( req ) ;
if ( ! tevent_req_is_in_progress ( req ) ) {
return ;
}
if ( state - > delay ) {
DBG_DEBUG ( " Delaying fsp [%s] \n " , fsp_str_dbg ( state - > fsp ) ) ;
TALLOC_FREE ( state - > lck ) ;
return ;
}
2024-10-17 20:18:57 +03:00
delay_for_handle_lease_break_below_check ( req ) ;
if ( ! tevent_req_is_in_progress ( req ) ) {
return ;
}
if ( state - > found_open ) {
tevent_req_nterror ( req , NT_STATUS_ACCESS_DENIED ) ;
return ;
}
if ( state - > delay ) {
TALLOC_FREE ( state - > lck ) ;
return ;
}
2024-09-16 03:36:00 +03:00
tevent_req_done ( req ) ;
}
NTSTATUS delay_for_handle_lease_break_recv ( struct tevent_req * req ,
TALLOC_CTX * mem_ctx ,
struct share_mode_lock * * lck )
{
NTSTATUS status ;
struct delay_for_handle_lease_break_state * state =
tevent_req_data ( req , struct delay_for_handle_lease_break_state ) ;
if ( tevent_req_is_nterror ( req , & status ) ) {
tevent_req_received ( req ) ;
return status ;
}
* lck = talloc_move ( mem_ctx , & state - > lck ) ;
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
2021-05-19 17:58:21 +03:00
const struct smb2_lease * fsp_get_smb2_lease ( const struct files_struct * fsp )
{
if ( fsp = = NULL ) {
return NULL ;
}
if ( fsp - > lease = = NULL ) {
return NULL ;
}
return & fsp - > lease - > lease ;
}