2000-01-13 15:09:36 +03:00
/*
Unix SMB / Netbios implementation .
Version 3.0
byte range locking code
2000-04-26 00:30:58 +04:00
Updated to handle range splits / merges .
Copyright ( C ) Andrew Tridgell 1992 - 2000
Copyright ( C ) Jeremy Allison 1992 - 2000
2000-01-13 15:09:36 +03:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 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 , write to the Free Software
Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
2000-04-26 00:30:58 +04:00
/* This module implements a tdb based byte range locking service,
2000-01-13 15:09:36 +03:00
replacing the fcntl ( ) based byte range locking previously
used . This allows us to provide the same semantics as NT */
# include "includes.h"
extern int DEBUGLEVEL ;
2000-04-26 00:30:58 +04:00
/* This contains elements that differentiate locks. The smbpid is a
2000-01-13 15:09:36 +03:00
client supplied pid , and is essentially the locking context for
this client */
2000-04-26 00:30:58 +04:00
2000-01-13 15:09:36 +03:00
struct lock_context {
uint16 smbpid ;
uint16 tid ;
pid_t pid ;
} ;
2000-04-26 00:30:58 +04:00
/* The data in brlock records is an unsorted linear array of these
2000-01-13 15:09:36 +03:00
records . It is unnecessary to store the count as tdb provides the
size of the record */
2000-04-26 00:30:58 +04:00
2000-01-13 15:09:36 +03:00
struct lock_struct {
struct lock_context context ;
br_off start ;
br_off size ;
2000-01-14 07:32:57 +03:00
int fnum ;
2000-01-14 11:01:44 +03:00
enum brl_type lock_type ;
2000-01-13 15:09:36 +03:00
} ;
2000-04-26 00:30:58 +04:00
/* The key used in the brlock database. */
2000-01-13 15:09:36 +03:00
struct lock_key {
SMB_DEV_T device ;
SMB_INO_T inode ;
} ;
2000-04-26 00:30:58 +04:00
/* The open brlock.tdb database. */
2000-01-13 15:09:36 +03:00
2000-04-26 00:30:58 +04:00
static TDB_CONTEXT * tdb ;
2000-01-13 15:09:36 +03:00
2000-05-10 04:05:27 +04:00
/****************************************************************************
Create a locking key - ensuring zero filled for pad purposes .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static TDB_DATA locking_key ( SMB_DEV_T dev , SMB_INO_T inode )
{
static struct lock_key key ;
TDB_DATA kbuf ;
memset ( & key , ' \0 ' , sizeof ( key ) ) ;
key . device = dev ;
key . inode = inode ;
kbuf . dptr = ( char * ) & key ;
kbuf . dsize = sizeof ( key ) ;
return kbuf ;
}
2000-01-13 15:09:36 +03:00
/****************************************************************************
2000-04-26 00:30:58 +04:00
See if two locking contexts are equal .
2000-01-13 15:09:36 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-04-26 00:30:58 +04:00
2000-01-13 15:09:36 +03:00
static BOOL brl_same_context ( struct lock_context * ctx1 ,
struct lock_context * ctx2 )
{
return ( ctx1 - > pid = = ctx2 - > pid ) & &
( ctx1 - > smbpid = = ctx2 - > smbpid ) & &
( ctx1 - > tid = = ctx2 - > tid ) ;
}
/****************************************************************************
2000-04-26 00:30:58 +04:00
See if lock2 can be added when lock1 is in place .
2000-01-13 15:09:36 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-04-26 00:30:58 +04:00
2000-01-13 15:09:36 +03:00
static BOOL brl_conflict ( struct lock_struct * lck1 ,
struct lock_struct * lck2 )
{
2000-01-14 11:01:44 +03:00
if ( lck1 - > lock_type = = READ_LOCK & & lck2 - > lock_type = = READ_LOCK )
return False ;
2000-01-13 15:09:36 +03:00
if ( brl_same_context ( & lck1 - > context , & lck2 - > context ) & &
2000-05-03 06:27:41 +04:00
lck2 - > lock_type = = READ_LOCK & & lck1 - > fnum = = lck2 - > fnum ) return False ;
2000-01-13 15:09:36 +03:00
if ( lck1 - > start > = ( lck2 - > start + lck2 - > size ) | |
lck2 - > start > = ( lck1 - > start + lck1 - > size ) ) return False ;
return True ;
}
/****************************************************************************
2000-05-03 18:29:05 +04:00
delete a record if it is for a dead process
2000-01-13 15:09:36 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-05-03 18:29:05 +04:00
static int delete_fn ( TDB_CONTEXT * ttdb , TDB_DATA kbuf , TDB_DATA dbuf , void * state )
{
struct lock_struct * locks ;
int count , i ;
2000-12-06 03:05:15 +03:00
tdb_chainlock ( tdb , kbuf ) ;
2000-05-03 18:29:05 +04:00
locks = ( struct lock_struct * ) dbuf . dptr ;
count = dbuf . dsize / sizeof ( * locks ) ;
for ( i = 0 ; i < count ; i + + ) {
struct lock_struct * lock = & locks [ i ] ;
if ( process_exists ( lock - > context . pid ) ) continue ;
if ( count > 1 & & i < count - 1 ) {
memmove ( & locks [ i ] , & locks [ i + 1 ] ,
sizeof ( * locks ) * ( ( count - 1 ) - i ) ) ;
}
count - - ;
i - - ;
}
2000-04-26 00:30:58 +04:00
2000-05-03 18:29:05 +04:00
if ( count = = 0 ) {
tdb_delete ( tdb , kbuf ) ;
} else if ( count < ( dbuf . dsize / sizeof ( * locks ) ) ) {
2000-05-04 00:33:25 +04:00
dbuf . dsize = count * sizeof ( * locks ) ;
2000-05-03 18:29:05 +04:00
tdb_store ( tdb , kbuf , dbuf , TDB_REPLACE ) ;
}
2000-12-06 03:05:15 +03:00
tdb_chainunlock ( tdb , kbuf ) ;
2000-05-03 18:29:05 +04:00
return 0 ;
}
/****************************************************************************
Open up the brlock . tdb database .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-01-16 14:14:44 +03:00
void brl_init ( int read_only )
2000-01-13 15:09:36 +03:00
{
if ( tdb ) return ;
tdb = tdb_open ( lock_path ( " brlock.tdb " ) , 0 , TDB_CLEAR_IF_FIRST ,
2000-06-13 19:46:15 +04:00
read_only ? O_RDONLY : ( O_RDWR | O_CREAT ) , 0644 ) ;
2000-01-13 15:09:36 +03:00
if ( ! tdb ) {
DEBUG ( 0 , ( " Failed to open byte range locking database \n " ) ) ;
2000-05-03 18:29:05 +04:00
return ;
}
/* delete any dead locks */
if ( ! read_only ) {
tdb_traverse ( tdb , delete_fn , NULL ) ;
2000-01-13 15:09:36 +03:00
}
}
/****************************************************************************
2000-04-26 00:30:58 +04:00
Lock a range of bytes .
2000-01-13 15:09:36 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-04-26 00:30:58 +04:00
2000-01-14 07:32:57 +03:00
BOOL brl_lock ( SMB_DEV_T dev , SMB_INO_T ino , int fnum ,
2000-01-13 15:09:36 +03:00
uint16 smbpid , pid_t pid , uint16 tid ,
br_off start , br_off size ,
2000-01-14 11:01:44 +03:00
enum brl_type lock_type )
2000-01-13 15:09:36 +03:00
{
TDB_DATA kbuf , dbuf ;
int count , i ;
struct lock_struct lock , * locks ;
2000-05-10 04:05:27 +04:00
kbuf = locking_key ( dev , ino ) ;
2000-01-13 15:09:36 +03:00
dbuf . dptr = NULL ;
2000-12-06 03:05:15 +03:00
tdb_chainlock ( tdb , kbuf ) ;
2000-01-13 15:09:36 +03:00
dbuf = tdb_fetch ( tdb , kbuf ) ;
lock . context . smbpid = smbpid ;
lock . context . pid = pid ;
lock . context . tid = tid ;
lock . start = start ;
lock . size = size ;
2000-01-14 07:32:57 +03:00
lock . fnum = fnum ;
2000-01-13 15:09:36 +03:00
lock . lock_type = lock_type ;
if ( dbuf . dptr ) {
/* there are existing locks - make sure they don't conflict */
locks = ( struct lock_struct * ) dbuf . dptr ;
count = dbuf . dsize / sizeof ( * locks ) ;
for ( i = 0 ; i < count ; i + + ) {
if ( brl_conflict ( & locks [ i ] , & lock ) ) {
goto fail ;
}
}
}
/* no conflicts - add it to the list of locks */
dbuf . dptr = Realloc ( dbuf . dptr , dbuf . dsize + sizeof ( * locks ) ) ;
if ( ! dbuf . dptr ) goto fail ;
memcpy ( dbuf . dptr + dbuf . dsize , & lock , sizeof ( lock ) ) ;
dbuf . dsize + = sizeof ( lock ) ;
tdb_store ( tdb , kbuf , dbuf , TDB_REPLACE ) ;
free ( dbuf . dptr ) ;
2000-12-06 03:05:15 +03:00
tdb_chainunlock ( tdb , kbuf ) ;
2000-01-13 15:09:36 +03:00
return True ;
fail :
if ( dbuf . dptr ) free ( dbuf . dptr ) ;
2000-12-06 03:05:15 +03:00
tdb_chainunlock ( tdb , kbuf ) ;
2000-01-13 15:09:36 +03:00
return False ;
}
/****************************************************************************
2000-04-26 00:30:58 +04:00
Unlock a range of bytes .
2000-01-13 15:09:36 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-04-26 00:30:58 +04:00
2000-01-14 07:32:57 +03:00
BOOL brl_unlock ( SMB_DEV_T dev , SMB_INO_T ino , int fnum ,
2000-01-13 15:09:36 +03:00
uint16 smbpid , pid_t pid , uint16 tid ,
br_off start , br_off size )
{
TDB_DATA kbuf , dbuf ;
int count , i ;
struct lock_struct * locks ;
struct lock_context context ;
2000-05-10 04:05:27 +04:00
kbuf = locking_key ( dev , ino ) ;
2000-01-13 15:09:36 +03:00
dbuf . dptr = NULL ;
2000-12-06 03:05:15 +03:00
tdb_chainlock ( tdb , kbuf ) ;
2000-01-13 15:09:36 +03:00
dbuf = tdb_fetch ( tdb , kbuf ) ;
2000-04-28 02:23:04 +04:00
if ( ! dbuf . dptr ) {
2000-04-29 00:54:23 +04:00
DEBUG ( 10 , ( " brl_unlock: tdb_fetch failed ! \n " ) ) ;
2000-04-28 02:23:04 +04:00
goto fail ;
}
2000-01-13 15:09:36 +03:00
context . smbpid = smbpid ;
context . pid = pid ;
context . tid = tid ;
/* there are existing locks - find a match */
locks = ( struct lock_struct * ) dbuf . dptr ;
count = dbuf . dsize / sizeof ( * locks ) ;
for ( i = 0 ; i < count ; i + + ) {
2000-04-26 00:30:58 +04:00
struct lock_struct * lock = & locks [ i ] ;
2000-04-28 02:23:04 +04:00
#if 0
/* JRATEST - DEBUGGING INFO */
if ( ! brl_same_context ( & lock - > context , & context ) ) {
DEBUG ( 10 , ( " brl_unlock: Not same context. l_smbpid = %u, l_pid = %u, l_tid = %u: \
smbpid = % u , pid = % u , tid = % u \ n " ,
lock - > context . smbpid , lock - > context . pid , lock - > context . tid ,
context . smbpid , context . pid , context . tid ) ) ;
}
/* JRATEST */
# endif
2000-04-26 00:30:58 +04:00
if ( brl_same_context ( & lock - > context , & context ) & &
lock - > fnum = = fnum & &
lock - > start = = start & &
lock - > size = = size ) {
2000-01-13 15:09:36 +03:00
/* found it - delete it */
if ( count = = 1 ) {
tdb_delete ( tdb , kbuf ) ;
} else {
if ( i < count - 1 ) {
memmove ( & locks [ i ] , & locks [ i + 1 ] ,
sizeof ( * locks ) * ( ( count - 1 ) - i ) ) ;
}
dbuf . dsize - = sizeof ( * locks ) ;
tdb_store ( tdb , kbuf , dbuf , TDB_REPLACE ) ;
}
free ( dbuf . dptr ) ;
2000-12-06 03:05:15 +03:00
tdb_chainunlock ( tdb , kbuf ) ;
2000-01-13 15:09:36 +03:00
return True ;
}
}
/* we didn't find it */
fail :
if ( dbuf . dptr ) free ( dbuf . dptr ) ;
2000-12-06 03:05:15 +03:00
tdb_chainunlock ( tdb , kbuf ) ;
2000-01-13 15:09:36 +03:00
return False ;
}
/****************************************************************************
2000-04-26 00:30:58 +04:00
Test if we could add a lock if we wanted to .
2000-01-13 15:09:36 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-04-26 00:30:58 +04:00
2000-05-06 00:50:22 +04:00
BOOL brl_locktest ( SMB_DEV_T dev , SMB_INO_T ino , int fnum ,
2000-01-13 15:09:36 +03:00
uint16 smbpid , pid_t pid , uint16 tid ,
br_off start , br_off size ,
2000-01-14 11:01:44 +03:00
enum brl_type lock_type )
2000-01-13 15:09:36 +03:00
{
TDB_DATA kbuf , dbuf ;
int count , i ;
struct lock_struct lock , * locks ;
2000-05-10 04:05:27 +04:00
kbuf = locking_key ( dev , ino ) ;
2000-01-13 15:09:36 +03:00
dbuf . dptr = NULL ;
2000-12-06 03:05:15 +03:00
tdb_chainlock ( tdb , kbuf ) ;
2000-01-13 15:09:36 +03:00
dbuf = tdb_fetch ( tdb , kbuf ) ;
lock . context . smbpid = smbpid ;
lock . context . pid = pid ;
lock . context . tid = tid ;
lock . start = start ;
lock . size = size ;
2000-05-06 00:50:22 +04:00
lock . fnum = fnum ;
2000-01-13 15:09:36 +03:00
lock . lock_type = lock_type ;
if ( dbuf . dptr ) {
/* there are existing locks - make sure they don't conflict */
locks = ( struct lock_struct * ) dbuf . dptr ;
count = dbuf . dsize / sizeof ( * locks ) ;
for ( i = 0 ; i < count ; i + + ) {
if ( brl_conflict ( & locks [ i ] , & lock ) ) {
goto fail ;
}
}
}
/* no conflicts - we could have added it */
free ( dbuf . dptr ) ;
2000-12-06 03:05:15 +03:00
tdb_chainunlock ( tdb , kbuf ) ;
2000-01-13 15:09:36 +03:00
return True ;
fail :
if ( dbuf . dptr ) free ( dbuf . dptr ) ;
2000-12-06 03:05:15 +03:00
tdb_chainunlock ( tdb , kbuf ) ;
2000-01-13 15:09:36 +03:00
return False ;
}
2000-01-14 07:32:57 +03:00
/****************************************************************************
2000-04-26 00:30:58 +04:00
Remove any locks associated with a open file .
2000-01-14 07:32:57 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-04-26 00:30:58 +04:00
2000-01-14 07:32:57 +03:00
void brl_close ( SMB_DEV_T dev , SMB_INO_T ino , pid_t pid , int tid , int fnum )
{
TDB_DATA kbuf , dbuf ;
2000-05-03 18:29:05 +04:00
int count , i , dcount = 0 ;
2000-01-14 07:32:57 +03:00
struct lock_struct * locks ;
2000-05-10 04:05:27 +04:00
kbuf = locking_key ( dev , ino ) ;
2000-01-14 07:32:57 +03:00
dbuf . dptr = NULL ;
2000-12-06 03:05:15 +03:00
tdb_chainlock ( tdb , kbuf ) ;
2000-01-14 07:32:57 +03:00
dbuf = tdb_fetch ( tdb , kbuf ) ;
if ( ! dbuf . dptr ) goto fail ;
/* there are existing locks - remove any for this fnum */
locks = ( struct lock_struct * ) dbuf . dptr ;
count = dbuf . dsize / sizeof ( * locks ) ;
for ( i = 0 ; i < count ; i + + ) {
2000-04-26 00:30:58 +04:00
struct lock_struct * lock = & locks [ i ] ;
if ( lock - > context . tid = = tid & &
lock - > context . pid = = pid & &
lock - > fnum = = fnum ) {
2000-01-14 07:32:57 +03:00
/* found it - delete it */
if ( count > 1 & & i < count - 1 ) {
memmove ( & locks [ i ] , & locks [ i + 1 ] ,
sizeof ( * locks ) * ( ( count - 1 ) - i ) ) ;
}
count - - ;
i - - ;
2000-05-03 18:29:05 +04:00
dcount + + ;
2000-01-14 07:32:57 +03:00
}
}
if ( count = = 0 ) {
tdb_delete ( tdb , kbuf ) ;
} else if ( count < ( dbuf . dsize / sizeof ( * locks ) ) ) {
2000-05-03 18:29:05 +04:00
dbuf . dsize - = dcount * sizeof ( * locks ) ;
2000-01-14 07:32:57 +03:00
tdb_store ( tdb , kbuf , dbuf , TDB_REPLACE ) ;
}
/* we didn't find it */
fail :
if ( dbuf . dptr ) free ( dbuf . dptr ) ;
2000-12-06 03:05:15 +03:00
tdb_chainunlock ( tdb , kbuf ) ;
2000-01-14 07:32:57 +03:00
}
2000-01-16 14:14:44 +03:00
/****************************************************************************
2000-04-26 00:30:58 +04:00
Traverse the whole database with this function , calling traverse_callback
on each lock .
2000-01-16 14:14:44 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-04-26 00:30:58 +04:00
1) added void* state argument to tdb_traverse. guess what! there were
two places i found where it was appropriate to _use_ that third argument,
in locking.c and brlock.c! there was a static traverse_function and
i removed the static variable, typecast it to a void*, passed it to
tdb_traverse and re-cast it back to the traverse_function inside the
tdb_traverse function. this makes the use of tdb_traverse() reentrant,
which is never going to happen, i know, i just don't like to see
statics lying about when there's no need for them.
as i had to do in samba-tng, all uses of tdb_traverse modified to take
the new void* state argument.
2) disabled rpcclient: referring people to use SAMBA_TNG rpcclient.
i don't know how the other samba team members would react if i deleted
rpcclient from cvs main. damn, that code's so old, it's unreal.
20 rpcclient commands, instead of about 70 in SAMBA_TNG.
(This used to be commit 49d7f0afbc1c5425d53019e234d54ddf205c8e9a)
2000-02-04 07:59:31 +03:00
static int traverse_fn ( TDB_CONTEXT * ttdb , TDB_DATA kbuf , TDB_DATA dbuf , void * state )
2000-01-16 14:14:44 +03:00
{
struct lock_struct * locks ;
struct lock_key * key ;
int i ;
1) added void* state argument to tdb_traverse. guess what! there were
two places i found where it was appropriate to _use_ that third argument,
in locking.c and brlock.c! there was a static traverse_function and
i removed the static variable, typecast it to a void*, passed it to
tdb_traverse and re-cast it back to the traverse_function inside the
tdb_traverse function. this makes the use of tdb_traverse() reentrant,
which is never going to happen, i know, i just don't like to see
statics lying about when there's no need for them.
as i had to do in samba-tng, all uses of tdb_traverse modified to take
the new void* state argument.
2) disabled rpcclient: referring people to use SAMBA_TNG rpcclient.
i don't know how the other samba team members would react if i deleted
rpcclient from cvs main. damn, that code's so old, it's unreal.
20 rpcclient commands, instead of about 70 in SAMBA_TNG.
(This used to be commit 49d7f0afbc1c5425d53019e234d54ddf205c8e9a)
2000-02-04 07:59:31 +03:00
BRLOCK_FN ( traverse_callback ) = ( BRLOCK_FN_CAST ( ) ) state ;
2000-01-16 14:14:44 +03:00
locks = ( struct lock_struct * ) dbuf . dptr ;
key = ( struct lock_key * ) kbuf . dptr ;
for ( i = 0 ; i < dbuf . dsize / sizeof ( * locks ) ; i + + ) {
traverse_callback ( key - > device , key - > inode ,
locks [ i ] . context . pid ,
locks [ i ] . lock_type ,
locks [ i ] . start ,
locks [ i ] . size ) ;
}
return 0 ;
}
/*******************************************************************
2000-04-26 00:30:58 +04:00
Call the specified function on each lock in the database .
2000-01-16 14:14:44 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-04-26 00:30:58 +04:00
1) added void* state argument to tdb_traverse. guess what! there were
two places i found where it was appropriate to _use_ that third argument,
in locking.c and brlock.c! there was a static traverse_function and
i removed the static variable, typecast it to a void*, passed it to
tdb_traverse and re-cast it back to the traverse_function inside the
tdb_traverse function. this makes the use of tdb_traverse() reentrant,
which is never going to happen, i know, i just don't like to see
statics lying about when there's no need for them.
as i had to do in samba-tng, all uses of tdb_traverse modified to take
the new void* state argument.
2) disabled rpcclient: referring people to use SAMBA_TNG rpcclient.
i don't know how the other samba team members would react if i deleted
rpcclient from cvs main. damn, that code's so old, it's unreal.
20 rpcclient commands, instead of about 70 in SAMBA_TNG.
(This used to be commit 49d7f0afbc1c5425d53019e234d54ddf205c8e9a)
2000-02-04 07:59:31 +03:00
int brl_forall ( BRLOCK_FN ( fn ) )
2000-01-16 14:14:44 +03:00
{
if ( ! tdb ) return 0 ;
2000-11-18 02:10:56 +03:00
return tdb_traverse ( tdb , traverse_fn , ( void * ) fn ) ;
2000-01-16 14:14:44 +03:00
}