2012-01-10 17:13:49 +04:00
/*
Unix SMB / CIFS implementation .
Locking functions
Copyright ( C ) Andrew Tridgell 1992 - 2000
Copyright ( C ) Jeremy Allison 1992 - 2006
Copyright ( C ) Volker Lendecke 2005
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
the Free Software Foundation ; either version 3 of the License , or
( at your option ) any later version .
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 .
You should have received a copy of the GNU General Public License
along with this program . If not , see < http : //www.gnu.org/licenses/>.
Revision History :
12 aug 96 : Erik . Devriendt @ te6 . siemens . be
added support for shared memory implementation of share mode locking
May 1997. Jeremy Allison ( jallison @ whistle . com ) . Modified share mode
locking to deal with multiple share modes per open file .
September 1997. Jeremy Allison ( jallison @ whistle . com ) . Added oplock
support .
rewritten completely to use new tdb code . Tridge , Dec ' 99
Added POSIX locking support . Jeremy Allison ( jeremy @ valinux . com ) , Apr . 2000.
Added Unix Extensions POSIX locking support . Jeremy Allison Mar 2006.
*/
# include "includes.h"
2022-08-30 08:55:03 +03:00
# include "lib/util/time_basic.h"
2012-01-10 17:13:49 +04:00
# include "system/filesys.h"
2017-01-01 23:00:55 +03:00
# include "lib/util/server_id.h"
2020-10-28 14:09:39 +03:00
# include "share_mode_lock.h"
2020-11-04 14:39:48 +03:00
# include "share_mode_lock_private.h"
2012-01-10 17:13:49 +04:00
# include "locking/proto.h"
# include "smbd/globals.h"
# include "dbwrap/dbwrap.h"
# include "dbwrap/dbwrap_open.h"
2019-11-05 14:01:52 +03:00
# include "dbwrap/dbwrap_private.h"
2012-01-10 17:13:49 +04:00
# include "../libcli/security/security.h"
# include "serverid.h"
# include "messages.h"
# include "util_tdb.h"
# include "../librpc/gen_ndr/ndr_open_files.h"
2012-11-29 19:10:22 +04:00
# include "source3/lib/dbwrap/dbwrap_watch.h"
2014-11-27 21:32:46 +03:00
# include "locking/leases_db.h"
2015-04-15 20:36:00 +03:00
# include "../lib/util/memcache.h"
2017-01-04 10:00:29 +03:00
# include "lib/util/tevent_ntstatus.h"
2019-11-05 14:01:52 +03:00
# include "g_lock.h"
2020-09-28 11:35:32 +03:00
# include "smbd/fd_handle.h"
2021-01-03 23:53:49 +03:00
# include "lib/global_contexts.h"
2012-01-10 17:13:49 +04:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_LOCKING
2022-09-10 21:39:19 +03:00
# define DBG_GET_SHARE_MODE_LOCK(__status, ...) \
DBG_PREFIX ( \
NT_STATUS_EQUAL ( __status , NT_STATUS_NOT_FOUND ) ? \
DBGLVL_DEBUG : DBGLVL_ERR , \
( __VA_ARGS__ ) )
2012-01-10 17:13:49 +04:00
2022-08-26 11:44:59 +03:00
struct share_mode_lock {
struct file_id id ;
struct share_mode_data * cached_data ;
} ;
2012-01-10 17:13:49 +04:00
/* the locking database handle */
2019-11-05 14:01:52 +03:00
static struct g_lock_ctx * lock_ctx ;
2012-01-10 17:13:49 +04:00
static bool locking_init_internal ( bool read_only )
{
2016-07-13 08:27:30 +03:00
struct db_context * backend ;
2014-11-02 22:21:44 +03:00
char * db_path ;
2012-01-10 17:13:49 +04:00
brl_init ( read_only ) ;
2019-11-05 14:01:52 +03:00
if ( lock_ctx ! = NULL ) {
2012-01-10 17:13:49 +04:00
return True ;
2019-11-05 14:01:52 +03:00
}
2012-01-10 17:13:49 +04:00
2018-08-16 11:51:44 +03:00
db_path = lock_path ( talloc_tos ( ) , " locking.tdb " ) ;
2014-11-02 22:21:44 +03:00
if ( db_path = = NULL ) {
return false ;
}
2016-07-13 08:27:30 +03:00
backend = db_open ( NULL , db_path ,
2014-01-10 06:41:08 +04:00
SMB_OPEN_DATABASE_TDB_HASH_SIZE ,
2019-06-30 09:54:20 +03:00
TDB_DEFAULT |
TDB_VOLATILE |
TDB_CLEAR_IF_FIRST |
TDB_INCOMPATIBLE_HASH |
TDB_SEQNUM ,
2012-01-06 20:19:54 +04:00
read_only ? O_RDONLY : O_RDWR | O_CREAT , 0644 ,
2019-11-05 14:01:52 +03:00
DBWRAP_LOCK_ORDER_NONE ,
DBWRAP_FLAG_NONE ) ;
2014-11-02 22:21:44 +03:00
TALLOC_FREE ( db_path ) ;
2016-07-13 08:27:30 +03:00
if ( ! backend ) {
2012-01-10 17:13:49 +04:00
DEBUG ( 0 , ( " ERROR: Failed to initialise locking database \n " ) ) ;
return False ;
}
2019-11-05 14:01:52 +03:00
lock_ctx = g_lock_ctx_init_backend (
NULL , global_messaging_context ( ) , & backend ) ;
if ( lock_ctx = = NULL ) {
2016-07-13 08:27:30 +03:00
TALLOC_FREE ( backend ) ;
return false ;
}
2019-11-05 14:01:52 +03:00
g_lock_set_lock_order ( lock_ctx , DBWRAP_LOCK_ORDER_1 ) ;
2012-01-10 17:13:49 +04:00
2016-07-13 08:27:30 +03:00
if ( ! posix_locking_init ( read_only ) ) {
2019-11-05 14:01:52 +03:00
TALLOC_FREE ( lock_ctx ) ;
2016-07-13 08:27:30 +03:00
return False ;
}
2012-11-29 19:10:22 +04:00
2012-01-10 17:13:49 +04:00
return True ;
}
bool locking_init ( void )
{
return locking_init_internal ( false ) ;
}
bool locking_init_readonly ( void )
{
return locking_init_internal ( true ) ;
}
/*******************************************************************
Deinitialize the share_mode management .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
bool locking_end ( void )
{
brl_shutdown ( ) ;
2019-11-05 14:01:52 +03:00
TALLOC_FREE ( lock_ctx ) ;
2012-01-10 17:13:49 +04:00
return true ;
}
/*******************************************************************
Form a static locking key for a dev / inode pair .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2013-03-12 18:10:51 +04:00
static TDB_DATA locking_key ( const struct file_id * id )
2012-01-10 17:13:49 +04:00
{
2013-03-12 18:10:51 +04:00
return make_tdb_data ( ( const uint8_t * ) id , sizeof ( * id ) ) ;
2012-01-10 17:13:49 +04:00
}
2015-04-15 20:36:00 +03:00
/*******************************************************************
Share mode cache utility functions that store / delete / retrieve
entries from memcache .
For now share the statcache ( global cache ) memory space . If
a lock record gets orphaned ( which shouldn ' t happen as we ' re
using the same locking_key data as lookup ) it will eventually
fall out of the cache via the normal LRU trim mechanism . If
necessary we can always make this a separate ( smaller ) cache .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2018-09-10 15:59:14 +03:00
static DATA_BLOB memcache_key ( const struct file_id * id )
2015-04-15 20:36:00 +03:00
{
return data_blob_const ( ( const void * ) id , sizeof ( * id ) ) ;
}
static void share_mode_memcache_store ( struct share_mode_data * d )
{
const DATA_BLOB key = memcache_key ( & d - > id ) ;
2019-11-09 23:05:41 +03:00
struct file_id_buf idbuf ;
2015-04-15 20:36:00 +03:00
2020-07-07 12:49:27 +03:00
DBG_DEBUG ( " stored entry for file %s epoch % " PRIx64 " key %s \n " ,
2018-09-10 13:55:48 +03:00
d - > base_name ,
2020-07-07 12:49:27 +03:00
d - > unique_content_epoch ,
2019-11-09 23:05:41 +03:00
file_id_str_buf ( d - > id , & idbuf ) ) ;
2015-04-15 20:36:00 +03:00
/* Ensure everything stored in the cache is pristine. */
2022-08-22 17:53:38 +03:00
SMB_ASSERT ( ! d - > modified ) ;
SMB_ASSERT ( ! d - > not_stored ) ;
2015-04-15 20:36:00 +03:00
/*
* Ensure the memory going into the cache
* doesn ' t have a destructor so it can be
2019-06-27 15:40:08 +03:00
* cleanly evicted by the memcache LRU
* mechanism .
2015-04-15 20:36:00 +03:00
*/
talloc_set_destructor ( d , NULL ) ;
/* Cache will own d after this call. */
memcache_add_talloc ( NULL ,
SHARE_MODE_LOCK_CACHE ,
key ,
& d ) ;
}
/*
* NB . We use ndr_pull_hyper on a stack - created
* struct ndr_pull with no talloc allowed , as we
* need this to be really fast as an ndr - peek into
2020-03-09 14:36:31 +03:00
* the first 10 bytes of the blob .
2015-04-15 20:36:00 +03:00
*/
2019-06-28 08:48:45 +03:00
static enum ndr_err_code get_share_mode_blob_header (
2020-07-07 12:49:27 +03:00
const uint8_t * buf , size_t buflen , uint64_t * pepoch , uint16_t * pflags )
2015-04-15 20:36:00 +03:00
{
2020-04-24 09:55:35 +03:00
struct ndr_pull ndr = {
. data = discard_const_p ( uint8_t , buf ) ,
. data_size = buflen ,
} ;
2020-07-07 12:49:27 +03:00
NDR_CHECK ( ndr_pull_hyper ( & ndr , NDR_SCALARS , pepoch ) ) ;
2019-08-09 17:16:21 +03:00
NDR_CHECK ( ndr_pull_uint16 ( & ndr , NDR_SCALARS , pflags ) ) ;
2015-04-15 20:36:00 +03:00
return NDR_ERR_SUCCESS ;
}
static int share_mode_data_nofree_destructor ( struct share_mode_data * d )
{
return - 1 ;
}
2020-04-24 10:04:27 +03:00
static struct share_mode_data * share_mode_memcache_fetch (
TALLOC_CTX * mem_ctx ,
2020-12-01 15:50:32 +03:00
struct file_id id ,
2020-04-24 10:04:27 +03:00
const uint8_t * buf ,
size_t buflen )
2015-04-15 20:36:00 +03:00
{
2020-12-01 15:50:32 +03:00
const DATA_BLOB key = memcache_key ( & id ) ;
2015-04-15 20:36:00 +03:00
enum ndr_err_code ndr_err ;
struct share_mode_data * d ;
2020-07-07 12:49:27 +03:00
uint64_t unique_content_epoch ;
2019-08-09 17:16:21 +03:00
uint16_t flags ;
2015-04-15 20:36:00 +03:00
void * ptr ;
2019-11-09 23:08:15 +03:00
struct file_id_buf idbuf ;
2015-04-15 20:36:00 +03:00
ptr = memcache_lookup_talloc ( NULL ,
SHARE_MODE_LOCK_CACHE ,
key ) ;
if ( ptr = = NULL ) {
2019-11-09 23:08:15 +03:00
DBG_DEBUG ( " failed to find entry for key %s \n " ,
file_id_str_buf ( id , & idbuf ) ) ;
2015-04-15 20:36:00 +03:00
return NULL ;
}
/* sequence number key is at start of blob. */
2020-04-24 09:55:35 +03:00
ndr_err = get_share_mode_blob_header (
2020-07-07 12:49:27 +03:00
buf , buflen , & unique_content_epoch , & flags ) ;
2015-04-15 20:36:00 +03:00
if ( ndr_err ! = NDR_ERR_SUCCESS ) {
/* Bad blob. Remove entry. */
2019-11-09 23:08:15 +03:00
DBG_DEBUG ( " bad blob %u key %s \n " ,
( unsigned int ) ndr_err ,
file_id_str_buf ( id , & idbuf ) ) ;
2015-04-15 20:36:00 +03:00
memcache_delete ( NULL ,
SHARE_MODE_LOCK_CACHE ,
key ) ;
return NULL ;
}
d = ( struct share_mode_data * ) ptr ;
2020-07-07 12:49:27 +03:00
if ( d - > unique_content_epoch ! = unique_content_epoch ) {
DBG_DEBUG ( " epoch changed (cached % " PRIx64 " ) (new % " PRIx64 " ) "
2018-09-10 13:55:48 +03:00
" for key %s \n " ,
2020-07-07 12:49:27 +03:00
d - > unique_content_epoch ,
unique_content_epoch ,
2019-11-09 23:08:15 +03:00
file_id_str_buf ( id , & idbuf ) ) ;
2015-04-15 20:36:00 +03:00
/* Cache out of date. Remove entry. */
memcache_delete ( NULL ,
SHARE_MODE_LOCK_CACHE ,
key ) ;
return NULL ;
}
/* Move onto mem_ctx. */
d = talloc_move ( mem_ctx , & ptr ) ;
/*
* Now we own d , prevent the cache from freeing it
* when we delete the entry .
*/
talloc_set_destructor ( d , share_mode_data_nofree_destructor ) ;
/* Remove from the cache. We own it now. */
memcache_delete ( NULL ,
SHARE_MODE_LOCK_CACHE ,
key ) ;
/* And reset the destructor to none. */
talloc_set_destructor ( d , NULL ) ;
2020-07-07 12:49:27 +03:00
DBG_DEBUG ( " fetched entry for file %s epoch % " PRIx64 " key %s \n " ,
2018-09-10 13:55:48 +03:00
d - > base_name ,
2020-07-07 12:49:27 +03:00
d - > unique_content_epoch ,
2019-11-09 23:08:15 +03:00
file_id_str_buf ( id , & idbuf ) ) ;
2015-04-15 20:36:00 +03:00
return d ;
}
2020-03-26 21:45:10 +03:00
/*
* 132 is the sizeof an ndr - encoded struct share_mode_entry_buf .
* Reading / writing entries will immediately error out if this
* size differs ( push / pull is done without allocs ) .
*/
struct share_mode_entry_buf {
uint8_t buf [ 132 ] ;
} ;
# define SHARE_MODE_ENTRY_SIZE (sizeof(struct share_mode_entry_buf))
static bool share_mode_entry_put (
const struct share_mode_entry * e ,
struct share_mode_entry_buf * dst )
{
DATA_BLOB blob = { . data = dst - > buf , . length = sizeof ( dst - > buf ) } ;
enum ndr_err_code ndr_err ;
if ( DEBUGLEVEL > = 10 ) {
DBG_DEBUG ( " share_mode_entry: \n " ) ;
NDR_PRINT_DEBUG ( share_mode_entry , discard_const_p ( void , e ) ) ;
}
ndr_err = ndr_push_struct_into_fixed_blob (
& blob ,
e ,
( ndr_push_flags_fn_t ) ndr_push_share_mode_entry ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DBG_WARNING ( " ndr_push_share_mode_entry failed: %s \n " ,
ndr_errstr ( ndr_err ) ) ;
return false ;
}
return true ;
}
static bool share_mode_entry_get (
const uint8_t ptr [ SHARE_MODE_ENTRY_SIZE ] , struct share_mode_entry * e )
{
enum ndr_err_code ndr_err = NDR_ERR_SUCCESS ;
DATA_BLOB blob = {
. data = discard_const_p ( uint8_t , ptr ) ,
. length = SHARE_MODE_ENTRY_SIZE ,
} ;
ndr_err = ndr_pull_struct_blob_all_noalloc (
& blob , e , ( ndr_pull_flags_fn_t ) ndr_pull_share_mode_entry ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DBG_WARNING ( " ndr_pull_share_mode_entry failed \n " ) ;
return false ;
}
return true ;
}
2020-04-14 17:51:15 +03:00
/*
* locking . tdb records consist of
*
* uint32_t share_mode_data_len
* uint8_t [ share_mode_data ] This is struct share_mode_data in NDR
*
* 0 [ SHARE_MODE_ENTRY_SIZE ] Sorted array of share modes ,
* 1 [ SHARE_MODE_ENTRY_SIZE ] filling up the rest of the data in the
* 2 [ SHARE_MODE_ENTRY_SIZE ] g_lock . c maintained record in locking . tdb
*/
struct locking_tdb_data {
const uint8_t * share_mode_data_buf ;
size_t share_mode_data_len ;
const uint8_t * share_entries ;
size_t num_share_entries ;
} ;
static bool locking_tdb_data_get (
struct locking_tdb_data * data , const uint8_t * buf , size_t buflen )
{
uint32_t share_mode_data_len , share_entries_len ;
if ( buflen = = 0 ) {
* data = ( struct locking_tdb_data ) { 0 } ;
return true ;
}
if ( buflen < sizeof ( uint32_t ) ) {
return false ;
}
share_mode_data_len = PULL_LE_U32 ( buf , 0 ) ;
buf + = sizeof ( uint32_t ) ;
buflen - = sizeof ( uint32_t ) ;
if ( buflen < share_mode_data_len ) {
return false ;
}
share_entries_len = buflen - share_mode_data_len ;
if ( ( share_entries_len % SHARE_MODE_ENTRY_SIZE ) ! = 0 ) {
return false ;
}
* data = ( struct locking_tdb_data ) {
. share_mode_data_buf = buf ,
. share_mode_data_len = share_mode_data_len ,
. share_entries = buf + share_mode_data_len ,
. num_share_entries = share_entries_len / SHARE_MODE_ENTRY_SIZE ,
} ;
return true ;
}
struct locking_tdb_data_fetch_state {
TALLOC_CTX * mem_ctx ;
uint8_t * data ;
size_t datalen ;
} ;
static void locking_tdb_data_fetch_fn (
struct server_id exclusive ,
size_t num_shared ,
2022-08-18 18:52:33 +03:00
const struct server_id * shared ,
2020-04-14 17:51:15 +03:00
const uint8_t * data ,
size_t datalen ,
void * private_data )
{
struct locking_tdb_data_fetch_state * state = private_data ;
state - > datalen = datalen ;
state - > data = talloc_memdup ( state - > mem_ctx , data , datalen ) ;
}
static NTSTATUS locking_tdb_data_fetch (
TDB_DATA key , TALLOC_CTX * mem_ctx , struct locking_tdb_data * * ltdb )
{
struct locking_tdb_data_fetch_state state = { 0 } ;
struct locking_tdb_data * result = NULL ;
NTSTATUS status ;
bool ok ;
result = talloc_zero ( mem_ctx , struct locking_tdb_data ) ;
if ( result = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
state . mem_ctx = result ;
status = g_lock_dump ( lock_ctx , key , locking_tdb_data_fetch_fn , & state ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NOT_FOUND ) ) {
/*
* Just return an empty record
*/
goto done ;
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-09-10 21:39:19 +03:00
DBG_ERR ( " g_lock_dump failed: %s \n " ,
nt_errstr ( status ) ) ;
2020-04-14 17:51:15 +03:00
return status ;
}
if ( state . datalen = = 0 ) {
goto done ;
}
ok = locking_tdb_data_get ( result , state . data , state . datalen ) ;
if ( ! ok ) {
2022-09-10 21:39:19 +03:00
DBG_ERR ( " locking_tdb_data_get failed for %zu bytes \n " ,
2020-04-14 17:51:15 +03:00
state . datalen ) ;
TALLOC_FREE ( result ) ;
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
done :
* ltdb = result ;
return NT_STATUS_OK ;
}
static NTSTATUS locking_tdb_data_store (
TDB_DATA key ,
const struct locking_tdb_data * ltdb ,
const TDB_DATA * share_mode_dbufs ,
size_t num_share_mode_dbufs )
{
uint8_t share_mode_data_len_buf [ 4 ] ;
TDB_DATA dbufs [ num_share_mode_dbufs + 3 ] ;
NTSTATUS status ;
if ( ( ltdb - > share_mode_data_len = = 0 ) & &
( ltdb - > num_share_entries = = 0 ) & &
( num_share_mode_dbufs = = 0 ) ) {
/*
* Nothing to write
*/
status = g_lock_write_data ( lock_ctx , key , NULL , 0 ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-09-10 21:39:19 +03:00
DBG_ERR ( " g_lock_writev_data() failed: %s \n " ,
nt_errstr ( status ) ) ;
2020-04-14 17:51:15 +03:00
}
return status ;
}
PUSH_LE_U32 ( share_mode_data_len_buf , 0 , ltdb - > share_mode_data_len ) ;
dbufs [ 0 ] = ( TDB_DATA ) {
. dptr = share_mode_data_len_buf ,
. dsize = sizeof ( share_mode_data_len_buf ) ,
} ;
dbufs [ 1 ] = ( TDB_DATA ) {
. dptr = discard_const_p ( uint8_t , ltdb - > share_mode_data_buf ) ,
. dsize = ltdb - > share_mode_data_len ,
} ;
if ( ltdb - > num_share_entries > SIZE_MAX / SHARE_MODE_ENTRY_SIZE ) {
/* overflow */
return NT_STATUS_BUFFER_OVERFLOW ;
}
dbufs [ 2 ] = ( TDB_DATA ) {
. dptr = discard_const_p ( uint8_t , ltdb - > share_entries ) ,
. dsize = ltdb - > num_share_entries * SHARE_MODE_ENTRY_SIZE ,
} ;
if ( num_share_mode_dbufs ! = 0 ) {
memcpy ( & dbufs [ 3 ] ,
share_mode_dbufs ,
num_share_mode_dbufs * sizeof ( TDB_DATA ) ) ;
}
status = g_lock_writev_data ( lock_ctx , key , dbufs , ARRAY_SIZE ( dbufs ) ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-09-10 21:39:19 +03:00
DBG_ERR ( " g_lock_writev_data() failed: %s \n " ,
nt_errstr ( status ) ) ;
2020-04-14 17:51:15 +03:00
}
return status ;
}
2012-01-10 17:13:49 +04:00
/*******************************************************************
Get all share mode entries for a dev / inode pair .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2020-04-24 10:14:55 +03:00
static struct share_mode_data * parse_share_modes (
TALLOC_CTX * mem_ctx ,
2020-12-01 15:50:32 +03:00
struct file_id id ,
2020-04-24 10:14:55 +03:00
const uint8_t * buf ,
size_t buflen )
2012-01-10 17:13:49 +04:00
{
struct share_mode_data * d ;
enum ndr_err_code ndr_err ;
DATA_BLOB blob ;
2015-04-15 20:36:00 +03:00
/* See if we already have a cached copy of this key. */
2020-12-01 15:50:32 +03:00
d = share_mode_memcache_fetch ( mem_ctx , id , buf , buflen ) ;
2015-04-15 20:36:00 +03:00
if ( d ! = NULL ) {
return d ;
}
2012-05-07 13:26:39 +04:00
d = talloc ( mem_ctx , struct share_mode_data ) ;
2012-01-10 17:13:49 +04:00
if ( d = = NULL ) {
DEBUG ( 0 , ( " talloc failed \n " ) ) ;
goto fail ;
}
2020-04-24 10:14:55 +03:00
blob = ( DATA_BLOB ) {
. data = discard_const_p ( uint8_t , buf ) ,
. length = buflen ,
} ;
2014-10-29 01:20:26 +03:00
ndr_err = ndr_pull_struct_blob_all (
2012-01-10 17:13:49 +04:00
& blob , d , d , ( ndr_pull_flags_fn_t ) ndr_pull_share_mode_data ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
2019-05-24 15:46:19 +03:00
DBG_WARNING ( " ndr_pull_share_mode_data failed: %s \n " ,
ndr_errstr ( ndr_err ) ) ;
2012-01-10 17:13:49 +04:00
goto fail ;
}
if ( DEBUGLEVEL > = 10 ) {
DEBUG ( 10 , ( " parse_share_modes: \n " ) ) ;
NDR_PRINT_DEBUG ( share_mode_data , d ) ;
}
return d ;
fail :
TALLOC_FREE ( d ) ;
return NULL ;
}
2022-08-22 17:53:38 +03:00
static NTSTATUS share_mode_data_ltdb_store ( struct share_mode_data * d ,
TDB_DATA key ,
struct locking_tdb_data * ltdb ,
const TDB_DATA * share_mode_dbufs ,
size_t num_share_mode_dbufs )
2012-01-10 17:13:49 +04:00
{
2019-11-05 14:01:52 +03:00
DATA_BLOB blob = { 0 } ;
2019-06-27 15:40:08 +03:00
NTSTATUS status ;
if ( ! d - > modified ) {
2022-08-22 17:53:38 +03:00
DBG_DEBUG ( " share_mode_data not modified \n " ) ;
goto store ;
2019-06-27 15:40:08 +03:00
}
2012-01-10 17:13:49 +04:00
2022-08-22 17:53:38 +03:00
d - > unique_content_epoch = generate_unique_u64 ( d - > unique_content_epoch ) ;
2012-01-10 17:13:49 +04:00
if ( DEBUGLEVEL > = 10 ) {
2019-06-27 15:40:08 +03:00
DBG_DEBUG ( " \n " ) ;
2012-01-10 17:13:49 +04:00
NDR_PRINT_DEBUG ( share_mode_data , d ) ;
}
2022-08-22 17:53:38 +03:00
if ( ltdb - > num_share_entries ! = 0 | | num_share_mode_dbufs ! = 0 ) {
2019-11-05 14:01:52 +03:00
enum ndr_err_code ndr_err ;
ndr_err = ndr_push_struct_blob (
& blob ,
2020-04-14 17:51:15 +03:00
ltdb ,
2019-11-05 14:01:52 +03:00
d ,
( ndr_push_flags_fn_t ) ndr_push_share_mode_data ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
2022-08-22 17:53:38 +03:00
DBG_ERR ( " ndr_push_share_mode_data failed: %s \n " ,
2019-11-05 14:01:52 +03:00
ndr_errstr ( ndr_err ) ) ;
return ndr_map_error2ntstatus ( ndr_err ) ;
}
2012-01-10 17:13:49 +04:00
}
2020-04-14 17:51:15 +03:00
ltdb - > share_mode_data_buf = blob . data ;
ltdb - > share_mode_data_len = blob . length ;
2012-01-10 17:13:49 +04:00
2022-08-22 17:53:38 +03:00
store :
status = locking_tdb_data_store ( key ,
ltdb ,
share_mode_dbufs ,
num_share_mode_dbufs ) ;
2022-08-22 17:53:38 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " locking_tdb_data_store failed: %s \n " ,
nt_errstr ( status ) ) ;
return status ;
}
d - > modified = false ;
d - > not_stored = ( ltdb - > share_mode_data_len = = 0 ) ;
2022-08-22 17:53:38 +03:00
return NT_STATUS_OK ;
}
/*******************************************************************
If modified , store the share_mode_data back into the database .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static NTSTATUS share_mode_data_store ( struct share_mode_data * d )
{
TDB_DATA key = locking_key ( & d - > id ) ;
struct locking_tdb_data * ltdb = NULL ;
NTSTATUS status ;
if ( ! d - > modified ) {
DBG_DEBUG ( " not modified \n " ) ;
return NT_STATUS_OK ;
}
if ( DEBUGLEVEL > = 10 ) {
DBG_DEBUG ( " \n " ) ;
NDR_PRINT_DEBUG ( share_mode_data , d ) ;
}
status = locking_tdb_data_fetch ( key , d , & ltdb ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " locking_tdb_data_fetch failed: %s \n " ,
nt_errstr ( status ) ) ;
return status ;
}
status = share_mode_data_ltdb_store ( d , key , ltdb , NULL , 0 ) ;
2020-04-14 17:51:15 +03:00
TALLOC_FREE ( ltdb ) ;
2022-08-22 17:53:38 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " share_mode_data_ltdb_store failed: %s \n " ,
nt_errstr ( status ) ) ;
return status ;
}
2022-08-22 17:53:38 +03:00
return NT_STATUS_OK ;
2012-01-10 17:13:49 +04:00
}
2012-01-13 02:46:45 +04:00
/*******************************************************************
Allocate a new share_mode_data struct , mark it unmodified .
fresh is set to note that currently there is no database entry .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2012-01-10 17:13:49 +04:00
static struct share_mode_data * fresh_share_mode_lock (
TALLOC_CTX * mem_ctx , const char * servicepath ,
const struct smb_filename * smb_fname ,
const struct timespec * old_write_time )
{
struct share_mode_data * d ;
if ( ( servicepath = = NULL ) | | ( smb_fname = = NULL ) | |
( old_write_time = = NULL ) ) {
return NULL ;
}
d = talloc_zero ( mem_ctx , struct share_mode_data ) ;
if ( d = = NULL ) {
goto fail ;
}
2020-07-07 12:49:27 +03:00
d - > unique_content_epoch = generate_unique_u64 ( 0 ) ;
2015-04-15 20:36:00 +03:00
2012-01-10 17:13:49 +04:00
d - > base_name = talloc_strdup ( d , smb_fname - > base_name ) ;
if ( d - > base_name = = NULL ) {
goto fail ;
}
if ( smb_fname - > stream_name ! = NULL ) {
d - > stream_name = talloc_strdup ( d , smb_fname - > stream_name ) ;
if ( d - > stream_name = = NULL ) {
goto fail ;
}
}
d - > servicepath = talloc_strdup ( d , servicepath ) ;
if ( d - > servicepath = = NULL ) {
goto fail ;
}
2019-12-02 18:30:50 +03:00
d - > old_write_time = full_timespec_to_nt_time ( old_write_time ) ;
2020-05-11 12:35:04 +03:00
d - > flags = SHARE_MODE_SHARE_DELETE |
SHARE_MODE_SHARE_WRITE |
SHARE_MODE_SHARE_READ ;
2012-01-10 17:13:49 +04:00
d - > modified = false ;
2022-08-22 17:53:38 +03:00
d - > not_stored = true ;
2012-01-10 17:13:49 +04:00
return d ;
fail :
DEBUG ( 0 , ( " talloc failed \n " ) ) ;
TALLOC_FREE ( d ) ;
return NULL ;
}
2019-11-05 14:01:52 +03:00
/*
* Key that ' s locked with g_lock
*/
2022-08-26 10:42:45 +03:00
static struct file_id share_mode_lock_key_id = { } ;
2019-11-05 14:01:52 +03:00
static TDB_DATA share_mode_lock_key = {
2022-08-26 10:42:45 +03:00
. dptr = ( uint8_t * ) & share_mode_lock_key_id ,
. dsize = sizeof ( share_mode_lock_key_id ) ,
2019-11-05 14:01:52 +03:00
} ;
static size_t share_mode_lock_key_refcount = 0 ;
2019-06-27 15:40:08 +03:00
/*
* We can only ever have one share mode locked . Use a static
* share_mode_data pointer that is shared by multiple nested
* share_mode_lock structures , explicitly refcounted .
*/
static struct share_mode_data * static_share_mode_data = NULL ;
2012-01-13 02:46:45 +04:00
/*******************************************************************
Either fetch a share mode from the database , or allocate a fresh
one if the record doesn ' t exist .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-11-05 14:01:52 +03:00
struct get_static_share_mode_data_state {
TALLOC_CTX * mem_ctx ;
struct file_id id ;
const char * servicepath ;
const struct smb_filename * smb_fname ;
const struct timespec * old_write_time ;
NTSTATUS status ;
} ;
2012-01-10 17:13:49 +04:00
2019-11-05 14:01:52 +03:00
static void get_static_share_mode_data_fn (
struct server_id exclusive ,
size_t num_shared ,
2022-08-18 18:52:33 +03:00
const struct server_id * shared ,
2019-11-05 14:01:52 +03:00
const uint8_t * data ,
size_t datalen ,
void * private_data )
{
struct get_static_share_mode_data_state * state = private_data ;
struct share_mode_data * d = NULL ;
2020-04-14 17:51:15 +03:00
struct locking_tdb_data ltdb = { 0 } ;
if ( datalen ! = 0 ) {
bool ok ;
ok = locking_tdb_data_get ( & ltdb , data , datalen ) ;
if ( ! ok ) {
2022-09-10 21:39:19 +03:00
DBG_ERR ( " locking_tdb_data_get failed \n " ) ;
2020-04-14 17:51:15 +03:00
state - > status = NT_STATUS_INTERNAL_DB_CORRUPTION ;
return ;
}
}
2012-01-10 17:13:49 +04:00
2020-04-14 17:51:15 +03:00
if ( ltdb . share_mode_data_len = = 0 ) {
2019-11-05 14:01:52 +03:00
if ( state - > smb_fname = = NULL ) {
state - > status = NT_STATUS_NOT_FOUND ;
return ;
}
2019-06-27 15:40:08 +03:00
d = fresh_share_mode_lock (
2019-11-05 14:01:52 +03:00
state - > mem_ctx ,
state - > servicepath ,
state - > smb_fname ,
state - > old_write_time ) ;
2019-06-27 15:40:08 +03:00
if ( d = = NULL ) {
2019-11-05 14:01:52 +03:00
state - > status = NT_STATUS_NO_MEMORY ;
return ;
2019-06-27 15:40:08 +03:00
}
2012-01-10 17:13:49 +04:00
} else {
2020-04-14 17:51:15 +03:00
d = parse_share_modes (
lock_ctx ,
2020-12-01 15:50:32 +03:00
state - > id ,
2020-04-14 17:51:15 +03:00
ltdb . share_mode_data_buf ,
ltdb . share_mode_data_len ) ;
2019-06-27 15:40:08 +03:00
if ( d = = NULL ) {
2019-11-05 14:01:52 +03:00
state - > status = NT_STATUS_INTERNAL_DB_CORRUPTION ;
return ;
2019-06-27 15:40:08 +03:00
}
2012-01-10 17:13:49 +04:00
}
2019-11-05 14:01:52 +03:00
d - > id = state - > id ;
2019-06-27 15:40:08 +03:00
static_share_mode_data = d ;
2019-11-05 14:01:52 +03:00
}
static NTSTATUS get_static_share_mode_data (
struct file_id id ,
const char * servicepath ,
const struct smb_filename * smb_fname ,
const struct timespec * old_write_time )
{
struct get_static_share_mode_data_state state = {
. mem_ctx = lock_ctx ,
. id = id ,
. servicepath = servicepath ,
. smb_fname = smb_fname ,
. old_write_time = old_write_time ,
} ;
NTSTATUS status ;
SMB_ASSERT ( static_share_mode_data = = NULL ) ;
status = g_lock_dump (
lock_ctx ,
share_mode_lock_key ,
get_static_share_mode_data_fn ,
& state ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-09-10 21:39:19 +03:00
DBG_GET_SHARE_MODE_LOCK ( status ,
" g_lock_dump failed: %s \n " ,
nt_errstr ( status ) ) ;
2019-11-05 14:01:52 +03:00
return status ;
}
if ( ! NT_STATUS_IS_OK ( state . status ) ) {
2022-09-10 21:39:19 +03:00
DBG_GET_SHARE_MODE_LOCK ( status ,
" get_static_share_mode_data_fn failed: %s \n " ,
nt_errstr ( state . status ) ) ;
2019-11-05 14:01:52 +03:00
return state . status ;
}
2012-01-10 17:13:49 +04:00
2019-06-27 15:40:08 +03:00
return NT_STATUS_OK ;
2012-01-10 20:07:29 +04:00
}
2022-08-26 11:40:44 +03:00
struct file_id share_mode_lock_file_id ( const struct share_mode_lock * lck )
{
return lck - > id ;
}
2022-08-26 11:24:52 +03:00
NTSTATUS share_mode_lock_access_private_data ( struct share_mode_lock * lck ,
struct share_mode_data * * data )
{
/*
2022-08-26 11:44:59 +03:00
* For now we always have lck - > cached_data ,
2022-08-26 11:24:52 +03:00
* but we may change that in future .
*/
2022-08-26 11:44:59 +03:00
SMB_ASSERT ( lck - > cached_data ! = NULL ) ;
* data = lck - > cached_data ;
2022-08-26 11:24:52 +03:00
return NT_STATUS_OK ;
}
2012-01-13 02:46:45 +04:00
/*******************************************************************
2012-08-29 19:19:04 +04:00
Get a share_mode_lock , Reference counted to allow nested calls .
2012-01-13 02:46:45 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2019-06-27 15:40:08 +03:00
static int share_mode_lock_destructor ( struct share_mode_lock * lck ) ;
2022-08-26 14:03:33 +03:00
static NTSTATUS get_share_mode_lock_internal (
2013-03-12 18:10:51 +04:00
struct file_id id ,
2012-01-10 20:07:29 +04:00
const char * servicepath ,
const struct smb_filename * smb_fname ,
2022-08-26 14:03:33 +03:00
const struct timespec * old_write_time ,
struct share_mode_lock * lck )
2012-01-10 20:07:29 +04:00
{
2019-06-27 15:40:08 +03:00
NTSTATUS status ;
2012-01-10 20:07:29 +04:00
2022-08-26 14:03:33 +03:00
* lck = ( struct share_mode_lock ) {
. id = id ,
} ;
2013-12-06 11:40:03 +04:00
2019-11-05 14:01:52 +03:00
if ( share_mode_lock_key_refcount = = 0 ) {
2022-08-26 14:03:33 +03:00
TDB_DATA key = locking_key ( & id ) ;
2019-11-05 14:01:52 +03:00
status = g_lock_lock (
lock_ctx ,
key ,
G_LOCK_WRITE ,
( struct timeval ) { . tv_sec = 3600 } ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " g_lock_lock failed: %s \n " ,
nt_errstr ( status ) ) ;
2022-08-26 14:03:33 +03:00
return status ;
2019-06-27 17:58:54 +03:00
}
2022-08-26 10:42:45 +03:00
share_mode_lock_key_id = id ;
2019-11-05 14:01:52 +03:00
}
2019-06-27 17:58:54 +03:00
2022-08-26 10:42:45 +03:00
if ( ! file_id_equal ( & share_mode_lock_key_id , & id ) ) {
struct file_id_buf existing ;
struct file_id_buf requested ;
DBG_ERR ( " Can not lock two share modes "
" simultaneously: existing %s requested %s \n " ,
file_id_str_buf ( share_mode_lock_key_id , & existing ) ,
file_id_str_buf ( id , & requested ) ) ;
2020-12-23 13:59:05 +03:00
smb_panic ( __location__ ) ;
2019-11-05 14:01:52 +03:00
goto fail ;
}
2019-06-27 18:23:07 +03:00
2019-11-05 14:01:52 +03:00
SMB_ASSERT ( share_mode_lock_key_refcount < SIZE_MAX ) ;
share_mode_lock_key_refcount + = 1 ;
2019-06-27 18:23:07 +03:00
2022-08-26 10:50:00 +03:00
if ( static_share_mode_data ! = NULL ) {
goto done ;
}
2019-06-27 18:23:07 +03:00
2019-11-05 14:01:52 +03:00
status = get_static_share_mode_data (
id ,
servicepath ,
smb_fname ,
old_write_time ) ;
2019-11-28 13:18:29 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " get_static_share_mode_data failed: %s \n " ,
nt_errstr ( status ) ) ;
2019-11-05 14:01:52 +03:00
share_mode_lock_key_refcount - = 1 ;
2019-11-28 13:18:29 +03:00
goto fail ;
2019-06-27 15:40:08 +03:00
}
done :
2022-08-26 11:44:59 +03:00
lck - > cached_data = static_share_mode_data ;
2019-06-27 15:40:08 +03:00
2020-12-23 13:59:05 +03:00
if ( CHECK_DEBUGLVL ( DBGLVL_DEBUG ) ) {
struct file_id_buf returned ;
2022-08-26 10:50:00 +03:00
DBG_DEBUG ( " Returning %s (data_cached=%u key_refcount=%zu) \n " ,
2020-12-23 13:59:05 +03:00
file_id_str_buf ( id , & returned ) ,
2022-08-26 10:50:00 +03:00
static_share_mode_data ! = NULL ,
2020-12-23 13:59:05 +03:00
share_mode_lock_key_refcount ) ;
}
2022-08-26 14:03:33 +03:00
return NT_STATUS_OK ;
2012-01-10 20:07:29 +04:00
fail :
2019-11-05 14:01:52 +03:00
if ( share_mode_lock_key_refcount = = 0 ) {
2022-08-26 14:03:33 +03:00
NTSTATUS ulstatus = g_lock_unlock ( lock_ctx , share_mode_lock_key ) ;
if ( ! NT_STATUS_IS_OK ( ulstatus ) ) {
2019-11-05 14:01:52 +03:00
DBG_ERR ( " g_lock_unlock failed: %s \n " ,
2022-08-26 14:03:33 +03:00
nt_errstr ( ulstatus ) ) ;
2019-11-05 14:01:52 +03:00
}
}
2022-08-26 14:03:33 +03:00
return status ;
2012-01-10 20:07:29 +04:00
}
2019-06-27 15:40:08 +03:00
static int share_mode_lock_destructor ( struct share_mode_lock * lck )
{
NTSTATUS status ;
2022-08-26 10:50:00 +03:00
SMB_ASSERT ( share_mode_lock_key_refcount > 0 ) ;
share_mode_lock_key_refcount - = 1 ;
2019-06-27 15:40:08 +03:00
2022-08-26 10:50:00 +03:00
if ( share_mode_lock_key_refcount > 0 ) {
2019-06-27 15:40:08 +03:00
return 0 ;
}
2022-08-22 17:53:38 +03:00
status = share_mode_data_store ( static_share_mode_data ) ;
2019-06-27 15:40:08 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " share_mode_data_store failed: %s \n " ,
nt_errstr ( status ) ) ;
smb_panic ( " Could not store share mode data \n " ) ;
}
2022-08-26 10:50:00 +03:00
status = g_lock_unlock ( lock_ctx , share_mode_lock_key ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_ERR ( " g_lock_unlock failed: %s \n " ,
nt_errstr ( status ) ) ;
smb_panic ( " Could not unlock share mode \n " ) ;
2019-06-27 17:58:54 +03:00
}
2019-06-27 15:40:08 +03:00
2022-08-22 17:53:38 +03:00
if ( ! static_share_mode_data - > not_stored ) {
2019-06-27 15:40:08 +03:00
/*
* This is worth keeping . Without share modes ,
* share_mode_data_store above has left nothing in the
* database .
*/
share_mode_memcache_store ( static_share_mode_data ) ;
static_share_mode_data = NULL ;
}
2020-12-03 19:03:32 +03:00
TALLOC_FREE ( static_share_mode_data ) ;
2019-06-27 15:40:08 +03:00
return 0 ;
}
2022-08-26 14:03:33 +03:00
struct share_mode_lock * get_share_mode_lock (
TALLOC_CTX * mem_ctx ,
struct file_id id ,
const char * servicepath ,
const struct smb_filename * smb_fname ,
const struct timespec * old_write_time )
{
struct share_mode_lock * lck = NULL ;
NTSTATUS status ;
lck = talloc ( mem_ctx , struct share_mode_lock ) ;
if ( lck = = NULL ) {
return NULL ;
}
status = get_share_mode_lock_internal ( id ,
servicepath ,
smb_fname ,
old_write_time ,
lck ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_GET_SHARE_MODE_LOCK ( status ,
" get_share_mode_lock_internal() failed - %s \n " ,
nt_errstr ( status ) ) ;
TALLOC_FREE ( lck ) ;
return NULL ;
}
talloc_set_destructor ( lck , share_mode_lock_destructor ) ;
return lck ;
}
2022-08-18 15:14:20 +03:00
/*******************************************************************
Fetch a share mode where we know one MUST exist . This call reference
counts it internally to allow for nested lock fetches .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
struct share_mode_lock * get_existing_share_mode_lock ( TALLOC_CTX * mem_ctx ,
const struct file_id id )
{
return get_share_mode_lock ( mem_ctx , id , NULL , NULL , NULL ) ;
}
2020-04-24 12:07:53 +03:00
static void share_mode_wakeup_waiters_fn (
2022-08-26 10:27:44 +03:00
struct share_mode_lock * lck ,
2020-04-24 12:07:53 +03:00
void * private_data )
2019-08-09 01:47:39 +03:00
{
2022-08-26 10:27:44 +03:00
g_lock_wake_watchers ( lock_ctx , share_mode_lock_key ) ;
2019-08-09 01:47:39 +03:00
}
NTSTATUS share_mode_wakeup_waiters ( struct file_id id )
{
2022-08-26 10:27:44 +03:00
return share_mode_do_locked_vfs_denied ( id ,
share_mode_wakeup_waiters_fn ,
NULL ) ;
2019-08-09 01:47:39 +03:00
}
2022-08-23 00:26:06 +03:00
struct fsp_update_share_mode_flags_state {
2022-08-22 17:19:40 +03:00
struct files_struct * fsp ;
2022-08-23 00:26:06 +03:00
enum ndr_err_code ndr_err ;
2022-08-22 17:19:40 +03:00
uint64_t share_mode_epoch ;
2022-08-23 00:26:06 +03:00
uint16_t share_mode_flags ;
} ;
static void fsp_update_share_mode_flags_fn (
2022-08-22 17:19:40 +03:00
struct server_id exclusive ,
size_t num_shared ,
const struct server_id * shared ,
const uint8_t * data ,
size_t datalen ,
2022-08-23 00:26:06 +03:00
void * private_data )
{
struct fsp_update_share_mode_flags_state * state = private_data ;
2022-08-22 17:19:40 +03:00
struct locking_tdb_data ltdb = { 0 } ;
if ( datalen ! = 0 ) {
bool ok = locking_tdb_data_get ( & ltdb , data , datalen ) ;
if ( ! ok ) {
DBG_DEBUG ( " locking_tdb_data_get failed \n " ) ;
return ;
}
}
if ( ltdb . share_mode_data_len = = 0 ) {
/* Likely a ctdb tombstone record, ignore it */
return ;
}
if ( exclusive . pid ! = 0 ) {
struct server_id self =
messaging_server_id ( state - > fsp - > conn - > sconn - > msg_ctx ) ;
bool is_self = server_id_equal ( & self , & exclusive ) ;
if ( ! is_self ) {
/*
* If someone else is holding an exclusive
* lock , pretend there ' s a read lease
*/
state - > share_mode_flags = SHARE_MODE_LEASE_READ ;
return ;
}
}
2022-08-23 00:26:06 +03:00
2022-08-22 17:19:40 +03:00
state - > ndr_err = get_share_mode_blob_header ( ltdb . share_mode_data_buf ,
ltdb . share_mode_data_len ,
& state - > share_mode_epoch ,
& state - > share_mode_flags ) ;
2022-08-23 00:26:06 +03:00
}
static NTSTATUS fsp_update_share_mode_flags ( struct files_struct * fsp )
{
2022-08-22 17:19:40 +03:00
struct fsp_update_share_mode_flags_state state = { . fsp = fsp , } ;
2022-08-23 00:26:06 +03:00
int seqnum = g_lock_seqnum ( lock_ctx ) ;
2022-08-22 17:19:40 +03:00
TDB_DATA key = { 0 } ;
2022-08-23 00:26:06 +03:00
NTSTATUS status ;
if ( seqnum = = fsp - > share_mode_flags_seqnum ) {
return NT_STATUS_OK ;
}
2022-08-22 17:19:40 +03:00
key = locking_key ( & fsp - > file_id ) ;
status = g_lock_dump ( lock_ctx , key ,
fsp_update_share_mode_flags_fn ,
& state ) ;
2022-08-23 00:26:06 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
/* no DBG_GET_SHARE_MODE_LOCK here! */
2022-08-22 17:19:40 +03:00
DBG_ERR ( " g_lock_dump returned %s \n " ,
2022-08-23 00:26:06 +03:00
nt_errstr ( status ) ) ;
return status ;
}
if ( ! NDR_ERR_CODE_IS_SUCCESS ( state . ndr_err ) ) {
DBG_ERR ( " get_share_mode_blob_header returned %s \n " ,
ndr_errstr ( state . ndr_err ) ) ;
return ndr_map_error2ntstatus ( state . ndr_err ) ;
}
fsp - > share_mode_flags_seqnum = seqnum ;
fsp - > share_mode_flags = state . share_mode_flags ;
return NT_STATUS_OK ;
}
bool file_has_read_lease ( struct files_struct * fsp )
{
NTSTATUS status ;
status = fsp_update_share_mode_flags ( fsp ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
/* Safe default for leases */
return true ;
}
return ( fsp - > share_mode_flags & SHARE_MODE_LEASE_READ ) ! = 0 ;
}
2022-08-26 13:05:04 +03:00
# define share_mode_lock_assert_private_data(__lck) \
_share_mode_lock_assert_private_data ( __lck , __func__ , __location__ )
static struct share_mode_data * _share_mode_lock_assert_private_data (
struct share_mode_lock * lck ,
const char * caller_function ,
const char * caller_location )
{
struct share_mode_data * d = NULL ;
NTSTATUS status ;
status = share_mode_lock_access_private_data ( lck , & d ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
struct file_id id = share_mode_lock_file_id ( lck ) ;
struct file_id_buf id_buf ;
/* Any error recovery possible here ? */
D_ERR ( " %s:%s(): share_mode_lock_access_private_data() "
" failed for id=%s - %s \n " ,
caller_location , caller_function ,
file_id_str_buf ( id , & id_buf ) ,
nt_errstr ( status ) ) ;
smb_panic ( caller_location ) ;
return NULL ;
}
return d ;
}
2020-11-04 14:53:29 +03:00
NTTIME share_mode_changed_write_time ( struct share_mode_lock * lck )
{
2022-08-26 13:05:04 +03:00
struct share_mode_data * d = share_mode_lock_assert_private_data ( lck ) ;
return d - > changed_write_time ;
2020-11-04 14:53:29 +03:00
}
2022-08-30 08:55:03 +03:00
void share_mode_set_changed_write_time ( struct share_mode_lock * lck , struct timespec write_time )
{
struct file_id fileid = share_mode_lock_file_id ( lck ) ;
struct share_mode_data * d = share_mode_lock_assert_private_data ( lck ) ;
struct file_id_buf ftmp ;
struct timeval_buf tbuf ;
NTTIME nt = full_timespec_to_nt_time ( & write_time ) ;
DBG_INFO ( " %s id=%s \n " ,
timespec_string_buf ( & write_time , true , & tbuf ) ,
file_id_str_buf ( fileid , & ftmp ) ) ;
if ( d - > changed_write_time ! = nt ) {
d - > modified = true ;
d - > changed_write_time = nt ;
}
}
void share_mode_set_old_write_time ( struct share_mode_lock * lck , struct timespec write_time )
{
struct file_id fileid = share_mode_lock_file_id ( lck ) ;
struct share_mode_data * d = share_mode_lock_assert_private_data ( lck ) ;
struct file_id_buf ftmp ;
struct timeval_buf tbuf ;
NTTIME nt = full_timespec_to_nt_time ( & write_time ) ;
DBG_INFO ( " %s id=%s \n " ,
timespec_string_buf ( & write_time , true , & tbuf ) ,
file_id_str_buf ( fileid , & ftmp ) ) ;
if ( d - > changed_write_time ! = nt ) {
d - > modified = true ;
d - > old_write_time = nt ;
}
}
2020-11-04 15:42:03 +03:00
const char * share_mode_servicepath ( struct share_mode_lock * lck )
{
2022-08-26 13:05:04 +03:00
struct share_mode_data * d = share_mode_lock_assert_private_data ( lck ) ;
return d - > servicepath ;
2020-11-04 15:42:03 +03:00
}
2020-11-04 15:19:46 +03:00
char * share_mode_filename ( TALLOC_CTX * mem_ctx , struct share_mode_lock * lck )
{
2022-08-26 13:05:04 +03:00
struct share_mode_data * d = share_mode_lock_assert_private_data ( lck ) ;
2020-11-04 15:19:46 +03:00
bool has_stream = ( d - > stream_name ! = NULL ) ;
char * fname = NULL ;
fname = talloc_asprintf (
mem_ctx ,
" %s%s%s " ,
d - > base_name ,
has_stream ? " : " : " " ,
has_stream ? d - > stream_name : " " ) ;
return fname ;
}
2020-11-04 17:27:56 +03:00
char * share_mode_data_dump (
TALLOC_CTX * mem_ctx , struct share_mode_lock * lck )
{
2022-08-26 13:05:04 +03:00
struct share_mode_data * d = share_mode_lock_assert_private_data ( lck ) ;
2020-11-04 17:27:56 +03:00
struct ndr_print * p = talloc ( mem_ctx , struct ndr_print ) ;
char * ret = NULL ;
if ( p = = NULL ) {
return NULL ;
}
* p = ( struct ndr_print ) {
. print = ndr_print_string_helper ,
. depth = 1 ,
. private_data = talloc_strdup ( mem_ctx , " " ) ,
} ;
if ( p - > private_data = = NULL ) {
TALLOC_FREE ( p ) ;
return NULL ;
}
2022-08-26 13:05:04 +03:00
ndr_print_share_mode_data ( p , " SHARE_MODE_DATA " , d ) ;
2020-11-04 17:27:56 +03:00
ret = p - > private_data ;
TALLOC_FREE ( p ) ;
return ret ;
}
2020-11-05 17:39:51 +03:00
void share_mode_flags_get (
struct share_mode_lock * lck ,
uint32_t * access_mask ,
uint32_t * share_mode ,
uint32_t * lease_type )
{
2022-08-26 13:05:04 +03:00
struct share_mode_data * d = share_mode_lock_assert_private_data ( lck ) ;
uint16_t flags = d - > flags ;
2020-11-05 17:39:51 +03:00
if ( access_mask ! = NULL ) {
* access_mask =
( ( flags & SHARE_MODE_ACCESS_READ ) ?
FILE_READ_DATA : 0 ) |
( ( flags & SHARE_MODE_ACCESS_WRITE ) ?
FILE_WRITE_DATA : 0 ) |
( ( flags & SHARE_MODE_ACCESS_DELETE ) ?
DELETE_ACCESS : 0 ) ;
}
if ( share_mode ! = NULL ) {
* share_mode =
( ( flags & SHARE_MODE_SHARE_READ ) ?
FILE_SHARE_READ : 0 ) |
( ( flags & SHARE_MODE_SHARE_WRITE ) ?
FILE_SHARE_WRITE : 0 ) |
( ( flags & SHARE_MODE_SHARE_DELETE ) ?
FILE_SHARE_DELETE : 0 ) ;
}
if ( lease_type ! = NULL ) {
* lease_type =
( ( flags & SHARE_MODE_LEASE_READ ) ?
SMB2_LEASE_READ : 0 ) |
( ( flags & SHARE_MODE_LEASE_WRITE ) ?
SMB2_LEASE_WRITE : 0 ) |
( ( flags & SHARE_MODE_LEASE_HANDLE ) ?
SMB2_LEASE_HANDLE : 0 ) ;
}
}
void share_mode_flags_set (
struct share_mode_lock * lck ,
uint32_t access_mask ,
uint32_t share_mode ,
uint32_t lease_type ,
bool * modified )
{
2022-08-26 13:05:04 +03:00
struct share_mode_data * d = share_mode_lock_assert_private_data ( lck ) ;
2020-11-05 17:39:51 +03:00
uint16_t flags = 0 ;
flags | = ( access_mask & ( FILE_READ_DATA | FILE_EXECUTE ) ) ?
SHARE_MODE_ACCESS_READ : 0 ;
flags | = ( access_mask & ( FILE_WRITE_DATA | FILE_APPEND_DATA ) ) ?
SHARE_MODE_ACCESS_WRITE : 0 ;
flags | = ( access_mask & ( DELETE_ACCESS ) ) ?
SHARE_MODE_ACCESS_DELETE : 0 ;
flags | = ( share_mode & FILE_SHARE_READ ) ?
SHARE_MODE_SHARE_READ : 0 ;
flags | = ( share_mode & FILE_SHARE_WRITE ) ?
SHARE_MODE_SHARE_WRITE : 0 ;
flags | = ( share_mode & FILE_SHARE_DELETE ) ?
SHARE_MODE_SHARE_DELETE : 0 ;
flags | = ( lease_type & SMB2_LEASE_READ ) ?
SHARE_MODE_LEASE_READ : 0 ;
flags | = ( lease_type & SMB2_LEASE_WRITE ) ?
SHARE_MODE_LEASE_WRITE : 0 ;
flags | = ( lease_type & SMB2_LEASE_HANDLE ) ?
SHARE_MODE_LEASE_HANDLE : 0 ;
if ( d - > flags = = flags ) {
return ;
}
if ( modified ! = NULL ) {
* modified = true ;
}
d - > flags = flags ;
d - > modified = true ;
}
2019-11-04 14:57:35 +03:00
struct share_mode_watch_state {
bool blockerdead ;
struct server_id blocker ;
} ;
static void share_mode_watch_done ( struct tevent_req * subreq ) ;
struct tevent_req * share_mode_watch_send (
TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
2020-11-03 19:36:08 +03:00
struct share_mode_lock * lck ,
2019-11-04 14:57:35 +03:00
struct server_id blocker )
{
2022-08-26 13:08:03 +03:00
struct file_id id = share_mode_lock_file_id ( lck ) ;
TDB_DATA key = locking_key ( & id ) ;
2019-11-05 14:01:52 +03:00
struct tevent_req * req = NULL , * subreq = NULL ;
2019-11-04 14:57:35 +03:00
struct share_mode_watch_state * state = NULL ;
req = tevent_req_create (
mem_ctx , & state , struct share_mode_watch_state ) ;
if ( req = = NULL ) {
return NULL ;
}
2019-11-05 14:01:52 +03:00
subreq = g_lock_watch_data_send ( state , ev , lock_ctx , key , blocker ) ;
if ( tevent_req_nomem ( subreq , req ) ) {
2019-11-04 14:57:35 +03:00
return tevent_req_post ( req , ev ) ;
}
2019-11-05 14:01:52 +03:00
tevent_req_set_callback ( subreq , share_mode_watch_done , req ) ;
2019-11-04 14:57:35 +03:00
return req ;
}
static void share_mode_watch_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct share_mode_watch_state * state = tevent_req_data (
req , struct share_mode_watch_state ) ;
NTSTATUS status ;
2019-11-05 14:01:52 +03:00
status = g_lock_watch_data_recv (
2019-11-04 14:57:35 +03:00
subreq , & state - > blockerdead , & state - > blocker ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
tevent_req_done ( req ) ;
}
NTSTATUS share_mode_watch_recv (
struct tevent_req * req , bool * blockerdead , struct server_id * blocker )
{
struct share_mode_watch_state * state = tevent_req_data (
req , struct share_mode_watch_state ) ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
return status ;
}
if ( blockerdead ! = NULL ) {
* blockerdead = state - > blockerdead ;
}
if ( blocker ! = NULL ) {
* blocker = state - > blocker ;
}
return NT_STATUS_OK ;
}
2016-10-24 18:32:17 +03:00
struct fetch_share_mode_unlocked_state {
TALLOC_CTX * mem_ctx ;
2020-12-01 15:50:32 +03:00
struct file_id id ;
2016-10-24 18:32:17 +03:00
struct share_mode_lock * lck ;
} ;
2012-11-27 18:40:06 +04:00
static void fetch_share_mode_unlocked_parser (
2019-11-05 14:01:52 +03:00
struct server_id exclusive ,
size_t num_shared ,
2022-08-18 18:52:33 +03:00
const struct server_id * shared ,
2019-11-05 14:01:52 +03:00
const uint8_t * data ,
size_t datalen ,
void * private_data )
2012-11-27 18:40:06 +04:00
{
2016-10-24 18:32:17 +03:00
struct fetch_share_mode_unlocked_state * state = private_data ;
2020-04-14 17:51:15 +03:00
struct locking_tdb_data ltdb = { 0 } ;
2012-11-27 18:40:06 +04:00
2020-04-14 17:51:15 +03:00
if ( datalen ! = 0 ) {
bool ok = locking_tdb_data_get ( & ltdb , data , datalen ) ;
if ( ! ok ) {
DBG_DEBUG ( " locking_tdb_data_get failed \n " ) ;
return ;
}
}
if ( ltdb . share_mode_data_len = = 0 ) {
2016-07-20 13:36:24 +03:00
/* Likely a ctdb tombstone record, ignore it */
return ;
}
2016-10-24 18:32:17 +03:00
state - > lck = talloc ( state - > mem_ctx , struct share_mode_lock ) ;
if ( state - > lck = = NULL ) {
DEBUG ( 0 , ( " talloc failed \n " ) ) ;
return ;
}
2022-08-26 11:40:44 +03:00
state - > lck - > id = state - > id ;
2016-10-24 18:32:17 +03:00
2022-08-26 11:44:59 +03:00
state - > lck - > cached_data = parse_share_modes (
2020-04-14 17:51:15 +03:00
state - > lck ,
2020-12-01 15:50:32 +03:00
state - > id ,
2020-04-14 17:51:15 +03:00
ltdb . share_mode_data_buf ,
ltdb . share_mode_data_len ) ;
2022-08-26 11:44:59 +03:00
if ( state - > lck - > cached_data = = NULL ) {
2020-04-14 17:51:15 +03:00
DBG_DEBUG ( " parse_share_modes failed \n " ) ;
TALLOC_FREE ( state - > lck ) ;
}
2012-11-27 18:40:06 +04:00
}
2012-01-13 02:46:45 +04:00
/*******************************************************************
Get a share_mode_lock without locking the database or reference
counting . Used by smbstatus to display existing share modes .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2012-01-10 17:13:49 +04:00
struct share_mode_lock * fetch_share_mode_unlocked ( TALLOC_CTX * mem_ctx ,
2013-03-12 18:10:51 +04:00
struct file_id id )
2012-01-10 17:13:49 +04:00
{
2019-11-05 14:01:52 +03:00
struct fetch_share_mode_unlocked_state state = {
. mem_ctx = mem_ctx ,
2020-12-01 15:50:32 +03:00
. id = id ,
2019-11-05 14:01:52 +03:00
} ;
2020-12-01 15:50:32 +03:00
TDB_DATA key = locking_key ( & id ) ;
2012-01-10 17:13:49 +04:00
NTSTATUS status ;
2019-11-05 14:01:52 +03:00
status = g_lock_dump (
2020-12-01 15:50:32 +03:00
lock_ctx , key , fetch_share_mode_unlocked_parser , & state ) ;
2016-10-24 18:32:17 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2019-11-05 14:01:52 +03:00
DBG_DEBUG ( " g_lock_dump failed: %s \n " , nt_errstr ( status ) ) ;
2012-01-10 17:13:49 +04:00
return NULL ;
}
2016-10-24 18:32:17 +03:00
return state . lck ;
2012-01-10 17:13:49 +04:00
}
2017-01-04 10:00:29 +03:00
struct fetch_share_mode_state {
struct file_id id ;
2019-11-05 14:01:52 +03:00
struct share_mode_lock * lck ;
NTSTATUS status ;
2017-01-04 10:00:29 +03:00
} ;
2019-11-05 14:01:52 +03:00
static void fetch_share_mode_fn (
struct server_id exclusive ,
size_t num_shared ,
2022-08-18 18:52:33 +03:00
const struct server_id * shared ,
2019-11-05 14:01:52 +03:00
const uint8_t * data ,
size_t datalen ,
void * private_data ) ;
static void fetch_share_mode_done ( struct tevent_req * subreq ) ;
2017-01-04 10:00:29 +03:00
/**
* @ brief Get a share_mode_lock without locking or refcounting
*
* This can be used in a clustered Samba environment where the async dbwrap
* request is sent over a socket to the local ctdbd . If the send queue is full
* and the caller was issuing multiple async dbwrap requests in a loop , the
* caller knows it ' s probably time to stop sending requests for now and try
* again later .
*
* @ param [ in ] mem_ctx The talloc memory context to use .
*
* @ param [ in ] ev The event context to work on .
*
* @ param [ in ] id The file id for the locking . tdb key
*
* @ param [ out ] queued This boolean out parameter tells the caller whether the
* async request is blocked in a full send queue :
*
* false : = request is dispatched
*
* true : = send queue is full , request waiting to be
* dispatched
*
* @ return The new async request , NULL on error .
* */
struct tevent_req * fetch_share_mode_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct file_id id ,
bool * queued )
{
2019-11-05 14:01:52 +03:00
struct tevent_req * req = NULL , * subreq = NULL ;
2017-01-04 10:00:29 +03:00
struct fetch_share_mode_state * state = NULL ;
* queued = false ;
req = tevent_req_create ( mem_ctx , & state ,
struct fetch_share_mode_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > id = id ;
2019-11-05 14:01:52 +03:00
subreq = g_lock_dump_send (
state ,
ev ,
lock_ctx ,
locking_key ( & id ) ,
fetch_share_mode_fn ,
state ) ;
2017-01-04 10:00:29 +03:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
}
tevent_req_set_callback ( subreq , fetch_share_mode_done , req ) ;
2019-11-05 14:01:52 +03:00
return req ;
}
static void fetch_share_mode_fn (
struct server_id exclusive ,
size_t num_shared ,
2022-08-18 18:52:33 +03:00
const struct server_id * shared ,
2019-11-05 14:01:52 +03:00
const uint8_t * data ,
size_t datalen ,
void * private_data )
{
struct fetch_share_mode_state * state = talloc_get_type_abort (
private_data , struct fetch_share_mode_state ) ;
2020-04-14 17:51:15 +03:00
struct locking_tdb_data ltdb = { 0 } ;
if ( datalen ! = 0 ) {
bool ok = locking_tdb_data_get ( & ltdb , data , datalen ) ;
if ( ! ok ) {
DBG_DEBUG ( " locking_tdb_data_get failed \n " ) ;
return ;
}
}
if ( ltdb . share_mode_data_len = = 0 ) {
/* Likely a ctdb tombstone record, ignore it */
return ;
}
2017-01-04 10:00:29 +03:00
2019-11-05 14:01:52 +03:00
state - > lck = talloc ( state , struct share_mode_lock ) ;
if ( state - > lck = = NULL ) {
DBG_WARNING ( " talloc failed \n " ) ;
state - > status = NT_STATUS_NO_MEMORY ;
return ;
2017-01-04 10:00:29 +03:00
}
2022-08-26 11:40:44 +03:00
state - > lck - > id = state - > id ,
2019-11-05 14:01:52 +03:00
2022-08-26 11:44:59 +03:00
state - > lck - > cached_data = parse_share_modes (
2020-04-14 17:51:15 +03:00
state - > lck ,
2020-12-01 15:50:32 +03:00
state - > id ,
2020-04-14 17:51:15 +03:00
ltdb . share_mode_data_buf ,
ltdb . share_mode_data_len ) ;
2022-08-26 11:44:59 +03:00
if ( state - > lck - > cached_data = = NULL ) {
2020-04-14 17:51:15 +03:00
DBG_DEBUG ( " parse_share_modes failed \n " ) ;
2020-12-18 15:04:47 +03:00
state - > status = NT_STATUS_INTERNAL_DB_CORRUPTION ;
2020-04-14 17:51:15 +03:00
TALLOC_FREE ( state - > lck ) ;
2020-12-18 15:04:47 +03:00
return ;
2020-04-14 17:51:15 +03:00
}
2017-01-04 10:00:29 +03:00
}
static void fetch_share_mode_done ( struct tevent_req * subreq )
{
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
2019-11-05 14:01:52 +03:00
struct fetch_share_mode_state * state = tevent_req_data (
req , struct fetch_share_mode_state ) ;
2017-01-04 10:00:29 +03:00
NTSTATUS status ;
2019-11-05 14:01:52 +03:00
status = g_lock_dump_recv ( subreq ) ;
2017-01-04 10:00:29 +03:00
TALLOC_FREE ( subreq ) ;
if ( tevent_req_nterror ( req , status ) ) {
return ;
}
2019-11-05 14:01:52 +03:00
if ( tevent_req_nterror ( req , state - > status ) ) {
return ;
}
2017-01-04 10:00:29 +03:00
tevent_req_done ( req ) ;
}
NTSTATUS fetch_share_mode_recv ( struct tevent_req * req ,
TALLOC_CTX * mem_ctx ,
struct share_mode_lock * * _lck )
{
struct fetch_share_mode_state * state = tevent_req_data (
req , struct fetch_share_mode_state ) ;
struct share_mode_lock * lck = NULL ;
NTSTATUS status ;
if ( tevent_req_is_nterror ( req , & status ) ) {
tevent_req_received ( req ) ;
return status ;
}
2020-04-14 17:51:15 +03:00
if ( state - > lck = = NULL ) {
2017-01-04 10:00:29 +03:00
tevent_req_received ( req ) ;
return NT_STATUS_NOT_FOUND ;
}
2019-11-05 14:01:52 +03:00
lck = talloc_move ( mem_ctx , & state - > lck ) ;
2017-01-04 10:00:29 +03:00
if ( DEBUGLEVEL > = 10 ) {
DBG_DEBUG ( " share_mode_data: \n " ) ;
2022-08-26 11:44:59 +03:00
NDR_PRINT_DEBUG ( share_mode_data , lck - > cached_data ) ;
2017-01-04 10:00:29 +03:00
}
* _lck = lck ;
tevent_req_received ( req ) ;
return NT_STATUS_OK ;
}
2014-09-23 07:45:49 +04:00
struct share_mode_forall_state {
2019-11-05 14:01:52 +03:00
TDB_DATA key ;
int ( * fn ) ( struct file_id fid ,
const struct share_mode_data * data ,
2014-09-23 07:45:49 +04:00
void * private_data ) ;
2012-01-10 17:13:49 +04:00
void * private_data ;
} ;
2019-11-05 14:01:52 +03:00
static void share_mode_forall_dump_fn (
struct server_id exclusive ,
size_t num_shared ,
2022-08-18 18:52:33 +03:00
const struct server_id * shared ,
2019-11-05 14:01:52 +03:00
const uint8_t * data ,
size_t datalen ,
void * private_data )
2012-01-10 17:13:49 +04:00
{
2019-11-05 14:01:52 +03:00
struct share_mode_forall_state * state = private_data ;
2014-09-23 07:45:49 +04:00
struct file_id fid ;
2020-04-14 17:51:15 +03:00
struct locking_tdb_data ltdb = { 0 } ;
bool ok ;
2019-11-05 14:01:52 +03:00
struct share_mode_data * d ;
2012-01-10 17:13:49 +04:00
2019-11-05 14:01:52 +03:00
if ( state - > key . dsize ! = sizeof ( fid ) ) {
DBG_DEBUG ( " Got invalid key length %zu \n " , state - > key . dsize ) ;
return ;
2014-09-23 07:45:49 +04:00
}
2019-11-05 14:01:52 +03:00
memcpy ( & fid , state - > key . dptr , sizeof ( fid ) ) ;
2012-01-10 17:13:49 +04:00
2020-04-14 17:51:15 +03:00
ok = locking_tdb_data_get ( & ltdb , data , datalen ) ;
if ( ! ok ) {
DBG_DEBUG ( " locking_tdb_data_get() failed \n " ) ;
return ;
}
d = parse_share_modes (
talloc_tos ( ) ,
2020-12-01 15:50:32 +03:00
fid ,
2020-04-14 17:51:15 +03:00
ltdb . share_mode_data_buf ,
ltdb . share_mode_data_len ) ;
2012-01-10 17:13:49 +04:00
if ( d = = NULL ) {
2019-11-05 14:01:52 +03:00
DBG_DEBUG ( " parse_share_modes() failed \n " ) ;
return ;
2012-01-10 17:13:49 +04:00
}
2019-11-05 14:01:52 +03:00
state - > fn ( fid , d , state - > private_data ) ;
TALLOC_FREE ( d ) ;
}
2015-10-12 13:28:04 +03:00
2019-11-05 14:01:52 +03:00
static int share_mode_forall_fn ( TDB_DATA key , void * private_data )
{
struct share_mode_forall_state * state = private_data ;
NTSTATUS status ;
2014-09-23 07:45:49 +04:00
2019-11-05 14:01:52 +03:00
state - > key = key ;
2014-09-23 07:45:49 +04:00
2019-11-05 14:01:52 +03:00
status = g_lock_dump (
lock_ctx , key , share_mode_forall_dump_fn , private_data ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-09-10 21:39:19 +03:00
DBG_GET_SHARE_MODE_LOCK ( status ,
" g_lock_dump failed: %s \n " ,
nt_errstr ( status ) ) ;
2019-11-05 14:01:52 +03:00
}
return 0 ;
2014-09-23 07:45:49 +04:00
}
int share_mode_forall ( int ( * fn ) ( struct file_id fid ,
const struct share_mode_data * data ,
void * private_data ) ,
void * private_data )
{
struct share_mode_forall_state state = {
. fn = fn ,
. private_data = private_data
} ;
2019-11-05 14:01:52 +03:00
int ret ;
2014-09-23 07:45:49 +04:00
2019-11-05 14:01:52 +03:00
if ( lock_ctx = = NULL ) {
2014-09-23 07:45:49 +04:00
return 0 ;
}
2019-11-05 14:01:52 +03:00
ret = g_lock_locks (
lock_ctx , share_mode_forall_fn , & state ) ;
if ( ret < 0 ) {
2022-09-10 21:39:19 +03:00
DBG_ERR ( " g_lock_locks failed \n " ) ;
2014-09-23 07:45:49 +04:00
}
2019-11-05 14:01:52 +03:00
return ret ;
2014-09-23 07:45:49 +04:00
}
struct share_entry_forall_state {
2019-09-12 14:03:53 +03:00
struct file_id fid ;
const struct share_mode_data * data ;
2018-07-25 17:56:35 +03:00
int ( * fn ) ( struct file_id fid ,
const struct share_mode_data * data ,
const struct share_mode_entry * entry ,
2014-09-24 22:46:15 +04:00
void * private_data ) ;
2014-09-23 07:45:49 +04:00
void * private_data ;
2019-09-12 14:03:53 +03:00
int ret ;
2014-09-23 07:45:49 +04:00
} ;
2019-09-12 14:03:53 +03:00
static bool share_entry_traverse_walker (
struct share_mode_entry * e ,
bool * modified ,
void * private_data )
{
struct share_entry_forall_state * state = private_data ;
state - > ret = state - > fn (
state - > fid , state - > data , e , state - > private_data ) ;
return ( state - > ret ! = 0 ) ;
}
2014-09-23 07:45:49 +04:00
static int share_entry_traverse_fn ( struct file_id fid ,
const struct share_mode_data * data ,
void * private_data )
{
struct share_entry_forall_state * state = private_data ;
2019-09-12 14:03:53 +03:00
struct share_mode_lock lck = {
2022-08-26 11:40:44 +03:00
. id = fid ,
2022-08-26 11:44:59 +03:00
. cached_data = discard_const_p ( struct share_mode_data , data )
2019-09-12 14:03:53 +03:00
} ;
bool ok ;
2014-09-23 07:45:49 +04:00
2019-09-12 14:03:53 +03:00
state - > fid = fid ;
state - > data = data ;
2014-09-24 22:46:15 +04:00
2019-09-12 14:03:53 +03:00
ok = share_mode_forall_entries (
& lck , share_entry_traverse_walker , state ) ;
if ( ! ok ) {
2022-09-10 21:39:19 +03:00
DBG_ERR ( " share_mode_forall_entries failed \n " ) ;
2019-09-12 14:03:53 +03:00
return false ;
2014-09-23 07:45:49 +04:00
}
2012-01-10 17:13:49 +04:00
2019-09-12 14:03:53 +03:00
return state - > ret ;
2012-01-10 17:13:49 +04:00
}
/*******************************************************************
Call the specified function on each entry under management by the
share mode system .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2018-07-25 17:56:35 +03:00
int share_entry_forall ( int ( * fn ) ( struct file_id fid ,
const struct share_mode_data * data ,
const struct share_mode_entry * entry ,
void * private_data ) ,
void * private_data )
2012-01-10 17:13:49 +04:00
{
2014-09-23 07:45:49 +04:00
struct share_entry_forall_state state = {
. fn = fn , . private_data = private_data } ;
2012-01-10 17:13:49 +04:00
2014-09-23 07:45:49 +04:00
return share_mode_forall ( share_entry_traverse_fn , & state ) ;
2012-01-10 17:13:49 +04:00
}
2013-03-13 14:35:37 +04:00
2019-09-16 20:01:36 +03:00
static int share_mode_entry_cmp (
struct server_id pid1 ,
uint64_t share_file_id1 ,
struct server_id pid2 ,
uint64_t share_file_id2 )
{
int cmp ;
cmp = server_id_cmp ( & pid1 , & pid2 ) ;
if ( cmp ! = 0 ) {
return cmp ;
}
if ( share_file_id1 ! = share_file_id2 ) {
return ( share_file_id1 < share_file_id2 ) ? - 1 : 1 ;
}
return 0 ;
}
static size_t share_mode_entry_find (
2020-04-24 16:58:59 +03:00
const uint8_t * data ,
2019-09-16 20:01:36 +03:00
size_t num_share_modes ,
struct server_id pid ,
uint64_t share_file_id ,
struct share_mode_entry * e ,
bool * match )
{
ssize_t left , right , middle ;
2020-06-29 11:42:09 +03:00
* match = false ;
2019-09-16 20:01:36 +03:00
if ( num_share_modes = = 0 ) {
return 0 ;
}
left = 0 ;
right = ( num_share_modes - 1 ) ;
while ( left < = right ) {
2020-04-23 18:00:25 +03:00
const uint8_t * middle_ptr = NULL ;
2019-09-16 20:01:36 +03:00
int cmp ;
bool ok ;
middle = left + ( ( right - left ) / 2 ) ;
2020-04-23 18:00:25 +03:00
middle_ptr = data + middle * SHARE_MODE_ENTRY_SIZE ;
2019-09-16 20:01:36 +03:00
2020-04-23 18:00:25 +03:00
DBG_DEBUG ( " left=%zu, right=%zu, middle=%zu, middle_ptr=%p \n " ,
2019-09-16 20:01:36 +03:00
left ,
right ,
2020-04-23 18:00:25 +03:00
middle ,
middle_ptr ) ;
2019-09-16 20:01:36 +03:00
2020-04-23 18:00:25 +03:00
ok = share_mode_entry_get ( middle_ptr , e ) ;
2019-09-16 20:01:36 +03:00
if ( ! ok ) {
DBG_DEBUG ( " share_mode_entry_get failed \n " ) ;
2020-06-29 11:42:09 +03:00
return 0 ;
2019-09-16 20:01:36 +03:00
}
cmp = share_mode_entry_cmp (
e - > pid , e - > share_file_id , pid , share_file_id ) ;
if ( cmp = = 0 ) {
* match = true ;
return middle ;
}
if ( cmp < 0 ) {
right = middle - 1 ;
} else {
left = middle + 1 ;
}
}
return left ;
}
2020-04-14 17:51:15 +03:00
bool set_share_mode ( struct share_mode_lock * lck ,
struct files_struct * fsp ,
uid_t uid ,
uint64_t mid ,
uint16_t op_type ,
2022-08-10 11:27:15 +03:00
const struct smb2_lease_key * lease_key ,
2020-04-14 17:51:15 +03:00
uint32_t share_access ,
uint32_t access_mask )
2019-09-16 20:01:36 +03:00
{
2022-08-26 13:05:04 +03:00
struct share_mode_data * d = share_mode_lock_assert_private_data ( lck ) ;
2020-04-14 17:51:15 +03:00
TDB_DATA key = locking_key ( & d - > id ) ;
struct server_id my_pid = messaging_server_id (
fsp - > conn - > sconn - > msg_ctx ) ;
struct locking_tdb_data * ltdb = NULL ;
size_t idx ;
struct share_mode_entry e = { . pid . pid = 0 } ;
struct share_mode_entry_buf e_buf ;
NTSTATUS status ;
2019-09-16 20:01:36 +03:00
bool ok , found ;
TDB_DATA dbufs [ 3 ] ;
size_t num_dbufs = 0 ;
2020-04-14 17:51:15 +03:00
status = locking_tdb_data_fetch ( key , talloc_tos ( ) , & ltdb ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-09-10 21:39:19 +03:00
DBG_ERR ( " locking_tdb_data_fetch failed: %s \n " ,
nt_errstr ( status ) ) ;
2020-04-14 17:51:15 +03:00
return false ;
2019-09-16 20:01:36 +03:00
}
2020-04-14 17:51:15 +03:00
DBG_DEBUG ( " num_share_modes=%zu \n " , ltdb - > num_share_entries ) ;
2019-09-16 20:01:36 +03:00
idx = share_mode_entry_find (
2020-04-14 17:51:15 +03:00
ltdb - > share_entries ,
ltdb - > num_share_entries ,
my_pid ,
2020-09-28 11:35:32 +03:00
fh_get_gen_id ( fsp - > fh ) ,
2020-04-14 17:51:15 +03:00
& e ,
2019-09-16 20:01:36 +03:00
& found ) ;
if ( found ) {
DBG_WARNING ( " Found duplicate share mode \n " ) ;
2020-04-14 17:51:15 +03:00
status = NT_STATUS_INTERNAL_DB_CORRUPTION ;
goto done ;
}
e = ( struct share_mode_entry ) {
. pid = my_pid ,
. share_access = share_access ,
2020-10-23 18:47:46 +03:00
. private_options = fh_get_private_options ( fsp - > fh ) ,
2020-04-14 17:51:15 +03:00
. access_mask = access_mask ,
. op_mid = mid ,
. op_type = op_type ,
. time . tv_sec = fsp - > open_time . tv_sec ,
. time . tv_usec = fsp - > open_time . tv_usec ,
2020-09-28 11:35:32 +03:00
. share_file_id = fh_get_gen_id ( fsp - > fh ) ,
2020-04-14 17:51:15 +03:00
. uid = ( uint32_t ) uid ,
. flags = ( fsp - > posix_flags & FSP_POSIX_FLAGS_OPEN ) ?
SHARE_MODE_FLAG_POSIX_OPEN : 0 ,
. name_hash = fsp - > name_hash ,
} ;
if ( op_type = = LEASE_OPLOCK ) {
const struct GUID * client_guid = fsp_client_guid ( fsp ) ;
e . client_guid = * client_guid ;
2022-08-10 11:27:15 +03:00
e . lease_key = * lease_key ;
2020-04-14 17:51:15 +03:00
}
ok = share_mode_entry_put ( & e , & e_buf ) ;
if ( ! ok ) {
DBG_DEBUG ( " share_mode_entry_put failed \n " ) ;
status = NT_STATUS_INTERNAL_ERROR ;
goto done ;
2019-09-16 20:01:36 +03:00
}
DBG_DEBUG ( " idx=%zu, found=%d \n " , idx , ( int ) found ) ;
if ( idx > 0 ) {
dbufs [ num_dbufs ] = ( TDB_DATA ) {
2020-04-14 17:51:15 +03:00
. dptr = discard_const_p ( uint8_t , ltdb - > share_entries ) ,
2019-09-16 20:01:36 +03:00
. dsize = idx * SHARE_MODE_ENTRY_SIZE ,
} ;
num_dbufs + = 1 ;
}
dbufs [ num_dbufs ] = ( TDB_DATA ) {
2020-04-14 17:51:15 +03:00
. dptr = e_buf . buf , . dsize = SHARE_MODE_ENTRY_SIZE ,
2019-09-16 20:01:36 +03:00
} ;
num_dbufs + = 1 ;
2020-04-14 17:51:15 +03:00
if ( idx < ltdb - > num_share_entries ) {
size_t num_after_idx = ( ltdb - > num_share_entries - idx ) ;
2019-09-16 20:01:36 +03:00
dbufs [ num_dbufs ] = ( TDB_DATA ) {
2020-04-14 17:51:15 +03:00
. dptr = discard_const_p ( uint8_t , ltdb - > share_entries ) +
idx * SHARE_MODE_ENTRY_SIZE ,
. dsize = num_after_idx * SHARE_MODE_ENTRY_SIZE ,
2019-09-16 20:01:36 +03:00
} ;
num_dbufs + = 1 ;
}
{
size_t i ;
for ( i = 0 ; i < num_dbufs ; i + + ) {
DBG_DEBUG ( " dbufs[%zu]=(%p, %zu) \n " ,
i ,
dbufs [ i ] . dptr ,
dbufs [ i ] . dsize ) ;
}
}
2020-04-14 17:51:15 +03:00
if ( num_dbufs = = 1 ) {
/*
* Storing a fresh record with just one share entry
*/
d - > modified = true ;
}
2019-09-16 20:01:36 +03:00
2020-04-14 17:51:15 +03:00
/*
* If there was any existing data in
* ltdb - > share_entries , it ' s now been
* moved and we ' ve split it into :
*
* num_dbufs = 3
* dbufs [ 0 ] - > old sorted data less than new_entry
* dbufs [ 1 ] - > new_share_mode_entry
* dbufs [ 2 ] - > old sorted_data greater than new entry .
*
* So the old data inside ltdb - > share_entries is
* no longer valid .
*
* If we ' re storing a brand new entry the
* dbufs look like :
*
* num_dbufs = 1
* dbufs [ 0 ] - > new_share_mode_entry
*
* Either way we must set ltdb - > share_entries = NULL
* and ltdb - > num_share_entries = 0 so that
* locking_tdb_data_store ( ) doesn ' t use it to
* store any data . It ' s no longer there .
*/
2019-09-16 20:01:36 +03:00
2020-04-14 17:51:15 +03:00
ltdb - > share_entries = NULL ;
ltdb - > num_share_entries = 0 ;
2019-09-16 20:01:36 +03:00
2022-08-22 17:53:38 +03:00
status = share_mode_data_ltdb_store ( d , key , ltdb , dbufs , num_dbufs ) ;
2019-09-16 20:01:36 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-08-22 17:53:38 +03:00
DBG_ERR ( " share_mode_data_ltdb_store failed: %s \n " ,
nt_errstr ( status ) ) ;
2019-12-10 20:15:40 +03:00
}
2020-04-14 17:51:15 +03:00
done :
TALLOC_FREE ( ltdb ) ;
return NT_STATUS_IS_OK ( status ) ;
2019-09-16 20:01:36 +03:00
}
2020-04-14 17:51:15 +03:00
static bool share_mode_for_one_entry (
2019-09-16 20:01:36 +03:00
bool ( * fn ) ( struct share_mode_entry * e ,
bool * modified ,
2020-04-14 17:51:15 +03:00
void * private_data ) ,
void * private_data ,
2019-09-16 20:01:36 +03:00
size_t * i ,
2020-04-27 14:16:00 +03:00
uint8_t * data ,
2019-09-16 20:01:36 +03:00
size_t * num_share_modes ,
bool * writeback )
{
DATA_BLOB blob = {
2020-04-27 14:16:00 +03:00
. data = data + ( * i ) * SHARE_MODE_ENTRY_SIZE ,
2019-09-16 20:01:36 +03:00
. length = SHARE_MODE_ENTRY_SIZE ,
} ;
struct share_mode_entry e = { . pid . pid = 0 } ;
enum ndr_err_code ndr_err = NDR_ERR_SUCCESS ;
bool modified = false ;
bool stop = false ;
struct server_id e_pid ;
uint64_t e_share_file_id ;
ndr_err = ndr_pull_struct_blob_all_noalloc (
& blob ,
& e ,
( ndr_pull_flags_fn_t ) ndr_pull_share_mode_entry ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DBG_WARNING ( " ndr_pull_share_mode_entry failed \n " ) ;
* i + = 1 ;
return false ;
}
if ( DEBUGLEVEL > = 10 ) {
DBG_DEBUG ( " entry[%zu]: \n " , * i ) ;
NDR_PRINT_DEBUG ( share_mode_entry , & e ) ;
}
e_pid = e . pid ;
e_share_file_id = e . share_file_id ;
2020-04-14 17:51:15 +03:00
stop = fn ( & e , & modified , private_data ) ;
2019-09-16 20:01:36 +03:00
DBG_DEBUG ( " entry[%zu]: modified=%d, e.stale=%d \n " ,
* i ,
( int ) modified ,
( int ) e . stale ) ;
2020-08-28 16:56:35 +03:00
if ( e . stale ) {
2020-08-28 16:56:35 +03:00
if ( DEBUGLEVEL > = 10 ) {
DBG_DEBUG ( " share_mode_entry: \n " ) ;
NDR_PRINT_DEBUG ( share_mode_entry , & e ) ;
}
2020-08-28 16:56:35 +03:00
if ( * i < * num_share_modes ) {
memmove ( blob . data ,
blob . data + SHARE_MODE_ENTRY_SIZE ,
( * num_share_modes - * i - 1 ) *
SHARE_MODE_ENTRY_SIZE ) ;
}
* num_share_modes - = 1 ;
* writeback = true ;
return stop ;
}
2019-09-16 20:01:36 +03:00
if ( modified ) {
if ( DEBUGLEVEL > = 10 ) {
DBG_DEBUG ( " share_mode_entry: \n " ) ;
NDR_PRINT_DEBUG ( share_mode_entry , & e ) ;
}
/*
* Make sure sorting order is kept intact
*/
SMB_ASSERT ( server_id_equal ( & e_pid , & e . pid ) ) ;
SMB_ASSERT ( e_share_file_id = = e . share_file_id ) ;
ndr_err = ndr_push_struct_into_fixed_blob (
& blob ,
& e ,
( ndr_push_flags_fn_t )
ndr_push_share_mode_entry ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DBG_WARNING ( " ndr_push_share_mode_entry "
" failed: %s \n " ,
ndr_errstr ( ndr_err ) ) ;
/*
* Not much we can do , just ignore it
*/
}
* i + = 1 ;
* writeback = true ;
return stop ;
}
if ( stop ) {
return true ;
}
* i + = 1 ;
return false ;
}
2020-04-14 17:51:15 +03:00
bool share_mode_forall_entries (
struct share_mode_lock * lck ,
bool ( * fn ) ( struct share_mode_entry * e ,
bool * modified ,
void * private_data ) ,
2019-10-23 12:34:47 +03:00
void * private_data )
2019-09-16 20:01:36 +03:00
{
2022-08-26 13:09:06 +03:00
struct file_id id = share_mode_lock_file_id ( lck ) ;
struct share_mode_data * d = NULL ;
TDB_DATA key = locking_key ( & id ) ;
2020-04-14 17:51:15 +03:00
struct locking_tdb_data * ltdb = NULL ;
uint8_t * share_entries = NULL ;
size_t num_share_entries ;
2019-09-16 20:01:36 +03:00
bool writeback = false ;
NTSTATUS status ;
bool stop = false ;
size_t i ;
2022-08-26 13:09:06 +03:00
status = share_mode_lock_access_private_data ( lck , & d ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
struct file_id_buf id_buf ;
/* Any error recovery possible here ? */
DBG_ERR ( " share_mode_lock_access_private_data() failed for "
" %s - %s \n " ,
file_id_str_buf ( id , & id_buf ) ,
nt_errstr ( status ) ) ;
return false ;
}
2020-04-14 17:51:15 +03:00
status = locking_tdb_data_fetch ( key , talloc_tos ( ) , & ltdb ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-09-10 21:39:19 +03:00
DBG_ERR ( " locking_tdb_data_fetch failed: %s \n " ,
nt_errstr ( status ) ) ;
2020-04-14 17:51:15 +03:00
return false ;
2019-09-16 20:01:36 +03:00
}
2020-04-14 17:51:15 +03:00
DBG_DEBUG ( " num_share_modes=%zu \n " , ltdb - > num_share_entries ) ;
2019-09-16 20:01:36 +03:00
2020-04-14 17:51:15 +03:00
num_share_entries = ltdb - > num_share_entries ;
share_entries = discard_const_p ( uint8_t , ltdb - > share_entries ) ;
2019-09-16 20:01:36 +03:00
i = 0 ;
2020-04-14 17:51:15 +03:00
while ( i < num_share_entries ) {
2019-09-16 20:01:36 +03:00
stop = share_mode_for_one_entry (
2020-04-14 17:51:15 +03:00
fn ,
private_data ,
& i ,
share_entries ,
& num_share_entries ,
& writeback ) ;
2019-09-16 20:01:36 +03:00
if ( stop ) {
break ;
}
}
2020-04-14 17:51:15 +03:00
DBG_DEBUG ( " num_share_entries=%zu, writeback=%d \n " ,
num_share_entries ,
2019-09-16 20:01:36 +03:00
( int ) writeback ) ;
if ( ! writeback ) {
2022-09-04 02:35:06 +03:00
TALLOC_FREE ( ltdb ) ;
2020-04-14 17:51:15 +03:00
return true ;
2019-09-16 20:01:36 +03:00
}
2020-04-14 17:51:15 +03:00
if ( ( ltdb - > num_share_entries ! = 0 ) & & ( num_share_entries = = 0 ) ) {
/*
2020-12-03 19:16:25 +03:00
* This routine wiped all share entries , let
* share_mode_data_store ( ) delete the record
2020-04-14 17:51:15 +03:00
*/
d - > modified = true ;
2019-09-16 20:01:36 +03:00
}
2020-04-14 17:51:15 +03:00
ltdb - > num_share_entries = num_share_entries ;
ltdb - > share_entries = share_entries ;
2019-09-16 20:01:36 +03:00
2022-08-22 17:53:38 +03:00
status = share_mode_data_ltdb_store ( d , key , ltdb , NULL , 0 ) ;
2022-09-04 02:35:06 +03:00
TALLOC_FREE ( ltdb ) ;
2019-09-16 20:01:36 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-08-22 17:53:38 +03:00
DBG_ERR ( " share_mode_data_ltdb_store failed: %s \n " ,
2022-09-10 21:39:19 +03:00
nt_errstr ( status ) ) ;
2019-09-16 20:01:36 +03:00
return false ;
}
2020-04-14 17:51:15 +03:00
return true ;
2019-09-16 20:01:36 +03:00
}
2019-11-29 17:45:22 +03:00
struct share_mode_count_entries_state {
size_t num_share_modes ;
NTSTATUS status ;
} ;
static void share_mode_count_entries_fn (
2020-04-14 17:51:15 +03:00
struct server_id exclusive ,
size_t num_shared ,
2022-08-18 18:52:33 +03:00
const struct server_id * shared ,
2020-04-14 17:51:15 +03:00
const uint8_t * data ,
size_t datalen ,
void * private_data )
2019-11-29 17:45:22 +03:00
{
struct share_mode_count_entries_state * state = private_data ;
2020-04-14 17:51:15 +03:00
struct locking_tdb_data ltdb = { 0 } ;
bool ok ;
2019-11-29 17:45:22 +03:00
2020-04-14 17:51:15 +03:00
ok = locking_tdb_data_get ( & ltdb , data , datalen ) ;
if ( ! ok ) {
DBG_WARNING ( " locking_tdb_data_get failed for %zu \n " , datalen ) ;
2019-11-29 17:45:22 +03:00
state - > status = NT_STATUS_INTERNAL_DB_CORRUPTION ;
return ;
}
2020-04-14 17:51:15 +03:00
state - > num_share_modes = ltdb . num_share_entries ;
2019-11-29 17:45:22 +03:00
state - > status = NT_STATUS_OK ;
}
NTSTATUS share_mode_count_entries ( struct file_id fid , size_t * num_share_modes )
{
struct share_mode_count_entries_state state = {
. status = NT_STATUS_NOT_FOUND ,
} ;
NTSTATUS status ;
2020-04-14 17:51:15 +03:00
status = g_lock_dump (
lock_ctx ,
2019-11-29 17:45:22 +03:00
locking_key ( & fid ) ,
share_mode_count_entries_fn ,
& state ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2020-04-14 17:51:15 +03:00
DBG_DEBUG ( " g_lock_dump failed: %s \n " ,
2019-11-29 17:45:22 +03:00
nt_errstr ( status ) ) ;
return status ;
}
if ( ! NT_STATUS_IS_OK ( state . status ) ) {
2020-04-14 17:51:15 +03:00
DBG_DEBUG ( " share_mode_count_entries_fn failed: %s \n " ,
2019-11-29 17:45:22 +03:00
nt_errstr ( state . status ) ) ;
return state . status ;
}
* num_share_modes = state . num_share_modes ;
return NT_STATUS_OK ;
}
2020-04-14 17:51:15 +03:00
static bool share_mode_entry_do (
2022-08-26 13:10:55 +03:00
struct share_mode_data * d ,
2020-04-14 17:51:15 +03:00
struct server_id pid ,
uint64_t share_file_id ,
2019-09-16 20:01:36 +03:00
void ( * fn ) ( struct share_mode_entry * e ,
2019-12-03 12:36:21 +03:00
size_t num_share_modes ,
2019-09-16 20:01:36 +03:00
bool * modified ,
2020-04-14 17:51:15 +03:00
void * private_data ) ,
2019-10-23 12:34:47 +03:00
void * private_data )
2019-09-16 20:01:36 +03:00
{
2020-04-14 17:51:15 +03:00
TDB_DATA key = locking_key ( & d - > id ) ;
struct locking_tdb_data * ltdb = NULL ;
2019-09-16 20:01:36 +03:00
size_t idx ;
bool found = false ;
2021-03-03 21:19:23 +03:00
bool modified = false ;
2019-09-16 20:01:36 +03:00
struct share_mode_entry e ;
2020-04-14 17:51:15 +03:00
uint8_t * e_ptr = NULL ;
NTSTATUS status ;
bool ret = false ;
2019-09-16 20:01:36 +03:00
2020-04-14 17:51:15 +03:00
status = locking_tdb_data_fetch ( key , talloc_tos ( ) , & ltdb ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-09-10 21:39:19 +03:00
DBG_ERR ( " locking_tdb_data_fetch failed: %s \n " ,
nt_errstr ( status ) ) ;
2020-04-14 17:51:15 +03:00
return false ;
2019-09-16 20:01:36 +03:00
}
2020-04-14 17:51:15 +03:00
DBG_DEBUG ( " num_share_modes=%zu \n " , ltdb - > num_share_entries ) ;
2019-09-16 20:01:36 +03:00
idx = share_mode_entry_find (
2020-04-14 17:51:15 +03:00
ltdb - > share_entries ,
ltdb - > num_share_entries ,
pid ,
share_file_id ,
2019-09-16 20:01:36 +03:00
& e ,
& found ) ;
if ( ! found ) {
DBG_WARNING ( " Did not find share mode entry for % " PRIu64 " \n " ,
2020-04-14 17:51:15 +03:00
share_file_id ) ;
goto done ;
2019-09-16 20:01:36 +03:00
}
2020-08-28 16:56:35 +03:00
if ( DEBUGLEVEL > = 10 ) {
DBG_DEBUG ( " entry[%zu]: \n " , idx ) ;
NDR_PRINT_DEBUG ( share_mode_entry , & e ) ;
}
2020-04-14 17:51:15 +03:00
fn ( & e , ltdb - > num_share_entries , & modified , private_data ) ;
2019-09-16 20:01:36 +03:00
2020-08-28 16:56:35 +03:00
DBG_DEBUG ( " entry[%zu]: modified=%d, e.stale=%d \n " ,
idx ,
( int ) modified ,
( int ) e . stale ) ;
2019-09-16 20:01:36 +03:00
if ( ! e . stale & & ! modified ) {
2020-04-14 17:51:15 +03:00
ret = true ;
goto done ;
2019-09-16 20:01:36 +03:00
}
2020-04-14 17:51:15 +03:00
e_ptr = discard_const_p ( uint8_t , ltdb - > share_entries ) +
idx * SHARE_MODE_ENTRY_SIZE ;
2019-09-16 20:01:36 +03:00
2020-04-14 17:51:15 +03:00
if ( e . stale ) {
/*
2020-08-28 16:56:35 +03:00
* Move the rest down one entry
2020-04-14 17:51:15 +03:00
*/
size_t behind = ltdb - > num_share_entries - idx - 1 ;
if ( behind ! = 0 ) {
memmove ( e_ptr ,
e_ptr + SHARE_MODE_ENTRY_SIZE ,
behind * SHARE_MODE_ENTRY_SIZE ) ;
}
ltdb - > num_share_entries - = 1 ;
2020-08-28 16:56:35 +03:00
2020-12-17 14:36:42 +03:00
if ( ltdb - > num_share_entries = = 0 ) {
/*
* Tell share_mode_lock_destructor ( ) to delete
* the whole record
*/
d - > modified = true ;
}
2020-08-28 16:56:35 +03:00
if ( DEBUGLEVEL > = 10 ) {
DBG_DEBUG ( " share_mode_entry: \n " ) ;
NDR_PRINT_DEBUG ( share_mode_entry , & e ) ;
}
2020-04-14 17:51:15 +03:00
} else {
struct share_mode_entry_buf buf ;
2020-04-27 16:59:09 +03:00
bool ok ;
2020-04-14 17:51:15 +03:00
if ( ltdb - > num_share_entries ! = 1 ) {
2020-04-27 16:59:09 +03:00
/*
* Make sure the sorting order stays intact
*/
2020-04-14 17:51:15 +03:00
SMB_ASSERT ( server_id_equal ( & e . pid , & pid ) ) ;
SMB_ASSERT ( e . share_file_id = = share_file_id ) ;
2020-04-27 16:59:09 +03:00
}
ok = share_mode_entry_put ( & e , & buf ) ;
2019-09-16 20:01:36 +03:00
if ( ! ok ) {
DBG_DEBUG ( " share_mode_entry_put failed \n " ) ;
2020-04-14 17:51:15 +03:00
goto done ;
2019-11-29 23:30:39 +03:00
}
2020-04-14 17:51:15 +03:00
memcpy ( e_ptr , buf . buf , SHARE_MODE_ENTRY_SIZE ) ;
2019-09-16 20:01:36 +03:00
}
2022-08-22 17:53:38 +03:00
status = share_mode_data_ltdb_store ( d , key , ltdb , NULL , 0 ) ;
2019-09-16 20:01:36 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-08-22 17:53:38 +03:00
DBG_ERR ( " share_mode_data_ltdb_store failed: %s \n " ,
2022-09-10 21:39:19 +03:00
nt_errstr ( status ) ) ;
2020-04-14 17:51:15 +03:00
goto done ;
2019-09-16 20:01:36 +03:00
}
2020-04-14 17:51:15 +03:00
ret = true ;
done :
TALLOC_FREE ( ltdb ) ;
return ret ;
2019-09-16 20:01:36 +03:00
}
struct del_share_mode_state {
bool ok ;
} ;
static void del_share_mode_fn (
struct share_mode_entry * e ,
2019-12-03 12:36:21 +03:00
size_t num_share_modes ,
2019-09-16 20:01:36 +03:00
bool * modified ,
void * private_data )
{
struct del_share_mode_state * state = private_data ;
e - > stale = true ;
state - > ok = true ;
}
bool del_share_mode ( struct share_mode_lock * lck , files_struct * fsp )
{
struct del_share_mode_state state = { . ok = false } ;
2022-08-26 13:10:55 +03:00
struct share_mode_data * d = NULL ;
NTSTATUS status ;
2019-09-16 20:01:36 +03:00
bool ok ;
2022-08-26 13:10:55 +03:00
status = share_mode_lock_access_private_data ( lck , & d ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
struct file_id id = share_mode_lock_file_id ( lck ) ;
struct file_id_buf id_buf ;
/* Any error recovery possible here ? */
DBG_ERR ( " share_mode_lock_access_private_data() failed for "
" %s %s - %s \n " ,
file_id_str_buf ( id , & id_buf ) ,
fsp_str_dbg ( fsp ) ,
nt_errstr ( status ) ) ;
return false ;
}
2019-09-16 20:01:36 +03:00
ok = share_mode_entry_do (
2022-08-26 13:10:55 +03:00
d ,
2019-09-16 20:01:36 +03:00
messaging_server_id ( fsp - > conn - > sconn - > msg_ctx ) ,
2020-09-28 11:35:32 +03:00
fh_get_gen_id ( fsp - > fh ) ,
2019-09-16 20:01:36 +03:00
del_share_mode_fn ,
& state ) ;
if ( ! ok ) {
DBG_DEBUG ( " share_mode_entry_do failed \n " ) ;
return false ;
}
if ( ! state . ok ) {
DBG_DEBUG ( " del_share_mode_fn failed \n " ) ;
return false ;
}
return true ;
}
struct remove_share_oplock_state {
bool ok ;
} ;
static void remove_share_oplock_fn (
struct share_mode_entry * e ,
2019-12-03 12:36:21 +03:00
size_t num_share_modes ,
2019-09-16 20:01:36 +03:00
bool * modified ,
void * private_data )
{
struct remove_share_oplock_state * state = private_data ;
e - > op_type = NO_OPLOCK ;
* modified = true ;
state - > ok = true ;
}
bool remove_share_oplock ( struct share_mode_lock * lck , files_struct * fsp )
{
struct remove_share_oplock_state state = { . ok = false } ;
2022-08-26 13:10:55 +03:00
struct share_mode_data * d = NULL ;
NTSTATUS status ;
2019-09-16 20:01:36 +03:00
bool ok ;
2022-08-26 13:10:55 +03:00
status = share_mode_lock_access_private_data ( lck , & d ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
struct file_id id = share_mode_lock_file_id ( lck ) ;
struct file_id_buf id_buf ;
/* Any error recovery possible here ? */
DBG_ERR ( " share_mode_lock_access_private_data() failed for "
" %s %s - %s \n " ,
file_id_str_buf ( id , & id_buf ) ,
fsp_str_dbg ( fsp ) ,
nt_errstr ( status ) ) ;
return false ;
}
2019-09-16 20:01:36 +03:00
ok = share_mode_entry_do (
2022-08-26 13:10:55 +03:00
d ,
2019-09-16 20:01:36 +03:00
messaging_server_id ( fsp - > conn - > sconn - > msg_ctx ) ,
2020-09-28 11:35:32 +03:00
fh_get_gen_id ( fsp - > fh ) ,
2019-09-16 20:01:36 +03:00
remove_share_oplock_fn ,
& state ) ;
if ( ! ok ) {
DBG_DEBUG ( " share_mode_entry_do failed \n " ) ;
return false ;
}
if ( ! state . ok ) {
DBG_DEBUG ( " remove_share_oplock_fn failed \n " ) ;
return false ;
}
if ( fsp - > oplock_type = = LEASE_OPLOCK ) {
remove_lease_if_stale (
lck ,
fsp_client_guid ( fsp ) ,
& fsp - > lease - > lease . lease_key ) ;
}
2019-12-13 16:40:03 +03:00
share_mode_wakeup_waiters ( fsp - > file_id ) ;
2019-09-16 20:01:36 +03:00
return true ;
}
struct downgrade_share_oplock_state {
bool ok ;
} ;
static void downgrade_share_oplock_fn (
struct share_mode_entry * e ,
2019-12-03 12:36:21 +03:00
size_t num_share_modes ,
2019-09-16 20:01:36 +03:00
bool * modified ,
void * private_data )
{
struct downgrade_share_oplock_state * state = private_data ;
e - > op_type = LEVEL_II_OPLOCK ;
* modified = true ;
state - > ok = true ;
}
bool downgrade_share_oplock ( struct share_mode_lock * lck , files_struct * fsp )
{
struct downgrade_share_oplock_state state = { . ok = false } ;
2022-08-26 13:10:55 +03:00
struct share_mode_data * d = NULL ;
NTSTATUS status ;
2019-09-16 20:01:36 +03:00
bool ok ;
2022-08-26 13:10:55 +03:00
status = share_mode_lock_access_private_data ( lck , & d ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
struct file_id id = share_mode_lock_file_id ( lck ) ;
struct file_id_buf id_buf ;
/* Any error recovery possible here ? */
DBG_ERR ( " share_mode_lock_access_private_data() failed for "
" %s %s - %s \n " ,
file_id_str_buf ( id , & id_buf ) ,
fsp_str_dbg ( fsp ) ,
nt_errstr ( status ) ) ;
return false ;
}
2019-09-16 20:01:36 +03:00
ok = share_mode_entry_do (
2022-08-26 13:10:55 +03:00
d ,
2019-09-16 20:01:36 +03:00
messaging_server_id ( fsp - > conn - > sconn - > msg_ctx ) ,
2020-09-28 11:35:32 +03:00
fh_get_gen_id ( fsp - > fh ) ,
2019-09-16 20:01:36 +03:00
downgrade_share_oplock_fn ,
& state ) ;
if ( ! ok ) {
DBG_DEBUG ( " share_mode_entry_do failed \n " ) ;
return false ;
}
if ( ! state . ok ) {
DBG_DEBUG ( " downgrade_share_oplock_fn failed \n " ) ;
return false ;
}
2022-08-26 13:10:55 +03:00
d - > flags | = SHARE_MODE_LEASE_READ ;
d - > modified = true ;
2019-09-16 20:01:36 +03:00
return true ;
}
bool mark_share_mode_disconnected ( struct share_mode_lock * lck ,
struct files_struct * fsp )
{
2020-12-16 20:35:50 +03:00
struct server_id disconnected_pid = { . pid = 0 } ;
2019-09-16 20:01:36 +03:00
bool ok ;
if ( fsp - > op = = NULL ) {
return false ;
}
if ( ! fsp - > op - > global - > durable ) {
return false ;
}
2020-12-16 20:35:50 +03:00
server_id_set_disconnected ( & disconnected_pid ) ;
2019-09-24 19:51:08 +03:00
2020-12-16 20:35:50 +03:00
ok = reset_share_mode_entry (
2019-09-16 20:01:36 +03:00
lck ,
messaging_server_id ( fsp - > conn - > sconn - > msg_ctx ) ,
2020-09-28 11:35:32 +03:00
fh_get_gen_id ( fsp - > fh ) ,
2020-12-16 20:35:50 +03:00
disconnected_pid ,
UINT64_MAX ,
fsp - > op - > global - > open_persistent_id ) ;
2019-09-16 20:01:36 +03:00
2020-12-16 20:35:50 +03:00
return ok ;
2019-09-16 20:01:36 +03:00
}
2020-04-14 17:51:15 +03:00
bool reset_share_mode_entry (
struct share_mode_lock * lck ,
struct server_id old_pid ,
uint64_t old_share_file_id ,
struct server_id new_pid ,
uint64_t new_mid ,
uint64_t new_share_file_id )
2019-09-16 20:01:36 +03:00
{
2022-08-26 13:11:44 +03:00
struct file_id id = share_mode_lock_file_id ( lck ) ;
struct share_mode_data * d = NULL ;
TDB_DATA key = locking_key ( & id ) ;
2020-04-14 17:51:15 +03:00
struct locking_tdb_data * ltdb = NULL ;
2020-04-24 18:30:44 +03:00
struct share_mode_entry e ;
struct share_mode_entry_buf e_buf ;
NTSTATUS status ;
2022-09-10 21:39:37 +03:00
int cmp ;
2020-04-14 17:51:15 +03:00
bool ret = false ;
2020-04-24 18:30:44 +03:00
bool ok ;
2019-09-16 20:01:36 +03:00
2022-08-26 13:11:44 +03:00
status = share_mode_lock_access_private_data ( lck , & d ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
struct file_id_buf id_buf ;
/* Any error recovery possible here ? */
DBG_ERR ( " share_mode_lock_access_private_data() failed for "
" %s - %s \n " ,
file_id_str_buf ( id , & id_buf ) ,
nt_errstr ( status ) ) ;
return false ;
}
2020-04-14 17:51:15 +03:00
status = locking_tdb_data_fetch ( key , talloc_tos ( ) , & ltdb ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-09-10 21:39:19 +03:00
DBG_ERR ( " locking_tdb_data_fetch failed: %s \n " ,
nt_errstr ( status ) ) ;
2020-04-14 17:51:15 +03:00
return false ;
}
if ( ltdb - > num_share_entries ! = 1 ) {
DBG_DEBUG ( " num_share_modes=%zu \n " , ltdb - > num_share_entries ) ;
goto done ;
2020-04-24 18:30:44 +03:00
}
2020-04-14 17:51:15 +03:00
ok = share_mode_entry_get ( ltdb - > share_entries , & e ) ;
2020-04-24 18:30:44 +03:00
if ( ! ok ) {
DBG_WARNING ( " share_mode_entry_get failed \n " ) ;
2020-04-14 17:51:15 +03:00
goto done ;
2020-04-24 18:30:44 +03:00
}
2022-09-10 21:39:37 +03:00
cmp = share_mode_entry_cmp (
2020-04-14 17:51:15 +03:00
old_pid , old_share_file_id , e . pid , e . share_file_id ) ;
2022-09-10 21:39:37 +03:00
if ( cmp ! = 0 ) {
2020-04-24 18:30:44 +03:00
struct server_id_buf tmp1 , tmp2 ;
DBG_WARNING ( " Expected pid=%s, file_id=% " PRIu64 " , "
" got pid=%s, file_id=% " PRIu64 " \n " ,
2020-04-14 17:51:15 +03:00
server_id_str_buf ( old_pid , & tmp1 ) ,
old_share_file_id ,
2020-04-24 18:30:44 +03:00
server_id_str_buf ( e . pid , & tmp2 ) ,
e . share_file_id ) ;
2020-04-14 17:51:15 +03:00
goto done ;
2020-04-24 18:30:44 +03:00
}
2020-04-14 17:51:15 +03:00
e . pid = new_pid ;
2020-12-16 20:35:50 +03:00
if ( new_mid ! = UINT64_MAX ) {
e . op_mid = new_mid ;
}
2020-04-14 17:51:15 +03:00
e . share_file_id = new_share_file_id ;
2020-04-24 18:30:44 +03:00
ok = share_mode_entry_put ( & e , & e_buf ) ;
if ( ! ok ) {
DBG_WARNING ( " share_mode_entry_put failed \n " ) ;
2020-04-14 17:51:15 +03:00
goto done ;
2020-04-24 18:30:44 +03:00
}
2020-04-14 17:51:15 +03:00
ltdb - > share_entries = e_buf . buf ;
2019-09-16 20:01:36 +03:00
2022-08-22 17:53:38 +03:00
d - > modified = true ;
status = share_mode_data_ltdb_store ( d , key , ltdb , NULL , 0 ) ;
2019-09-16 20:01:36 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2022-08-22 17:53:38 +03:00
DBG_ERR ( " share_mode_data_ltdb_store failed: %s \n " ,
2022-09-10 21:39:19 +03:00
nt_errstr ( status ) ) ;
2020-04-14 17:51:15 +03:00
goto done ;
2019-09-16 20:01:36 +03:00
}
2020-04-14 17:51:15 +03:00
ret = true ;
done :
TALLOC_FREE ( ltdb ) ;
2022-09-10 21:39:37 +03:00
return ret ;
2019-09-16 20:01:36 +03:00
}
2022-08-18 14:26:47 +03:00
/**
* @ brief Run @ fn protected with G_LOCK_WRITE in the given file_id
*
* @ fn is NOT allowed to call SMB_VFS_ * or similar functions ,
* which may block for some time in the kernel .
*
* There must be at least one share_mode_entry , otherwise
* NT_STATUS_NOT_FOUND is returned .
*
* @ param [ in ] id The key for the share_mode record .
* @ param [ in ] fn The function to run under the g_lock .
* @ param [ in ] private_date A private pointer passed to @ fn .
*/
NTSTATUS _share_mode_do_locked_vfs_denied (
struct file_id id ,
share_mode_do_locked_vfs_fn_t fn ,
void * private_data ,
const char * location )
{
struct smb_vfs_deny_state vfs_deny = { } ;
struct share_mode_lock * lck = NULL ;
lck = get_existing_share_mode_lock ( talloc_tos ( ) , id ) ;
if ( lck = = NULL ) {
NTSTATUS status = NT_STATUS_NOT_FOUND ;
DBG_DEBUG ( " get_existing_share_mode_lock failed: %s \n " ,
nt_errstr ( status ) ) ;
return status ;
}
_smb_vfs_deny_push ( & vfs_deny , location ) ;
fn ( lck , private_data ) ;
_smb_vfs_deny_pop ( & vfs_deny , location ) ;
TALLOC_FREE ( lck ) ;
return NT_STATUS_OK ;
}
/**
* @ brief Run @ fn protected with G_LOCK_WRITE in the given file_id
*
* @ fn is allowed to call SMB_VFS_ * or similar functions ,
* which may block for some time in the kernel .
*
* There must be at least one share_mode_entry , otherwise
* NT_STATUS_NOT_FOUND is returned .
*
* @ param [ in ] id The key for the share_mode record .
* @ param [ in ] fn The function to run under the g_lock .
* @ param [ in ] private_date A private pointer passed to @ fn .
*/
NTSTATUS _share_mode_do_locked_vfs_allowed (
struct file_id id ,
share_mode_do_locked_vfs_fn_t fn ,
void * private_data ,
const char * location )
{
struct share_mode_lock * lck = NULL ;
smb_vfs_assert_allowed ( ) ;
lck = get_existing_share_mode_lock ( talloc_tos ( ) , id ) ;
if ( lck = = NULL ) {
NTSTATUS status = NT_STATUS_NOT_FOUND ;
DBG_DEBUG ( " get_existing_share_mode_lock failed: %s \n " ,
nt_errstr ( status ) ) ;
return status ;
}
fn ( lck , private_data ) ;
TALLOC_FREE ( lck ) ;
return NT_STATUS_OK ;
}