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-05-18 11:37:30 +03:00
static ssize_t g_lock_put ( uint8_t * buf , size_t buflen ,
const struct g_lock_rec * locks ,
2017-05-18 14:59:20 +03:00
size_t num_locks ,
const uint8_t * data , size_t datalen )
2017-05-18 11:37:30 +03:00
{
size_t i , len , ofs ;
if ( num_locks > UINT32_MAX / G_LOCK_REC_LENGTH ) {
return - 1 ;
}
len = num_locks * G_LOCK_REC_LENGTH ;
2017-05-18 14:59:20 +03:00
len + = sizeof ( uint32_t ) ;
if ( len < sizeof ( uint32_t ) ) {
return - 1 ;
}
len + = datalen ;
if ( len < datalen ) {
return - 1 ;
}
2017-05-18 11:37:30 +03:00
if ( len > buflen ) {
return len ;
}
ofs = 0 ;
2017-05-18 14:59:20 +03:00
SIVAL ( buf , ofs , num_locks ) ;
ofs + = sizeof ( uint32_t ) ;
2017-05-18 11:37:30 +03:00
for ( i = 0 ; i < num_locks ; i + + ) {
g_lock_rec_put ( buf + ofs , locks [ i ] ) ;
ofs + = G_LOCK_REC_LENGTH ;
}
2017-05-18 14:59:20 +03:00
if ( ( data ! = NULL ) & & ( datalen ! = 0 ) ) {
memcpy ( buf + ofs , data , datalen ) ;
}
2017-05-18 11:37:30 +03:00
return len ;
}
2017-05-18 14:59:20 +03:00
static ssize_t g_lock_get ( TDB_DATA recval ,
struct g_lock_rec * locks , size_t num_locks ,
uint8_t * * data , size_t * datalen )
2017-05-18 11:37:30 +03:00
{
2017-05-18 14:59:20 +03:00
size_t found_locks ;
2017-05-18 11:37:30 +03:00
2017-05-18 14:59:20 +03:00
if ( recval . dsize < sizeof ( uint32_t ) ) {
/* Fresh or invalid record */
found_locks = 0 ;
goto done ;
2017-05-18 11:37:30 +03:00
}
2017-05-18 14:59:20 +03:00
found_locks = IVAL ( recval . dptr , 0 ) ;
recval . dptr + = sizeof ( uint32_t ) ;
recval . dsize - = sizeof ( uint32_t ) ;
if ( found_locks > recval . dsize / G_LOCK_REC_LENGTH ) {
/* Invalid record */
return 0 ;
2017-05-18 11:37:30 +03:00
}
2017-05-18 14:59:20 +03:00
if ( found_locks < = num_locks ) {
size_t i ;
for ( i = 0 ; i < found_locks ; i + + ) {
g_lock_rec_get ( & locks [ i ] , recval . dptr ) ;
recval . dptr + = G_LOCK_REC_LENGTH ;
recval . dsize - = G_LOCK_REC_LENGTH ;
}
} else {
/*
* Not enough space passed in by the caller , don ' t
* parse the locks .
*/
recval . dptr + = found_locks * G_LOCK_REC_LENGTH ;
recval . dsize - = found_locks * G_LOCK_REC_LENGTH ;
2017-05-18 11:37:30 +03:00
}
2017-05-18 14:59:20 +03:00
done :
if ( data ! = NULL ) {
* data = recval . dptr ;
}
if ( datalen ! = NULL ) {
* datalen = recval . dsize ;
}
return found_locks ;
}
static NTSTATUS g_lock_get_talloc ( TALLOC_CTX * mem_ctx , TDB_DATA recval ,
struct g_lock_rec * * plocks ,
size_t * pnum_locks ,
uint8_t * * data , size_t * datalen )
{
struct g_lock_rec * locks ;
ssize_t num_locks ;
num_locks = g_lock_get ( recval , NULL , 0 , NULL , NULL ) ;
if ( num_locks = = - 1 ) {
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
locks = talloc_array ( mem_ctx , struct g_lock_rec , num_locks ) ;
if ( locks = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
g_lock_get ( recval , locks , num_locks , data , datalen ) ;
2017-05-18 11:37:30 +03:00
* plocks = locks ;
* pnum_locks = num_locks ;
2017-05-18 14:59:20 +03:00
return NT_STATUS_OK ;
2017-05-18 11:37:30 +03:00
}
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 ;
2014-11-02 22:21:40 +03:00
db_path = lock_path ( " g_lock.tdb " ) ;
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 ,
DBWRAP_LOCK_ORDER_2 ,
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 ) {
2011-09-20 00:30:57 +04:00
DEBUG ( 1 , ( " g_lock_init: 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
result - > db = db_open_watched ( result , backend , msg ) ;
if ( result - > db = = NULL ) {
DBG_WARNING ( " g_lock_init: db_open_watched failed \n " ) ;
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 ;
}
2017-05-17 06:52:56 +03:00
static NTSTATUS g_lock_record_store ( struct db_record * rec ,
const struct g_lock_rec * locks ,
2017-05-18 17:22:15 +03:00
size_t num_locks ,
const uint8_t * data , size_t datalen )
2017-05-17 06:52:56 +03:00
{
ssize_t len ;
uint8_t * buf ;
NTSTATUS status ;
2017-05-18 17:22:15 +03:00
len = g_lock_put ( NULL , 0 , locks , num_locks , data , datalen ) ;
2017-05-17 06:52:56 +03:00
if ( len = = - 1 ) {
return NT_STATUS_BUFFER_TOO_SMALL ;
}
buf = talloc_array ( rec , uint8_t , len ) ;
if ( buf = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
2017-05-18 17:22:15 +03:00
g_lock_put ( buf , len , locks , num_locks , data , datalen ) ;
2017-05-17 06:52:56 +03:00
status = dbwrap_record_store (
rec , ( TDB_DATA ) { . dptr = buf , . dsize = len } , 0 ) ;
TALLOC_FREE ( buf ) ;
return status ;
}
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-05-18 17:22:15 +03:00
TDB_DATA data , userdata ;
2017-05-19 17:57:00 +03:00
size_t i , num_locks , my_lock ;
2012-02-15 19:38:43 +04:00
struct g_lock_rec * locks , * tmp ;
NTSTATUS status ;
bool modified = false ;
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-05-18 14:59:20 +03:00
status = g_lock_get_talloc ( talloc_tos ( ) , data , & locks , & num_locks ,
2017-05-18 17:22:15 +03:00
& userdata . dptr , & userdata . dsize ) ;
2017-05-18 14:59:20 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2012-02-15 19:38:43 +04:00
}
2010-02-15 18:57:16 +03:00
2017-05-19 17:57:00 +03:00
my_lock = num_locks ; /* doesn't exist yet */
2010-02-15 18:57:16 +03:00
for ( i = 0 ; i < num_locks ; i + + ) {
2017-05-19 17:57:00 +03:00
struct g_lock_rec * lock = & locks [ i ] ;
if ( serverid_equal ( & self , & lock - > pid ) ) {
if ( lock - > lock_type = = type ) {
status = NT_STATUS_WAS_LOCKED ;
goto done ;
}
my_lock = i ;
break ;
}
}
for ( i = 0 ; i < num_locks ; i + + ) {
if ( i = = my_lock ) {
continue ;
2009-10-25 18:12:12 +03:00
}
2017-05-19 17:57:00 +03:00
2012-02-15 19:38:43 +04:00
if ( g_lock_conflicts ( type , locks [ i ] . lock_type ) ) {
2012-08-22 14:35:29 +04:00
struct server_id pid = locks [ i ] . 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 ;
2016-07-12 17:07:51 +03:00
* blocker = locks [ i ] . pid ;
2012-02-15 19:38:43 +04:00
goto done ;
}
/*
* Delete stale conflicting entry
*/
2009-10-25 18:12:12 +03:00
locks [ i ] = locks [ num_locks - 1 ] ;
2012-02-15 19:38:43 +04:00
num_locks - = 1 ;
modified = true ;
2009-10-25 18:12:12 +03:00
}
}
2017-05-19 17:57:00 +03:00
if ( my_lock > = num_locks ) {
tmp = talloc_realloc ( talloc_tos ( ) , locks , struct g_lock_rec ,
num_locks + 1 ) ;
if ( tmp = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto done ;
}
locks = tmp ;
my_lock = num_locks ;
num_locks + = 1 ;
2009-10-25 18:12:12 +03:00
}
2017-05-19 17:57:00 +03:00
locks [ my_lock ] = ( struct g_lock_rec ) { . pid = self , . lock_type = type } ;
2012-02-15 19:38:43 +04:00
modified = true ;
status = NT_STATUS_OK ;
done :
if ( modified ) {
NTSTATUS store_status ;
2017-05-18 17:22:15 +03:00
store_status = g_lock_record_store (
rec , locks , num_locks , userdata . dptr , userdata . dsize ) ;
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 ;
}
}
TALLOC_FREE ( locks ) ;
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 ;
const char * name ;
enum g_lock_type type ;
} ;
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 * g_lock_lock_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
struct g_lock_ctx * ctx ,
const char * name ,
enum g_lock_type type )
2009-10-25 18:12:12 +03:00
{
2012-02-15 19:38:43 +04:00
struct tevent_req * req , * subreq ;
struct g_lock_lock_state * state ;
struct db_record * rec ;
2016-07-12 17:07:51 +03:00
struct server_id self , blocker ;
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 ;
state - > name = name ;
state - > type = type ;
2009-10-25 18:12:12 +03:00
2011-08-17 13:21:31 +04:00
rec = dbwrap_fetch_locked ( ctx - > db , talloc_tos ( ) ,
2012-02-15 19:38:43 +04:00
string_term_tdb_data ( state - > name ) ) ;
2009-10-25 18:12:12 +03:00
if ( rec = = NULL ) {
DEBUG ( 10 , ( " fetch_locked( \" %s \" ) failed \n " , name ) ) ;
2012-02-15 19:38:43 +04:00
tevent_req_nterror ( req , NT_STATUS_LOCK_NOT_GRANTED ) ;
return tevent_req_post ( req , ev ) ;
2009-10-25 18:12:12 +03:00
}
2012-02-15 19:38:43 +04:00
self = messaging_server_id ( state - > ctx - > msg ) ;
2009-10-25 18:12:12 +03:00
2016-07-12 17:07:51 +03:00
status = g_lock_trylock ( rec , self , state - > type , & blocker ) ;
2012-02-15 19:38:43 +04:00
if ( NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( rec ) ;
tevent_req_done ( req ) ;
return tevent_req_post ( req , ev ) ;
2009-10-25 18:12:12 +03:00
}
2012-02-15 19:38:43 +04:00
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_LOCK_NOT_GRANTED ) ) {
TALLOC_FREE ( rec ) ;
tevent_req_nterror ( req , status ) ;
return tevent_req_post ( req , ev ) ;
2009-10-25 18:12:12 +03:00
}
2016-07-13 08:26:52 +03:00
subreq = dbwrap_watched_watch_send ( state , state - > ev , rec , blocker ) ;
2009-10-25 18:12:12 +03:00
TALLOC_FREE ( rec ) ;
2012-02-15 19:38:43 +04:00
if ( tevent_req_nomem ( subreq , req ) ) {
return tevent_req_post ( req , ev ) ;
2009-10-25 18:12:12 +03:00
}
2012-08-10 19:00:38 +04:00
if ( ! tevent_req_set_endtime (
subreq , state - > ev ,
timeval_current_ofs ( 5 + sys_random ( ) % 5 , 0 ) ) ) {
tevent_req_oom ( req ) ;
return tevent_req_post ( req , ev ) ;
}
2012-02-15 19:38:43 +04:00
tevent_req_set_callback ( subreq , g_lock_lock_retry , req ) ;
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 ) ;
struct server_id self = messaging_server_id ( state - > ctx - > msg ) ;
2016-07-12 17:07:51 +03:00
struct server_id blocker ;
2012-02-15 19:38:43 +04:00
struct db_record * rec ;
2009-10-25 18:12:12 +03:00
NTSTATUS status ;
2016-07-13 08:26:52 +03:00
status = dbwrap_watched_watch_recv ( subreq , talloc_tos ( ) , & rec , NULL ,
NULL ) ;
2012-02-15 19:38:43 +04:00
TALLOC_FREE ( subreq ) ;
2012-08-10 19:00:38 +04:00
if ( NT_STATUS_EQUAL ( status , NT_STATUS_IO_TIMEOUT ) ) {
rec = dbwrap_fetch_locked (
state - > ctx - > db , talloc_tos ( ) ,
string_term_tdb_data ( state - > name ) ) ;
if ( rec = = NULL ) {
status = map_nt_error_from_unix ( errno ) ;
} else {
status = NT_STATUS_OK ;
}
}
2012-02-15 19:38:43 +04:00
if ( tevent_req_nterror ( req , status ) ) {
return ;
2009-10-25 18:12:12 +03:00
}
2016-07-12 17:07:51 +03:00
status = g_lock_trylock ( rec , self , state - > type , & blocker ) ;
2012-02-15 19:38:43 +04:00
if ( NT_STATUS_IS_OK ( status ) ) {
TALLOC_FREE ( rec ) ;
tevent_req_done ( req ) ;
return ;
2009-10-25 18:12:12 +03:00
}
2012-02-15 19:38:43 +04:00
if ( ! NT_STATUS_EQUAL ( status , NT_STATUS_LOCK_NOT_GRANTED ) ) {
TALLOC_FREE ( rec ) ;
tevent_req_nterror ( req , status ) ;
return ;
2009-10-25 18:12:12 +03:00
}
2016-07-13 08:26:52 +03:00
subreq = dbwrap_watched_watch_send ( state , state - > ev , rec , blocker ) ;
2012-08-10 15:42:51 +04:00
TALLOC_FREE ( rec ) ;
2012-02-15 19:38:43 +04:00
if ( tevent_req_nomem ( subreq , req ) ) {
return ;
2009-10-25 18:12:12 +03:00
}
2012-08-10 19:00:38 +04:00
if ( ! tevent_req_set_endtime (
subreq , state - > ev ,
timeval_current_ofs ( 5 + sys_random ( ) % 5 , 0 ) ) ) {
tevent_req_oom ( req ) ;
return ;
}
2012-02-15 19:38:43 +04:00
tevent_req_set_callback ( subreq , g_lock_lock_retry , req ) ;
return ;
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
2012-02-15 19:38:43 +04:00
NTSTATUS g_lock_lock ( struct g_lock_ctx * ctx , const char * name ,
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 ;
}
req = g_lock_lock_send ( frame , ev , ctx , name , type ) ;
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
}
2012-02-15 19:38:43 +04:00
NTSTATUS g_lock_unlock ( struct g_lock_ctx * ctx , const char * name )
2009-10-25 18:12:12 +03:00
{
2012-02-15 19:38:43 +04:00
struct server_id self = messaging_server_id ( ctx - > msg ) ;
2009-10-25 18:12:12 +03:00
struct db_record * rec = NULL ;
struct g_lock_rec * locks = NULL ;
2017-05-18 14:59:20 +03:00
size_t i , num_locks ;
2009-10-25 18:12:12 +03:00
NTSTATUS status ;
2017-05-18 17:22:15 +03:00
TDB_DATA value , userdata ;
2009-10-25 18:12:12 +03:00
2011-08-17 13:21:31 +04:00
rec = dbwrap_fetch_locked ( ctx - > db , talloc_tos ( ) ,
string_term_tdb_data ( name ) ) ;
2009-10-25 18:12:12 +03:00
if ( rec = = NULL ) {
DEBUG ( 10 , ( " fetch_locked( \" %s \" ) failed \n " , name ) ) ;
status = NT_STATUS_INTERNAL_ERROR ;
goto done ;
}
2011-08-17 13:21:31 +04:00
value = dbwrap_record_get_value ( rec ) ;
2017-05-18 14:59:20 +03:00
status = g_lock_get_talloc ( talloc_tos ( ) , value , & locks , & num_locks ,
2017-05-18 17:22:15 +03:00
& userdata . dptr , & userdata . dsize ) ;
2017-05-18 14:59:20 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " g_lock_get for %s failed: %s \n " , name ,
nt_errstr ( status ) ) ;
2011-08-31 12:19:18 +04:00
status = NT_STATUS_FILE_INVALID ;
2009-10-25 18:12:12 +03:00
goto done ;
}
for ( i = 0 ; i < num_locks ; i + + ) {
2012-06-16 02:26:26 +04:00
if ( serverid_equal ( & self , & locks [ i ] . pid ) ) {
2009-10-25 18:12:12 +03:00
break ;
}
}
if ( i = = num_locks ) {
2017-05-18 14:59:20 +03:00
DBG_DEBUG ( " Lock not found, num_locks=%zu \n " , num_locks ) ;
2011-08-31 12:19:18 +04:00
status = NT_STATUS_NOT_FOUND ;
2009-10-25 18:12:12 +03:00
goto done ;
}
2012-02-15 19:38:43 +04:00
locks [ i ] = locks [ num_locks - 1 ] ;
2009-10-25 18:12:12 +03:00
num_locks - = 1 ;
2017-05-18 17:22:15 +03:00
if ( ( num_locks = = 0 ) & & ( userdata . dsize = = 0 ) ) {
2011-08-17 13:21:31 +04:00
status = dbwrap_record_delete ( rec ) ;
2009-10-25 18:12:12 +03:00
} else {
2017-05-18 17:22:15 +03:00
status = g_lock_record_store (
rec , locks , num_locks , userdata . dptr , userdata . dsize ) ;
2009-10-25 18:12:12 +03:00
}
if ( ! NT_STATUS_IS_OK ( status ) ) {
2017-05-24 14:27:18 +03:00
DBG_WARNING ( " Could not store record: %s \n " , nt_errstr ( status ) ) ;
2009-10-25 18:12:12 +03:00
goto done ;
}
2012-02-15 19:38:43 +04:00
status = NT_STATUS_OK ;
2009-10-25 18:12:12 +03:00
done :
2010-02-16 14:31:58 +03:00
TALLOC_FREE ( rec ) ;
2009-10-25 18:12:12 +03:00
TALLOC_FREE ( locks ) ;
return status ;
}
2017-05-23 13:32:24 +03:00
NTSTATUS g_lock_write_data ( struct g_lock_ctx * ctx , const char * name ,
const uint8_t * buf , size_t buflen )
{
struct server_id self = messaging_server_id ( ctx - > msg ) ;
struct db_record * rec = NULL ;
struct g_lock_rec * locks = NULL ;
size_t i , num_locks ;
NTSTATUS status ;
TDB_DATA value ;
rec = dbwrap_fetch_locked ( ctx - > db , talloc_tos ( ) ,
string_term_tdb_data ( name ) ) ;
if ( rec = = NULL ) {
DEBUG ( 10 , ( " fetch_locked( \" %s \" ) failed \n " , name ) ) ;
status = NT_STATUS_INTERNAL_ERROR ;
goto done ;
}
value = dbwrap_record_get_value ( rec ) ;
status = g_lock_get_talloc ( talloc_tos ( ) , value , & locks , & num_locks ,
NULL , NULL ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " g_lock_get for %s failed: %s \n " , name ,
nt_errstr ( status ) ) ;
status = NT_STATUS_FILE_INVALID ;
goto done ;
}
for ( i = 0 ; i < num_locks ; i + + ) {
if ( server_id_equal ( & self , & locks [ i ] . pid ) & &
( locks [ i ] . lock_type = = G_LOCK_WRITE ) ) {
break ;
}
}
if ( i = = num_locks ) {
DBG_DEBUG ( " Not locked by us \n " ) ;
status = NT_STATUS_NOT_LOCKED ;
goto done ;
}
status = g_lock_record_store ( rec , locks , num_locks , buf , buflen ) ;
done :
TALLOC_FREE ( locks ) ;
TALLOC_FREE ( rec ) ;
return status ;
}
2009-10-25 18:12:12 +03:00
struct g_lock_locks_state {
int ( * fn ) ( const char * name , void * private_data ) ;
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 ) ;
if ( ( key . dsize = = 0 ) | | ( key . dptr [ key . dsize - 1 ] ! = 0 ) ) {
2009-10-25 18:12:12 +03:00
DEBUG ( 1 , ( " invalid key in g_lock.tdb, ignoring \n " ) ) ;
return 0 ;
}
2011-08-17 13:21:31 +04:00
return state - > fn ( ( char * ) key . dptr , state - > private_data ) ;
2009-10-25 18:12:12 +03:00
}
int g_lock_locks ( struct g_lock_ctx * ctx ,
int ( * fn ) ( const char * name , void * private_data ) ,
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
}
NTSTATUS g_lock_dump ( struct g_lock_ctx * ctx , const char * name ,
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 )
{
TDB_DATA data ;
2017-05-18 16:27:46 +03:00
size_t num_locks ;
2009-10-25 18:12:12 +03:00
struct g_lock_rec * locks = NULL ;
2017-05-18 16:27:46 +03:00
uint8_t * userdata ;
size_t userdatalen ;
2011-08-17 13:21:31 +04:00
NTSTATUS status ;
2009-10-25 18:12:12 +03:00
2011-08-17 13:21:31 +04:00
status = dbwrap_fetch_bystring ( ctx - > db , talloc_tos ( ) , name , & data ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2009-10-25 18:12:12 +03:00
}
if ( ( data . dsize = = 0 ) | | ( data . dptr = = NULL ) ) {
return NT_STATUS_OK ;
}
2017-05-18 14:59:20 +03:00
status = g_lock_get_talloc ( talloc_tos ( ) , data , & locks , & num_locks ,
2017-05-18 16:27:46 +03:00
& userdata , & userdatalen ) ;
2009-10-25 18:12:12 +03:00
2017-05-18 14:59:20 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DBG_DEBUG ( " g_lock_get for %s failed: %s \n " , name ,
nt_errstr ( status ) ) ;
2017-05-18 16:27:46 +03:00
TALLOC_FREE ( data . dptr ) ;
2009-10-25 18:12:12 +03:00
return NT_STATUS_INTERNAL_ERROR ;
}
2017-05-18 16:27:46 +03:00
fn ( locks , num_locks , userdata , userdatalen , private_data ) ;
2009-10-25 18:12:12 +03:00
TALLOC_FREE ( locks ) ;
2017-05-18 16:27:46 +03:00
TALLOC_FREE ( data . dptr ) ;
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 ;
}
NTSTATUS g_lock_do ( const char * name , 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 ;
}
status = g_lock_lock ( g_ctx , name , lock_type , timeout ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
goto done ;
}
fn ( private_data ) ;
g_lock_unlock ( g_ctx , name ) ;
done :
TALLOC_FREE ( g_ctx ) ;
TALLOC_FREE ( msg ) ;
TALLOC_FREE ( ev ) ;
return status ;
}