2009-10-25 18:12:12 +03:00
/*
Unix SMB / CIFS implementation .
global locks based on dbwrap and messaging
Copyright ( C ) 2009 by Volker Lendecke
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/>.
*/
# include "includes.h"
2011-02-26 01:20:06 +03:00
# include "system/filesys.h"
2017-01-01 23:00:55 +03:00
# include "lib/util/server_id.h"
2011-07-07 19:42:08 +04:00
# include "dbwrap/dbwrap.h"
2011-07-06 18:40:21 +04:00
# include "dbwrap/dbwrap_open.h"
2012-02-15 19:38:43 +04:00
# include "dbwrap/dbwrap_watch.h"
2009-10-25 18:12:12 +03:00
# include "g_lock.h"
2011-05-05 13:25:29 +04:00
# include "util_tdb.h"
2012-02-15 19:38:43 +04:00
# include "../lib/util/tevent_ntstatus.h"
2011-03-24 17:31:06 +03:00
# include "messages.h"
2012-08-22 14:35:29 +04:00
# include "serverid.h"
2009-10-25 18:12:12 +03:00
struct g_lock_ctx {
struct db_context * db ;
struct messaging_context * msg ;
} ;
/*
* The " g_lock.tdb " file contains records , indexed by the 0 - terminated
* lockname . The record contains an array of " struct g_lock_rec "
2012-02-15 19:38:43 +04:00
* structures .
2009-10-25 18:12:12 +03:00
*/
2017-05-17 06:52:56 +03:00
# define G_LOCK_REC_LENGTH (SERVER_ID_BUF_LENGTH+1)
static void g_lock_rec_put ( uint8_t buf [ G_LOCK_REC_LENGTH ] ,
const struct g_lock_rec rec )
{
SCVAL ( buf , 0 , rec . lock_type ) ;
server_id_put ( buf + 1 , rec . pid ) ;
}
static void g_lock_rec_get ( struct g_lock_rec * rec ,
const uint8_t buf [ G_LOCK_REC_LENGTH ] )
{
rec - > lock_type = CVAL ( buf , 0 ) ;
server_id_get ( & rec - > pid , buf + 1 ) ;
}
2017-06-28 14:36:53 +03:00
struct g_lock {
uint8_t * recsbuf ;
size_t num_recs ;
uint8_t * data ;
size_t datalen ;
} ;
static bool g_lock_parse ( uint8_t * buf , size_t buflen , struct g_lock * lck )
{
size_t found_recs , data_ofs ;
if ( buflen < sizeof ( uint32_t ) ) {
* lck = ( struct g_lock ) { 0 } ;
return true ;
}
found_recs = IVAL ( buf , 0 ) ;
2017-10-26 10:43:56 +03:00
buf + = sizeof ( uint32_t ) ;
buflen - = sizeof ( uint32_t ) ;
2017-06-28 14:36:53 +03:00
if ( found_recs > buflen / G_LOCK_REC_LENGTH ) {
return false ;
}
data_ofs = found_recs * G_LOCK_REC_LENGTH ;
* lck = ( struct g_lock ) {
. recsbuf = buf , . num_recs = found_recs ,
. data = buf + data_ofs , . datalen = buflen - data_ofs
} ;
return true ;
}
2019-05-06 17:19:22 +03:00
static void g_lock_get_rec ( const struct g_lock * lck ,
size_t i ,
2017-06-28 14:36:53 +03:00
struct g_lock_rec * rec )
{
if ( i > = lck - > num_recs ) {
abort ( ) ;
}
g_lock_rec_get ( rec , lck - > recsbuf + i * G_LOCK_REC_LENGTH ) ;
}
static void g_lock_rec_del ( struct g_lock * lck , size_t i )
{
if ( i > = lck - > num_recs ) {
abort ( ) ;
}
lck - > num_recs - = 1 ;
if ( i < lck - > num_recs ) {
uint8_t * recptr = lck - > recsbuf + i * G_LOCK_REC_LENGTH ;
memcpy ( recptr , lck - > recsbuf + lck - > num_recs * G_LOCK_REC_LENGTH ,
G_LOCK_REC_LENGTH ) ;
}
}
static NTSTATUS g_lock_store ( struct db_record * rec , struct g_lock * lck ,
struct g_lock_rec * add )
{
uint8_t sizebuf [ 4 ] ;
uint8_t addbuf [ G_LOCK_REC_LENGTH ] ;
struct TDB_DATA dbufs [ ] = {
{ . dptr = sizebuf , . dsize = sizeof ( sizebuf ) } ,
{ . dptr = lck - > recsbuf ,
. dsize = lck - > num_recs * G_LOCK_REC_LENGTH } ,
{ 0 } ,
{ . dptr = lck - > data , . dsize = lck - > datalen }
} ;
if ( add ! = NULL ) {
g_lock_rec_put ( addbuf , * add ) ;
dbufs [ 2 ] = ( TDB_DATA ) {
. dptr = addbuf , . dsize = G_LOCK_REC_LENGTH
} ;
lck - > num_recs + = 1 ;
}
SIVAL ( sizebuf , 0 , lck - > num_recs ) ;
return dbwrap_record_storev ( rec , dbufs , ARRAY_SIZE ( dbufs ) , 0 ) ;
}
2009-10-25 18:12:12 +03:00
struct g_lock_ctx * g_lock_ctx_init ( TALLOC_CTX * mem_ctx ,
struct messaging_context * msg )
{
struct g_lock_ctx * result ;
2016-07-13 08:26:52 +03:00
struct db_context * backend ;
2014-11-02 22:21:40 +03:00
char * db_path ;
2009-10-25 18:12:12 +03:00
result = talloc ( mem_ctx , struct g_lock_ctx ) ;
if ( result = = NULL ) {
return NULL ;
}
result - > msg = msg ;
2018-08-16 11:51:44 +03:00
db_path = lock_path ( talloc_tos ( ) , " g_lock.tdb " ) ;
2014-11-02 22:21:40 +03:00
if ( db_path = = NULL ) {
TALLOC_FREE ( result ) ;
return NULL ;
}
2016-07-13 08:26:52 +03:00
backend = db_open ( result , db_path , 0 ,
TDB_CLEAR_IF_FIRST | TDB_INCOMPATIBLE_HASH ,
O_RDWR | O_CREAT , 0600 ,
2017-06-14 14:57:56 +03:00
DBWRAP_LOCK_ORDER_3 ,
2016-07-13 08:26:52 +03:00
DBWRAP_FLAG_NONE ) ;
2014-11-02 22:21:40 +03:00
TALLOC_FREE ( db_path ) ;
2016-07-13 08:26:52 +03:00
if ( backend = = NULL ) {
2018-08-16 12:34:36 +03:00
DBG_WARNING ( " Could not open g_lock.tdb \n " ) ;
2009-10-25 18:12:12 +03:00
TALLOC_FREE ( result ) ;
return NULL ;
}
2016-07-13 08:26:52 +03:00
2018-08-16 12:25:54 +03:00
result - > db = db_open_watched ( result , & backend , msg ) ;
2016-07-13 08:26:52 +03:00
if ( result - > db = = NULL ) {
2018-08-16 12:34:36 +03:00
DBG_WARNING ( " db_open_watched failed \n " ) ;
2016-07-13 08:26:52 +03:00
TALLOC_FREE ( result ) ;
return NULL ;
}
2009-10-25 18:12:12 +03:00
return result ;
}
2012-02-15 19:38:43 +04:00
static bool g_lock_conflicts ( enum g_lock_type l1 , enum g_lock_type l2 )
2009-10-25 18:12:12 +03:00
{
/*
* Only tested write locks so far . Very likely this routine
* needs to be fixed for read locks . . . .
*/
2012-02-15 19:38:43 +04:00
if ( ( l1 = = G_LOCK_READ ) & & ( l2 = = G_LOCK_READ ) ) {
2009-10-25 18:12:12 +03:00
return false ;
}
return true ;
}
2012-02-15 19:38:43 +04:00
static NTSTATUS g_lock_trylock ( struct db_record * rec , struct server_id self ,
2016-07-12 17:07:51 +03:00
enum g_lock_type type ,
struct server_id * blocker )
2010-02-15 18:57:16 +03:00
{
2017-06-28 20:39:33 +03:00
TDB_DATA data ;
size_t i ;
struct g_lock lck ;
2018-08-13 16:07:06 +03:00
struct g_lock_rec mylock = { 0 } ;
2012-02-15 19:38:43 +04:00
NTSTATUS status ;
bool modified = false ;
2017-06-28 20:39:33 +03:00
bool ok ;
2010-02-15 18:57:16 +03:00
2012-02-15 19:38:43 +04:00
data = dbwrap_record_get_value ( rec ) ;
2010-02-15 18:57:16 +03:00
2017-06-28 20:39:33 +03:00
ok = g_lock_parse ( data . dptr , data . dsize , & lck ) ;
if ( ! ok ) {
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
2012-02-15 19:38:43 +04:00
}
2010-02-15 18:57:16 +03:00
2017-06-28 20:39:33 +03:00
if ( ( type = = G_LOCK_READ ) & & ( lck . num_recs > 0 ) ) {
struct g_lock_rec check_rec ;
2017-05-22 18:05:57 +03:00
/*
* Read locks can stay around forever if the process
* dies . Do a heuristic check for process existence :
* Check one random process for existence . Hopefully
* this will keep runaway read locks under control .
*/
2017-06-28 20:39:33 +03:00
i = generate_random ( ) % lck . num_recs ;
g_lock_get_rec ( & lck , i , & check_rec ) ;
2017-05-22 18:05:57 +03:00
2018-08-13 15:12:47 +03:00
if ( ( check_rec . lock_type = = G_LOCK_READ ) & &
! serverid_exists ( & check_rec . pid ) ) {
2017-06-28 20:39:33 +03:00
g_lock_rec_del ( & lck , i ) ;
2017-05-22 18:05:57 +03:00
modified = true ;
}
}
2018-08-13 16:07:06 +03:00
/*
* For the lock upgrade / downgrade case , remove ourselves from
* the list . We re - add ourselves later after we checked the
* other entries for conflict .
*/
2017-12-20 10:25:19 +03:00
2018-08-13 16:07:06 +03:00
for ( i = 0 ; i < lck . num_recs ; i + + ) {
2017-06-28 20:39:33 +03:00
struct g_lock_rec lock ;
g_lock_get_rec ( & lck , i , & lock ) ;
2017-05-19 17:57:00 +03:00
2017-06-28 20:39:33 +03:00
if ( serverid_equal ( & self , & lock . pid ) ) {
if ( lock . lock_type = = type ) {
2017-05-19 17:57:00 +03:00
status = NT_STATUS_WAS_LOCKED ;
goto done ;
}
2018-08-13 16:07:06 +03:00
mylock = lock ;
2017-06-28 20:39:33 +03:00
g_lock_rec_del ( & lck , i ) ;
2017-12-20 10:41:09 +03:00
modified = true ;
2018-08-13 16:07:06 +03:00
break ;
2009-10-25 18:12:12 +03:00
}
2018-08-13 16:07:06 +03:00
}
/*
* Check for conflicts with everybody else . Not a for - loop
* because we remove stale entries in the meantime ,
* decrementing lck . num_recs .
*/
i = 0 ;
while ( i < lck . num_recs ) {
struct g_lock_rec lock ;
g_lock_get_rec ( & lck , i , & lock ) ;
2017-05-19 17:57:00 +03:00
2017-06-28 20:39:33 +03:00
if ( g_lock_conflicts ( type , lock . lock_type ) ) {
struct server_id pid = lock . pid ;
2009-10-25 18:12:12 +03:00
2012-08-22 14:35:29 +04:00
/*
* As the serverid_exists might recurse into
* the g_lock code , we use
* SERVERID_UNIQUE_ID_NOT_TO_VERIFY to avoid the loop
*/
pid . unique_id = SERVERID_UNIQUE_ID_NOT_TO_VERIFY ;
if ( serverid_exists ( & pid ) ) {
2012-02-15 19:38:43 +04:00
status = NT_STATUS_LOCK_NOT_GRANTED ;
2017-06-28 20:39:33 +03:00
* blocker = lock . pid ;
2012-02-15 19:38:43 +04:00
goto done ;
}
/*
* Delete stale conflicting entry
*/
2017-06-28 20:39:33 +03:00
g_lock_rec_del ( & lck , i ) ;
2012-02-15 19:38:43 +04:00
modified = true ;
2017-12-20 10:25:19 +03:00
continue ;
2009-10-25 18:12:12 +03:00
}
2017-12-20 10:25:19 +03:00
i + + ;
2009-10-25 18:12:12 +03:00
}
2012-02-15 19:38:43 +04:00
modified = true ;
2018-08-13 16:07:06 +03:00
mylock = ( struct g_lock_rec ) {
2017-12-20 10:41:09 +03:00
. pid = self ,
. lock_type = type
} ;
2012-02-15 19:38:43 +04:00
status = NT_STATUS_OK ;
done :
if ( modified ) {
NTSTATUS store_status ;
2017-12-20 10:41:09 +03:00
2018-08-13 16:07:06 +03:00
/*
* ( Re - ) add ourselves if needed via non - NULL
* g_lock_store argument
*/
store_status = g_lock_store (
rec ,
& lck ,
mylock . pid . pid ! = 0 ? & mylock : NULL ) ;
2012-02-15 19:38:43 +04:00
if ( ! NT_STATUS_IS_OK ( store_status ) ) {
2017-05-17 06:52:56 +03:00
DBG_WARNING ( " g_lock_record_store failed: %s \n " ,
nt_errstr ( store_status ) ) ;
2012-02-15 19:38:43 +04:00
status = store_status ;
}
}
return status ;
2009-10-25 18:12:12 +03:00
}
2012-02-15 19:38:43 +04:00
struct g_lock_lock_state {
struct tevent_context * ev ;
struct g_lock_ctx * ctx ;
2017-12-03 22:47:02 +03:00
TDB_DATA key ;
2012-02-15 19:38:43 +04:00
enum g_lock_type type ;
} ;
static void g_lock_lock_retry ( struct tevent_req * subreq ) ;
2009-10-25 18:12:12 +03:00
2017-06-28 21:01:34 +03:00
struct g_lock_lock_fn_state {
struct g_lock_lock_state * state ;
struct server_id self ;
struct tevent_req * watch_req ;
NTSTATUS status ;
} ;
static void g_lock_lock_fn ( struct db_record * rec , void * private_data )
{
struct g_lock_lock_fn_state * state = private_data ;
struct server_id blocker ;
state - > status = g_lock_trylock ( rec , state - > self , state - > state - > type ,
& blocker ) ;
if ( ! NT_STATUS_EQUAL ( state - > status , NT_STATUS_LOCK_NOT_GRANTED ) ) {
return ;
}
state - > watch_req = dbwrap_watched_watch_send (
state - > state , state - > state - > ev , rec , blocker ) ;
}
2012-02-15 19:38:43 +04:00
struct tevent_req * g_lock_lock_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct g_lock_ctx * ctx ,
2017-12-03 22:47:02 +03:00
TDB_DATA key ,
2012-02-15 19:38:43 +04:00
enum g_lock_type type )
2009-10-25 18:12:12 +03:00
{
2017-06-28 21:01:34 +03:00
struct tevent_req * req ;
2012-02-15 19:38:43 +04:00
struct g_lock_lock_state * state ;
2017-06-28 21:01:34 +03:00
struct g_lock_lock_fn_state fn_state ;
2012-02-15 19:38:43 +04:00
NTSTATUS status ;
req = tevent_req_create ( mem_ctx , & state , struct g_lock_lock_state ) ;
if ( req = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > ctx = ctx ;
2017-12-03 22:47:02 +03:00
state - > key = key ;
2012-02-15 19:38:43 +04:00
state - > type = type ;
2009-10-25 18:12:12 +03:00
2017-06-28 21:01:34 +03:00
fn_state = ( struct g_lock_lock_fn_state ) {
. state = state , . self = messaging_server_id ( ctx - > msg )
} ;
2017-12-03 22:47:02 +03:00
status = dbwrap_do_locked ( ctx - > db , key , g_lock_lock_fn , & fn_state ) ;
2017-06-28 21:01:34 +03:00
if ( tevent_req_nterror ( req , status ) ) {
DBG_DEBUG ( " dbwrap_do_locked failed: %s \n " ,
nt_errstr ( status ) ) ;
2012-02-15 19:38:43 +04:00
return tevent_req_post ( req , ev ) ;
2009-10-25 18:12:12 +03:00
}
2017-06-28 21:01:34 +03:00
if ( NT_STATUS_IS_OK ( fn_state . status ) ) {
2012-02-15 19:38:43 +04:00
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
2009-10-25 18:12:12 +03:00
}
2017-06-28 21:01:34 +03:00
if ( ! NT_STATUS_EQUAL ( fn_state . status , NT_STATUS_LOCK_NOT_GRANTED ) ) {
tevent_req_nterror ( req , fn_state . status ) ;
2012-02-15 19:38:43 +04:00
return tevent_req_post ( req , ev ) ;
2009-10-25 18:12:12 +03:00
}
2017-06-28 21:01:34 +03:00
if ( tevent_req_nomem ( fn_state . watch_req , req ) ) {
2012-02-15 19:38:43 +04:00
return tevent_req_post ( req , ev ) ;
2009-10-25 18:12:12 +03:00
}
2017-06-28 21:01:34 +03:00
2012-08-10 19:00:38 +04:00
if ( ! tevent_req_set_endtime (
2017-06-28 21:01:34 +03:00
fn_state . watch_req , state - > ev ,
2012-08-10 19:00:38 +04:00
timeval_current_ofs ( 5 + sys_random ( ) % 5 , 0 ) ) ) {
return tevent_req_post ( req , ev ) ;
}
2017-06-28 21:01:34 +03:00
tevent_req_set_callback ( fn_state . watch_req , g_lock_lock_retry , req ) ;
2012-02-15 19:38:43 +04:00
return req ;
2009-10-25 18:12:12 +03:00
}
2012-02-15 19:38:43 +04:00
static void g_lock_lock_retry ( struct tevent_req * subreq )
2009-10-25 18:12:12 +03:00
{
2012-02-15 19:38:43 +04:00
struct tevent_req * req = tevent_req_callback_data (
subreq , struct tevent_req ) ;
struct g_lock_lock_state * state = tevent_req_data (
req , struct g_lock_lock_state ) ;
2017-06-30 23:20:41 +03:00
struct g_lock_lock_fn_state fn_state ;
2009-10-25 18:12:12 +03:00
NTSTATUS status ;
2017-07-01 09:27:57 +03:00
status = dbwrap_watched_watch_recv ( subreq , NULL , NULL ) ;
2017-06-30 23:20:41 +03:00
DBG_DEBUG ( " watch_recv returned %s \n " , nt_errstr ( status ) ) ;
2012-02-15 19:38:43 +04:00
TALLOC_FREE ( subreq ) ;
2012-08-10 19:00:38 +04:00
2017-06-30 23:20:41 +03:00
if ( ! NT_STATUS_IS_OK ( status ) & &
! NT_STATUS_EQUAL ( status , NT_STATUS_IO_TIMEOUT ) ) {
tevent_req_nterror ( req , status ) ;
return ;
2012-08-10 19:00:38 +04:00
}
2017-06-30 23:20:41 +03:00
fn_state = ( struct g_lock_lock_fn_state ) {
. state = state , . self = messaging_server_id ( state - > ctx - > msg )
} ;
2017-12-03 22:47:02 +03:00
status = dbwrap_do_locked ( state - > ctx - > db , state - > key ,
2017-06-30 23:20:41 +03:00
g_lock_lock_fn , & fn_state ) ;
2012-02-15 19:38:43 +04:00
if ( tevent_req_nterror ( req , status ) ) {
2017-06-30 23:20:41 +03:00
DBG_DEBUG ( " dbwrap_do_locked failed: %s \n " ,
nt_errstr ( status ) ) ;
2012-02-15 19:38:43 +04:00
return ;
2009-10-25 18:12:12 +03:00
}
2017-06-30 23:20:41 +03:00
if ( NT_STATUS_IS_OK ( fn_state . status ) ) {
2012-02-15 19:38:43 +04:00
tevent_req_done ( req ) ;
return ;
2009-10-25 18:12:12 +03:00
}
2017-06-30 23:20:41 +03:00
if ( ! NT_STATUS_EQUAL ( fn_state . status , NT_STATUS_LOCK_NOT_GRANTED ) ) {
tevent_req_nterror ( req , fn_state . status ) ;
2012-02-15 19:38:43 +04:00
return ;
2009-10-25 18:12:12 +03:00
}
2017-06-30 23:20:41 +03:00
if ( tevent_req_nomem ( fn_state . watch_req , req ) ) {
2012-02-15 19:38:43 +04:00
return ;
2009-10-25 18:12:12 +03:00
}
2017-06-30 23:20:41 +03:00
2012-08-10 19:00:38 +04:00
if ( ! tevent_req_set_endtime (
2017-06-30 23:20:41 +03:00
fn_state . watch_req , state - > ev ,
2012-08-10 19:00:38 +04:00
timeval_current_ofs ( 5 + sys_random ( ) % 5 , 0 ) ) ) {
return ;
}
2017-06-30 23:20:41 +03:00
tevent_req_set_callback ( fn_state . watch_req , g_lock_lock_retry , req ) ;
2009-10-25 18:12:12 +03:00
}
2012-02-15 19:38:43 +04:00
NTSTATUS g_lock_lock_recv ( struct tevent_req * req )
2009-10-25 18:12:12 +03:00
{
2012-02-15 19:38:43 +04:00
return tevent_req_simple_recv_ntstatus ( req ) ;
}
2009-10-25 18:12:12 +03:00
2017-12-03 22:47:02 +03:00
NTSTATUS g_lock_lock ( struct g_lock_ctx * ctx , TDB_DATA key ,
2012-02-15 19:38:43 +04:00
enum g_lock_type type , struct timeval timeout )
{
TALLOC_CTX * frame = talloc_stackframe ( ) ;
struct tevent_context * ev ;
struct tevent_req * req ;
struct timeval end ;
NTSTATUS status = NT_STATUS_NO_MEMORY ;
2009-10-25 18:12:12 +03:00
2013-02-18 12:10:34 +04:00
ev = samba_tevent_context_init ( frame ) ;
2012-02-15 19:38:43 +04:00
if ( ev = = NULL ) {
goto fail ;
}
2017-12-03 22:47:02 +03:00
req = g_lock_lock_send ( frame , ev , ctx , key , type ) ;
2012-02-15 19:38:43 +04:00
if ( req = = NULL ) {
goto fail ;
}
end = timeval_current_ofs ( timeout . tv_sec , timeout . tv_usec ) ;
if ( ! tevent_req_set_endtime ( req , ev , end ) ) {
goto fail ;
}
if ( ! tevent_req_poll_ntstatus ( req , ev , & status ) ) {
goto fail ;
}
status = g_lock_lock_recv ( req ) ;
fail :
TALLOC_FREE ( frame ) ;
return status ;
2009-10-25 18:12:12 +03:00
}
2017-06-28 16:39:49 +03:00
struct g_lock_unlock_state {
2017-12-03 22:47:02 +03:00
TDB_DATA key ;
2017-06-28 16:39:49 +03:00
struct server_id self ;
2009-10-25 18:12:12 +03:00
NTSTATUS status ;
2017-06-28 16:39:49 +03:00
} ;
2009-10-25 18:12:12 +03:00
2017-06-28 16:39:49 +03:00
static void g_lock_unlock_fn ( struct db_record * rec ,
void * private_data )
{
struct g_lock_unlock_state * state = private_data ;
TDB_DATA value ;
struct g_lock lck ;
size_t i ;
bool ok ;
2009-10-25 18:12:12 +03:00
2011-08-17 13:21:31 +04:00
value = dbwrap_record_get_value ( rec ) ;
2017-06-28 16:39:49 +03:00
ok = g_lock_parse ( value . dptr , value . dsize , & lck ) ;
if ( ! ok ) {
2019-05-06 17:34:15 +03:00
DBG_DEBUG ( " g_lock_parse for %s failed \n " ,
2017-12-03 22:47:02 +03:00
hex_encode_talloc ( talloc_tos ( ) ,
state - > key . dptr ,
state - > key . dsize ) ) ;
2017-06-28 16:39:49 +03:00
state - > status = NT_STATUS_FILE_INVALID ;
return ;
2009-10-25 18:12:12 +03:00
}
2017-06-28 16:39:49 +03:00
for ( i = 0 ; i < lck . num_recs ; i + + ) {
struct g_lock_rec lockrec ;
g_lock_get_rec ( & lck , i , & lockrec ) ;
if ( serverid_equal ( & state - > self , & lockrec . pid ) ) {
2009-10-25 18:12:12 +03:00
break ;
}
}
2017-06-28 16:39:49 +03:00
if ( i = = lck . num_recs ) {
DBG_DEBUG ( " Lock not found, num_rec=%zu \n " , lck . num_recs ) ;
state - > status = NT_STATUS_NOT_FOUND ;
return ;
2009-10-25 18:12:12 +03:00
}
2017-06-28 16:39:49 +03:00
g_lock_rec_del ( & lck , i ) ;
2009-10-25 18:12:12 +03:00
2017-06-28 16:39:49 +03:00
if ( ( lck . num_recs = = 0 ) & & ( lck . datalen = = 0 ) ) {
state - > status = dbwrap_record_delete ( rec ) ;
return ;
2009-10-25 18:12:12 +03:00
}
2017-06-28 16:39:49 +03:00
state - > status = g_lock_store ( rec , & lck , NULL ) ;
}
2017-12-03 22:47:02 +03:00
NTSTATUS g_lock_unlock ( struct g_lock_ctx * ctx , TDB_DATA key )
2017-06-28 16:39:49 +03:00
{
struct g_lock_unlock_state state = {
2017-12-03 22:47:02 +03:00
. self = messaging_server_id ( ctx - > msg ) , . key = key
2017-06-28 16:39:49 +03:00
} ;
NTSTATUS status ;
2017-12-03 22:47:02 +03:00
status = dbwrap_do_locked ( ctx - > db , key , g_lock_unlock_fn , & state ) ;
2009-10-25 18:12:12 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2017-06-28 16:39:49 +03:00
DBG_WARNING ( " dbwrap_do_locked failed: %s \n " ,
nt_errstr ( status ) ) ;
return status ;
}
if ( ! NT_STATUS_IS_OK ( state . status ) ) {
DBG_WARNING ( " g_lock_unlock_fn failed: %s \n " ,
nt_errstr ( state . status ) ) ;
return state . status ;
2009-10-25 18:12:12 +03:00
}
2017-06-28 16:39:49 +03:00
return NT_STATUS_OK ;
2009-10-25 18:12:12 +03:00
}
2017-06-30 20:42:50 +03:00
struct g_lock_write_data_state {
2017-12-03 22:47:02 +03:00
TDB_DATA key ;
2017-06-30 20:42:50 +03:00
struct server_id self ;
const uint8_t * data ;
size_t datalen ;
2017-05-23 13:32:24 +03:00
NTSTATUS status ;
2017-06-30 20:42:50 +03:00
} ;
2017-05-23 13:32:24 +03:00
2017-06-30 20:42:50 +03:00
static void g_lock_write_data_fn ( struct db_record * rec ,
void * private_data )
{
struct g_lock_write_data_state * state = private_data ;
TDB_DATA value ;
struct g_lock lck ;
size_t i ;
bool ok ;
2017-05-23 13:32:24 +03:00
value = dbwrap_record_get_value ( rec ) ;
2017-06-30 20:42:50 +03:00
ok = g_lock_parse ( value . dptr , value . dsize , & lck ) ;
if ( ! ok ) {
2017-12-03 22:47:02 +03:00
DBG_DEBUG ( " g_lock_parse for %s failed \n " ,
hex_encode_talloc ( talloc_tos ( ) ,
state - > key . dptr ,
state - > key . dsize ) ) ;
2017-06-30 20:42:50 +03:00
state - > status = NT_STATUS_INTERNAL_DB_CORRUPTION ;
return ;
2017-05-23 13:32:24 +03:00
}
2017-06-30 20:42:50 +03:00
for ( i = 0 ; i < lck . num_recs ; i + + ) {
struct g_lock_rec lockrec ;
g_lock_get_rec ( & lck , i , & lockrec ) ;
if ( ( lockrec . lock_type = = G_LOCK_WRITE ) & &
serverid_equal ( & state - > self , & lockrec . pid ) ) {
2017-05-23 13:32:24 +03:00
break ;
}
}
2017-06-30 20:42:50 +03:00
if ( i = = lck . num_recs ) {
2017-05-23 13:32:24 +03:00
DBG_DEBUG ( " Not locked by us \n " ) ;
2017-06-30 20:42:50 +03:00
state - > status = NT_STATUS_NOT_LOCKED ;
return ;
2017-05-23 13:32:24 +03:00
}
2017-06-30 20:42:50 +03:00
lck . data = discard_const_p ( uint8_t , state - > data ) ;
lck . datalen = state - > datalen ;
state - > status = g_lock_store ( rec , & lck , NULL ) ;
}
2017-05-23 13:32:24 +03:00
2017-12-03 22:47:02 +03:00
NTSTATUS g_lock_write_data ( struct g_lock_ctx * ctx , TDB_DATA key ,
2017-06-30 20:42:50 +03:00
const uint8_t * buf , size_t buflen )
{
struct g_lock_write_data_state state = {
2017-12-03 22:47:02 +03:00
. key = key , . self = messaging_server_id ( ctx - > msg ) ,
2017-06-30 20:42:50 +03:00
. data = buf , . datalen = buflen
} ;
NTSTATUS status ;
2017-12-03 22:47:02 +03:00
status = dbwrap_do_locked ( ctx - > db , key ,
2017-06-30 20:42:50 +03:00
g_lock_write_data_fn , & state ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_WARNING ( " dbwrap_do_locked failed: %s \n " ,
nt_errstr ( status ) ) ;
return status ;
}
if ( ! NT_STATUS_IS_OK ( state . status ) ) {
DBG_WARNING ( " g_lock_write_data_fn failed: %s \n " ,
nt_errstr ( state . status ) ) ;
return state . status ;
}
return NT_STATUS_OK ;
2017-05-23 13:32:24 +03:00
}
2009-10-25 18:12:12 +03:00
struct g_lock_locks_state {
2017-12-03 22:47:02 +03:00
int ( * fn ) ( TDB_DATA key , void * private_data ) ;
2009-10-25 18:12:12 +03:00
void * private_data ;
} ;
static int g_lock_locks_fn ( struct db_record * rec , void * priv )
{
2011-08-17 13:21:31 +04:00
TDB_DATA key ;
2009-10-25 18:12:12 +03:00
struct g_lock_locks_state * state = ( struct g_lock_locks_state * ) priv ;
2011-08-17 13:21:31 +04:00
key = dbwrap_record_get_key ( rec ) ;
2017-12-03 22:47:02 +03:00
return state - > fn ( key , state - > private_data ) ;
2009-10-25 18:12:12 +03:00
}
int g_lock_locks ( struct g_lock_ctx * ctx ,
2017-12-03 22:47:02 +03:00
int ( * fn ) ( TDB_DATA key , void * private_data ) ,
2009-10-25 18:12:12 +03:00
void * private_data )
{
struct g_lock_locks_state state ;
2011-08-17 13:21:31 +04:00
NTSTATUS status ;
int count ;
2009-10-25 18:12:12 +03:00
state . fn = fn ;
state . private_data = private_data ;
2011-08-17 13:21:31 +04:00
status = dbwrap_traverse_read ( ctx - > db , g_lock_locks_fn , & state , & count ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return - 1 ;
}
2017-05-17 17:43:01 +03:00
return count ;
2009-10-25 18:12:12 +03:00
}
2017-06-30 23:09:12 +03:00
struct g_lock_dump_state {
TALLOC_CTX * mem_ctx ;
2017-12-03 22:47:02 +03:00
TDB_DATA key ;
2017-06-30 23:09:12 +03:00
void ( * fn ) ( const struct g_lock_rec * locks ,
size_t num_locks ,
const uint8_t * data ,
size_t datalen ,
void * private_data ) ;
void * private_data ;
NTSTATUS status ;
} ;
static void g_lock_dump_fn ( TDB_DATA key , TDB_DATA data ,
void * private_data )
{
struct g_lock_dump_state * state = private_data ;
struct g_lock_rec * recs ;
struct g_lock lck ;
size_t i ;
bool ok ;
ok = g_lock_parse ( data . dptr , data . dsize , & lck ) ;
if ( ! ok ) {
DBG_DEBUG ( " g_lock_parse failed for %s \n " ,
2017-12-03 22:47:02 +03:00
hex_encode_talloc ( talloc_tos ( ) ,
state - > key . dptr ,
state - > key . dsize ) ) ;
2017-06-30 23:09:12 +03:00
state - > status = NT_STATUS_INTERNAL_DB_CORRUPTION ;
return ;
}
recs = talloc_array ( state - > mem_ctx , struct g_lock_rec , lck . num_recs ) ;
if ( recs = = NULL ) {
DBG_DEBUG ( " talloc failed \n " ) ;
state - > status = NT_STATUS_NO_MEMORY ;
return ;
}
for ( i = 0 ; i < lck . num_recs ; i + + ) {
g_lock_get_rec ( & lck , i , & recs [ i ] ) ;
}
state - > fn ( recs , lck . num_recs , lck . data , lck . datalen ,
state - > private_data ) ;
TALLOC_FREE ( recs ) ;
state - > status = NT_STATUS_OK ;
}
2017-12-03 22:47:02 +03:00
NTSTATUS g_lock_dump ( struct g_lock_ctx * ctx , TDB_DATA key ,
2017-05-18 16:27:46 +03:00
void ( * fn ) ( const struct g_lock_rec * locks ,
size_t num_locks ,
const uint8_t * data ,
size_t datalen ,
void * private_data ) ,
2009-10-25 18:12:12 +03:00
void * private_data )
{
2017-06-30 23:09:12 +03:00
struct g_lock_dump_state state = {
2017-12-03 22:47:02 +03:00
. mem_ctx = ctx , . key = key ,
2017-06-30 23:09:12 +03:00
. fn = fn , . private_data = private_data
} ;
2011-08-17 13:21:31 +04:00
NTSTATUS status ;
2009-10-25 18:12:12 +03:00
2017-12-03 22:47:02 +03:00
status = dbwrap_parse_record ( ctx - > db , key , g_lock_dump_fn , & state ) ;
2011-08-17 13:21:31 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2017-06-30 23:09:12 +03:00
DBG_DEBUG ( " dbwrap_parse_record returned %s \n " ,
nt_errstr ( status ) ) ;
2011-08-17 13:21:31 +04:00
return status ;
2009-10-25 18:12:12 +03:00
}
2017-06-30 23:09:12 +03:00
if ( ! NT_STATUS_IS_OK ( state . status ) ) {
DBG_DEBUG ( " g_lock_dump_fn returned %s \n " ,
nt_errstr ( state . status ) ) ;
return state . status ;
2009-10-25 18:12:12 +03:00
}
return NT_STATUS_OK ;
}
2010-03-12 16:22:54 +03:00
static bool g_lock_init_all ( TALLOC_CTX * mem_ctx ,
struct tevent_context * * pev ,
struct messaging_context * * pmsg ,
struct g_lock_ctx * * pg_ctx )
{
struct tevent_context * ev = NULL ;
struct messaging_context * msg = NULL ;
struct g_lock_ctx * g_ctx = NULL ;
2013-02-18 12:10:34 +04:00
ev = samba_tevent_context_init ( mem_ctx ) ;
2010-03-12 16:22:54 +03:00
if ( ev = = NULL ) {
d_fprintf ( stderr , " ERROR: could not init event context \n " ) ;
goto fail ;
}
2011-12-12 17:55:54 +04:00
msg = messaging_init ( mem_ctx , ev ) ;
2010-03-12 16:22:54 +03:00
if ( msg = = NULL ) {
d_fprintf ( stderr , " ERROR: could not init messaging context \n " ) ;
goto fail ;
}
g_ctx = g_lock_ctx_init ( mem_ctx , msg ) ;
if ( g_ctx = = NULL ) {
d_fprintf ( stderr , " ERROR: could not init g_lock context \n " ) ;
goto fail ;
}
* pev = ev ;
* pmsg = msg ;
* pg_ctx = g_ctx ;
return true ;
fail :
TALLOC_FREE ( g_ctx ) ;
TALLOC_FREE ( msg ) ;
TALLOC_FREE ( ev ) ;
return false ;
}
2017-12-03 22:47:02 +03:00
NTSTATUS g_lock_do ( TDB_DATA key , enum g_lock_type lock_type ,
2011-12-12 17:55:54 +04:00
struct timeval timeout ,
2010-03-12 16:22:54 +03:00
void ( * fn ) ( void * private_data ) , void * private_data )
{
struct tevent_context * ev = NULL ;
struct messaging_context * msg = NULL ;
struct g_lock_ctx * g_ctx = NULL ;
NTSTATUS status ;
2011-12-12 17:55:54 +04:00
if ( ! g_lock_init_all ( talloc_tos ( ) , & ev , & msg , & g_ctx ) ) {
2010-03-12 16:22:54 +03:00
status = NT_STATUS_ACCESS_DENIED ;
goto done ;
}
2017-12-03 22:47:02 +03:00
status = g_lock_lock ( g_ctx , key , lock_type , timeout ) ;
2010-03-12 16:22:54 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto done ;
}
fn ( private_data ) ;
2017-12-03 22:47:02 +03:00
g_lock_unlock ( g_ctx , key ) ;
2010-03-12 16:22:54 +03:00
done :
TALLOC_FREE ( g_ctx ) ;
TALLOC_FREE ( msg ) ;
TALLOC_FREE ( ev ) ;
return status ;
}