2023-04-13 14:17:08 +03:00
/* We save the locks so we can reacquire them. */
2012-02-14 08:15:21 +04:00
# include "../common/tdb_private.h"
2012-02-13 21:35:43 +04:00
# include <unistd.h>
# include <fcntl.h>
# include <stdarg.h>
# include <stdlib.h>
2012-02-14 08:15:19 +04:00
# include "tap-interface.h"
2012-02-13 21:35:43 +04:00
# include "lock-tracking.h"
2012-08-10 23:50:22 +04:00
struct testlock {
struct testlock * next ;
2012-02-13 21:35:43 +04:00
unsigned int off ;
unsigned int len ;
int type ;
} ;
2012-08-10 23:50:22 +04:00
static struct testlock * testlocks ;
2012-02-13 21:35:43 +04:00
int locking_errors = 0 ;
bool suppress_lockcheck = false ;
bool nonblocking_locks ;
int locking_would_block = 0 ;
void ( * unlock_callback ) ( int fd ) ;
int fcntl_with_lockcheck ( int fd , int cmd , . . . /* arg */ )
{
va_list ap ;
int ret , arg3 ;
struct flock * fl ;
bool may_block = false ;
if ( cmd ! = F_SETLK & & cmd ! = F_SETLKW ) {
/* This may be totally bogus, but we don't know in general. */
va_start ( ap , cmd ) ;
arg3 = va_arg ( ap , int ) ;
va_end ( ap ) ;
return fcntl ( fd , cmd , arg3 ) ;
}
va_start ( ap , cmd ) ;
fl = va_arg ( ap , struct flock * ) ;
va_end ( ap ) ;
if ( cmd = = F_SETLKW & & nonblocking_locks ) {
cmd = F_SETLK ;
may_block = true ;
}
ret = fcntl ( fd , cmd , fl ) ;
/* Detect when we failed, but might have been OK if we waited. */
if ( may_block & & ret = = - 1 & & ( errno = = EAGAIN | | errno = = EACCES ) ) {
locking_would_block + + ;
}
if ( fl - > l_type = = F_UNLCK ) {
2012-08-10 23:50:22 +04:00
struct testlock * * l ;
struct testlock * old = NULL ;
2012-02-13 21:35:43 +04:00
2012-08-10 23:50:22 +04:00
for ( l = & testlocks ; * l ; l = & ( * l ) - > next ) {
2012-02-13 21:35:43 +04:00
if ( ( * l ) - > off = = fl - > l_start
& & ( * l ) - > len = = fl - > l_len ) {
if ( ret = = 0 ) {
old = * l ;
* l = ( * l ) - > next ;
free ( old ) ;
}
break ;
}
2013-02-14 19:10:31 +04:00
if ( ( ( * l ) - > off = = fl - > l_start )
& & ( ( * l ) - > len = = 0 )
& & ( ret = = 0 ) ) {
/*
* Remove a piece from the start of the
* allrecord_lock
*/
old = * l ;
( * l ) - > off + = fl - > l_len ;
break ;
}
2012-02-13 21:35:43 +04:00
}
if ( ! old & & ! suppress_lockcheck ) {
diag ( " Unknown unlock %u@%u - %i " ,
( int ) fl - > l_len , ( int ) fl - > l_start , ret ) ;
locking_errors + + ;
}
} else {
2012-08-10 23:50:22 +04:00
struct testlock * new , * i ;
2012-02-13 21:35:43 +04:00
unsigned int fl_end = fl - > l_start + fl - > l_len ;
if ( fl - > l_len = = 0 )
fl_end = ( unsigned int ) - 1 ;
/* Check for overlaps: we shouldn't do this. */
2012-08-10 23:50:22 +04:00
for ( i = testlocks ; i ; i = i - > next ) {
2012-02-13 21:35:43 +04:00
unsigned int i_end = i - > off + i - > len ;
if ( i - > len = = 0 )
i_end = ( unsigned int ) - 1 ;
if ( fl - > l_start > = i - > off & & fl - > l_start < i_end )
break ;
if ( fl_end > = i - > off & & fl_end < i_end )
break ;
/* tdb_allrecord_lock does this, handle adjacent: */
if ( fl - > l_start = = i_end & & fl - > l_type = = i - > type ) {
if ( ret = = 0 ) {
i - > len = fl - > l_len
? i - > len + fl - > l_len
: 0 ;
}
goto done ;
}
}
if ( i ) {
/* Special case: upgrade of allrecord lock. */
if ( i - > type = = F_RDLCK & & fl - > l_type = = F_WRLCK
& & i - > off = = FREELIST_TOP
& & fl - > l_start = = FREELIST_TOP
& & i - > len = = 0
& & fl - > l_len = = 0 ) {
if ( ret = = 0 )
i - > type = F_WRLCK ;
goto done ;
}
if ( ! suppress_lockcheck ) {
2012-08-10 23:50:22 +04:00
diag ( " %s testlock %u@%u overlaps %u@%u " ,
2012-02-13 21:35:43 +04:00
fl - > l_type = = F_WRLCK ? " write " : " read " ,
( int ) fl - > l_len , ( int ) fl - > l_start ,
i - > len , ( int ) i - > off ) ;
locking_errors + + ;
}
}
if ( ret = = 0 ) {
new = malloc ( sizeof * new ) ;
new - > off = fl - > l_start ;
new - > len = fl - > l_len ;
new - > type = fl - > l_type ;
2012-08-10 23:50:22 +04:00
new - > next = testlocks ;
testlocks = new ;
2012-02-13 21:35:43 +04:00
}
}
done :
if ( ret = = 0 & & fl - > l_type = = F_UNLCK & & unlock_callback )
unlock_callback ( fd ) ;
return ret ;
}
unsigned int forget_locking ( void )
{
unsigned int num = 0 ;
2012-08-10 23:50:22 +04:00
while ( testlocks ) {
struct testlock * next = testlocks - > next ;
free ( testlocks ) ;
testlocks = next ;
2012-02-13 21:35:43 +04:00
num + + ;
}
return num ;
}