2004-10-17 06:55:47 +04:00
/*
Unix SMB / CIFS implementation .
2007-01-19 06:58:16 +03:00
generic byte range locking code - tdb backend
2004-10-17 06:55:47 +04:00
2007-01-19 06:58:16 +03:00
Copyright ( C ) Andrew Tridgell 1992 - 2006
2004-10-17 06:55:47 +04:00
Copyright ( C ) Jeremy Allison 1992 - 2000
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
2007-07-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2004-10-17 06:55:47 +04:00
( 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
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2004-10-17 06:55:47 +04:00
*/
/* This module implements a tdb based byte range locking service,
replacing the fcntl ( ) based byte range locking previously
used . This allows us to provide the same semantics as NT */
# include "includes.h"
2005-02-10 08:09:35 +03:00
# include "system/filesys.h"
2006-01-03 18:40:05 +03:00
# include "messaging/messaging.h"
2005-06-05 10:53:07 +04:00
# include "lib/messaging/irpc.h"
2006-02-23 18:52:24 +03:00
# include "libcli/libcli.h"
2007-01-10 16:25:39 +03:00
# include "cluster/cluster.h"
2007-01-19 06:58:16 +03:00
# include "ntvfs/common/brlock.h"
2007-05-14 21:51:19 +04:00
# include "ntvfs/ntvfs.h"
2007-12-02 19:56:09 +03:00
# include "param/param.h"
2013-04-11 11:42:15 +04:00
# include "dbwrap/dbwrap.h"
2004-10-17 06:55:47 +04:00
/*
in this module a " DATA_BLOB *file_key " is a blob that uniquely identifies
a file . For a local posix filesystem this will usually be a combination
of the device and inode numbers of the file , but it can be anything
that uniquely idetifies a file for locking purposes , as long
as it is applied consistently .
*/
2007-01-19 06:58:16 +03:00
/* this struct is typicaly attached to tcon */
struct brl_context {
2013-04-11 11:42:15 +04:00
struct db_context * db ;
2007-01-19 06:58:16 +03:00
struct server_id server ;
2011-05-03 04:40:33 +04:00
struct imessaging_context * imessaging_ctx ;
2007-01-19 06:58:16 +03:00
} ;
2004-10-17 06:55:47 +04:00
/*
the lock context contains the elements that define whether one
lock is the same as another lock
*/
struct lock_context {
2007-01-10 13:52:09 +03:00
struct server_id server ;
2008-06-02 05:03:19 +04:00
uint32_t smbpid ;
2006-05-15 15:43:25 +04:00
struct brl_context * ctx ;
2004-10-17 06:55:47 +04:00
} ;
/* The data in brlock records is an unsorted linear array of these
records . It is unnecessary to store the count as tdb provides the
size of the record */
struct lock_struct {
struct lock_context context ;
2006-05-20 12:15:22 +04:00
struct ntvfs_handle * ntvfs ;
2004-10-17 06:55:47 +04:00
uint64_t start ;
uint64_t size ;
enum brl_type lock_type ;
2004-10-18 11:40:17 +04:00
void * notify_ptr ;
} ;
2006-05-15 16:22:00 +04:00
/* this struct is attached to on oprn file handle */
struct brl_handle {
DATA_BLOB key ;
2006-05-20 12:15:22 +04:00
struct ntvfs_handle * ntvfs ;
2006-05-15 16:22:00 +04:00
struct lock_struct last_lock ;
} ;
2009-11-27 10:08:51 +03:00
/* see if we have wrapped locks, which are no longer allowed (windows
* changed this in win7 */
static bool brl_invalid_lock_range ( uint64_t start , uint64_t size )
{
return ( size > 1 & & ( start + size < start ) ) ;
}
2004-10-17 06:55:47 +04:00
/*
Open up the brlock . tdb database . Close it down using
2011-05-03 04:40:33 +04:00
talloc_free ( ) . We need the imessaging_ctx to allow for
2004-10-18 11:40:17 +04:00
pending lock notifications .
2004-10-17 06:55:47 +04:00
*/
2007-01-19 06:58:16 +03:00
static struct brl_context * brl_tdb_init ( TALLOC_CTX * mem_ctx , struct server_id server ,
2007-12-10 06:33:16 +03:00
struct loadparm_context * lp_ctx ,
2011-05-03 04:40:33 +04:00
struct imessaging_context * imessaging_ctx )
2004-10-17 06:55:47 +04:00
{
struct brl_context * brl ;
2005-01-27 10:08:20 +03:00
brl = talloc ( mem_ctx , struct brl_context ) ;
2004-10-17 06:55:47 +04:00
if ( brl = = NULL ) {
return NULL ;
}
2013-04-11 11:42:15 +04:00
brl - > db = cluster_db_tmp_open ( brl , lp_ctx , " brlock " , TDB_DEFAULT ) ;
if ( brl - > db = = NULL ) {
2004-10-17 06:55:47 +04:00
talloc_free ( brl ) ;
return NULL ;
}
brl - > server = server ;
2011-05-03 04:40:33 +04:00
brl - > imessaging_ctx = imessaging_ctx ;
2004-10-17 06:55:47 +04:00
2004-10-22 05:14:49 +04:00
return brl ;
2004-10-17 06:55:47 +04:00
}
2007-01-19 06:58:16 +03:00
static struct brl_handle * brl_tdb_create_handle ( TALLOC_CTX * mem_ctx , struct ntvfs_handle * ntvfs ,
DATA_BLOB * file_key )
2006-05-15 16:22:00 +04:00
{
struct brl_handle * brlh ;
brlh = talloc ( mem_ctx , struct brl_handle ) ;
if ( brlh = = NULL ) {
return NULL ;
}
brlh - > key = * file_key ;
2006-05-20 12:15:22 +04:00
brlh - > ntvfs = ntvfs ;
2006-05-15 16:22:00 +04:00
ZERO_STRUCT ( brlh - > last_lock ) ;
return brlh ;
}
2004-10-17 06:55:47 +04:00
/*
see if two locking contexts are equal
*/
2007-10-07 02:28:14 +04:00
static bool brl_tdb_same_context ( struct lock_context * ctx1 , struct lock_context * ctx2 )
2004-10-17 06:55:47 +04:00
{
2007-01-10 13:52:09 +03:00
return ( cluster_id_equal ( & ctx1 - > server , & ctx2 - > server ) & &
2004-10-17 06:55:47 +04:00
ctx1 - > smbpid = = ctx2 - > smbpid & &
2006-05-15 15:43:25 +04:00
ctx1 - > ctx = = ctx2 - > ctx ) ;
2004-10-17 06:55:47 +04:00
}
2004-10-18 11:40:17 +04:00
/*
see if lck1 and lck2 overlap
2007-08-24 06:37:38 +04:00
lck1 is the existing lock . lck2 is the new lock we are
looking at adding
2004-10-18 11:40:17 +04:00
*/
2007-10-07 02:28:14 +04:00
static bool brl_tdb_overlap ( struct lock_struct * lck1 ,
2007-08-24 06:37:38 +04:00
struct lock_struct * lck2 )
2004-10-18 11:40:17 +04:00
{
2013-02-18 13:04:42 +04:00
/* this extra check is not redundant - it copes with locks
2004-10-18 13:16:55 +04:00
that go beyond the end of 64 bit file space */
if ( lck1 - > size ! = 0 & &
lck1 - > start = = lck2 - > start & &
lck1 - > size = = lck2 - > size ) {
2007-10-07 02:28:14 +04:00
return true ;
2004-10-18 13:16:55 +04:00
}
if ( lck1 - > start > = ( lck2 - > start + lck2 - > size ) | |
lck2 - > start > = ( lck1 - > start + lck1 - > size ) ) {
2007-10-07 02:28:14 +04:00
return false ;
2004-10-18 11:40:17 +04:00
}
2007-08-24 06:37:38 +04:00
/* we have a conflict. Now check to see if lck1 really still
* exists , which involves checking if the process still
* exists . We leave this test to last as its the most
* expensive test , especially when we are clustered */
/* TODO: need to do this via a server_id_exists() call, which
* hasn ' t been written yet . When clustered this will need to
* call into ctdb */
2007-10-07 02:28:14 +04:00
return true ;
2004-10-18 11:40:17 +04:00
}
2004-10-17 06:55:47 +04:00
/*
See if lock2 can be added when lock1 is in place .
*/
2007-10-07 02:28:14 +04:00
static bool brl_tdb_conflict ( struct lock_struct * lck1 ,
2004-10-17 06:55:47 +04:00
struct lock_struct * lck2 )
{
2004-10-18 11:40:17 +04:00
/* pending locks don't conflict with anything */
if ( lck1 - > lock_type > = PENDING_READ_LOCK | |
lck2 - > lock_type > = PENDING_READ_LOCK ) {
2007-10-07 02:28:14 +04:00
return false ;
2004-10-18 11:40:17 +04:00
}
2004-10-17 06:55:47 +04:00
if ( lck1 - > lock_type = = READ_LOCK & & lck2 - > lock_type = = READ_LOCK ) {
2007-10-07 02:28:14 +04:00
return false ;
2004-10-17 06:55:47 +04:00
}
2007-01-19 06:58:16 +03:00
if ( brl_tdb_same_context ( & lck1 - > context , & lck2 - > context ) & &
2006-05-20 12:15:22 +04:00
lck2 - > lock_type = = READ_LOCK & & lck1 - > ntvfs = = lck2 - > ntvfs ) {
2007-10-07 02:28:14 +04:00
return false ;
2004-10-17 06:55:47 +04:00
}
2007-01-19 06:58:16 +03:00
return brl_tdb_overlap ( lck1 , lck2 ) ;
2004-10-17 06:55:47 +04:00
}
/*
Check to see if this lock conflicts , but ignore our own locks on the
same fnum only .
*/
2007-10-07 02:28:14 +04:00
static bool brl_tdb_conflict_other ( struct lock_struct * lck1 , struct lock_struct * lck2 )
2004-10-17 06:55:47 +04:00
{
2004-10-18 11:40:17 +04:00
/* pending locks don't conflict with anything */
if ( lck1 - > lock_type > = PENDING_READ_LOCK | |
lck2 - > lock_type > = PENDING_READ_LOCK ) {
2007-10-07 02:28:14 +04:00
return false ;
2004-10-18 11:40:17 +04:00
}
2004-10-17 06:55:47 +04:00
if ( lck1 - > lock_type = = READ_LOCK & & lck2 - > lock_type = = READ_LOCK )
2007-10-07 02:28:14 +04:00
return false ;
2004-10-17 06:55:47 +04:00
2004-10-18 11:40:17 +04:00
/*
* note that incoming write calls conflict with existing READ
* locks even if the context is the same . JRA . See LOCKTEST7
* in smbtorture .
*/
2007-01-19 06:58:16 +03:00
if ( brl_tdb_same_context ( & lck1 - > context , & lck2 - > context ) & &
2006-05-20 12:15:22 +04:00
lck1 - > ntvfs = = lck2 - > ntvfs & &
2004-10-18 11:40:17 +04:00
( lck2 - > lock_type = = READ_LOCK | | lck1 - > lock_type = = WRITE_LOCK ) ) {
2007-10-07 02:28:14 +04:00
return false ;
2004-10-17 06:55:47 +04:00
}
2007-01-19 06:58:16 +03:00
return brl_tdb_overlap ( lck1 , lck2 ) ;
2004-10-17 06:55:47 +04:00
}
2004-10-18 11:40:17 +04:00
/*
amazingly enough , w2k3 " remembers " whether the last lock failure
is the same as this one and changes its error code . I wonder if any
app depends on this ?
*/
2007-01-19 06:58:16 +03:00
static NTSTATUS brl_tdb_lock_failed ( struct brl_handle * brlh , struct lock_struct * lock )
2004-10-18 11:40:17 +04:00
{
2006-05-15 16:22:00 +04:00
/*
* this function is only called for non pending lock !
*/
2007-05-14 21:51:19 +04:00
/* in SMB2 mode always return NT_STATUS_LOCK_NOT_GRANTED! */
2011-09-05 15:10:42 +04:00
if ( lock - > ntvfs - > ctx - > protocol > = PROTOCOL_SMB2_02 ) {
2007-05-14 21:51:19 +04:00
return NT_STATUS_LOCK_NOT_GRANTED ;
}
2006-05-15 16:22:00 +04:00
/*
* if the notify_ptr is non NULL ,
* it means that we ' re at the end of a pending lock
* and the real lock is requested after the timout went by
* In this case we need to remember the last_lock and always
* give FILE_LOCK_CONFLICT
*/
if ( lock - > notify_ptr ) {
brlh - > last_lock = * lock ;
2004-10-18 11:40:17 +04:00
return NT_STATUS_FILE_LOCK_CONFLICT ;
}
2006-05-15 16:22:00 +04:00
/*
* amazing the little things you learn with a test
* suite . Locks beyond this offset ( as a 64 bit
* number ! ) always generate the conflict error code ,
* unless the top bit is set
*/
if ( lock - > start > = 0xEF000000 & & ( lock - > start > > 63 ) = = 0 ) {
brlh - > last_lock = * lock ;
2004-10-18 11:40:17 +04:00
return NT_STATUS_FILE_LOCK_CONFLICT ;
}
2006-05-15 16:22:00 +04:00
/*
* if the current lock matches the last failed lock on the file handle
* and starts at the same offset , then FILE_LOCK_CONFLICT should be returned
*/
2007-01-10 13:52:09 +03:00
if ( cluster_id_equal ( & lock - > context . server , & brlh - > last_lock . context . server ) & &
2006-05-15 16:22:00 +04:00
lock - > context . ctx = = brlh - > last_lock . context . ctx & &
2006-05-20 12:15:22 +04:00
lock - > ntvfs = = brlh - > last_lock . ntvfs & &
2006-05-15 16:22:00 +04:00
lock - > start = = brlh - > last_lock . start ) {
return NT_STATUS_FILE_LOCK_CONFLICT ;
}
brlh - > last_lock = * lock ;
2004-10-18 11:40:17 +04:00
return NT_STATUS_LOCK_NOT_GRANTED ;
}
2004-10-17 06:55:47 +04:00
/*
2004-10-18 11:40:17 +04:00
Lock a range of bytes . The lock_type can be a PENDING_ * _LOCK , in
which case a real lock is first tried , and if that fails then a
pending lock is created . When the pending lock is triggered ( by
someone else closing an overlapping lock range ) a messaging
notification is sent , identified by the notify_ptr
2004-10-17 06:55:47 +04:00
*/
2007-01-19 06:58:16 +03:00
static NTSTATUS brl_tdb_lock ( struct brl_context * brl ,
struct brl_handle * brlh ,
2008-06-02 05:03:19 +04:00
uint32_t smbpid ,
2007-01-19 06:58:16 +03:00
uint64_t start , uint64_t size ,
enum brl_type lock_type ,
void * notify_ptr )
2004-10-17 06:55:47 +04:00
{
TDB_DATA kbuf , dbuf ;
2004-12-03 09:42:06 +03:00
int count = 0 , i ;
struct lock_struct lock , * locks = NULL ;
2004-10-17 06:55:47 +04:00
NTSTATUS status ;
2013-04-11 11:42:15 +04:00
struct db_record * locked ;
2008-12-29 13:52:53 +03:00
kbuf . dptr = brlh - > key . data ;
kbuf . dsize = brlh - > key . length ;
2009-11-27 10:08:51 +03:00
if ( brl_invalid_lock_range ( start , size ) ) {
return NT_STATUS_INVALID_LOCK_RANGE ;
}
2013-04-11 11:42:15 +04:00
locked = dbwrap_fetch_locked ( brl - > db , brl , kbuf ) ;
if ( ! locked ) {
2008-12-29 13:52:53 +03:00
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
2004-10-17 06:55:47 +04:00
2004-10-18 11:40:17 +04:00
/* if this is a pending lock, then with the chainlock held we
try to get the real lock . If we succeed then we don ' t need
to make it pending . This prevents a possible race condition
where the pending lock gets created after the lock that is
preventing the real lock gets removed */
if ( lock_type > = PENDING_READ_LOCK ) {
enum brl_type rw = ( lock_type = = PENDING_READ_LOCK ? READ_LOCK : WRITE_LOCK ) ;
2006-05-15 16:22:00 +04:00
/* here we need to force that the last_lock isn't overwritten */
lock = brlh - > last_lock ;
2007-01-19 06:58:16 +03:00
status = brl_tdb_lock ( brl , brlh , smbpid , start , size , rw , NULL ) ;
2006-05-15 16:22:00 +04:00
brlh - > last_lock = lock ;
2004-10-18 11:40:17 +04:00
if ( NT_STATUS_IS_OK ( status ) ) {
2013-04-11 11:42:15 +04:00
talloc_free ( locked ) ;
2004-10-18 11:40:17 +04:00
return NT_STATUS_OK ;
}
}
2013-04-11 11:42:15 +04:00
dbuf = dbwrap_record_get_value ( locked ) ;
2004-10-17 06:55:47 +04:00
lock . context . smbpid = smbpid ;
lock . context . server = brl - > server ;
2006-05-15 15:43:25 +04:00
lock . context . ctx = brl ;
2006-05-20 12:15:22 +04:00
lock . ntvfs = brlh - > ntvfs ;
2006-05-15 16:22:00 +04:00
lock . context . ctx = brl ;
2004-10-17 06:55:47 +04:00
lock . start = start ;
lock . size = size ;
lock . lock_type = lock_type ;
2004-10-18 11:40:17 +04:00
lock . notify_ptr = notify_ptr ;
2004-10-17 06:55:47 +04:00
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 + + ) {
2007-01-19 06:58:16 +03:00
if ( brl_tdb_conflict ( & locks [ i ] , & lock ) ) {
status = brl_tdb_lock_failed ( brlh , & lock ) ;
2004-10-17 06:55:47 +04:00
goto fail ;
}
}
}
/* no conflicts - add it to the list of locks */
2013-04-11 11:42:15 +04:00
/* FIXME: a dbwrap_record_append() would help here! */
locks = talloc_array ( locked , struct lock_struct , count + 1 ) ;
2004-12-03 09:42:06 +03:00
if ( ! locks ) {
2004-10-17 06:55:47 +04:00
status = NT_STATUS_NO_MEMORY ;
goto fail ;
}
2019-05-24 02:00:05 +03:00
if ( dbuf . dsize > 0 ) {
memcpy ( locks , dbuf . dptr , dbuf . dsize ) ;
}
2004-12-03 09:42:06 +03:00
locks [ count ] = lock ;
2013-04-11 11:42:15 +04:00
dbuf . dptr = ( unsigned char * ) locks ;
2004-10-17 06:55:47 +04:00
dbuf . dsize + = sizeof ( lock ) ;
2013-04-11 11:42:15 +04:00
status = dbwrap_record_store ( locked , dbuf , TDB_REPLACE ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2004-10-17 06:55:47 +04:00
goto fail ;
}
2013-04-11 11:42:15 +04:00
talloc_free ( locked ) ;
2004-10-18 11:40:17 +04:00
/* the caller needs to know if the real lock was granted. If
we have reached here then it must be a pending lock that
was granted , so tell them the lock failed */
if ( lock_type > = PENDING_READ_LOCK ) {
2006-05-15 16:22:00 +04:00
return NT_STATUS_LOCK_NOT_GRANTED ;
2004-10-18 11:40:17 +04:00
}
2004-10-17 06:55:47 +04:00
return NT_STATUS_OK ;
fail :
2013-04-11 11:42:15 +04:00
talloc_free ( locked ) ;
2004-10-17 06:55:47 +04:00
return status ;
}
2004-10-18 11:40:17 +04:00
/*
we are removing a lock that might be holding up a pending lock . Scan for pending
locks that cover this range and if we find any then notify the server that it should
retry the lock
*/
2007-01-19 06:58:16 +03:00
static void brl_tdb_notify_unlock ( struct brl_context * brl ,
2004-10-18 11:40:17 +04:00
struct lock_struct * locks , int count ,
struct lock_struct * removed_lock )
{
int i , last_notice ;
/* the last_notice logic is to prevent stampeding on a lock
range . It prevents us sending hundreds of notifies on the
same range of bytes . It doesn ' t prevent all possible
stampedes , but it does prevent the most common problem */
last_notice = - 1 ;
for ( i = 0 ; i < count ; i + + ) {
if ( locks [ i ] . lock_type > = PENDING_READ_LOCK & &
2007-01-19 06:58:16 +03:00
brl_tdb_overlap ( & locks [ i ] , removed_lock ) ) {
if ( last_notice ! = - 1 & & brl_tdb_overlap ( & locks [ i ] , & locks [ last_notice ] ) ) {
2004-10-18 11:40:17 +04:00
continue ;
}
2004-10-28 10:45:28 +04:00
if ( locks [ i ] . lock_type = = PENDING_WRITE_LOCK ) {
last_notice = i ;
}
2011-05-03 04:40:33 +04:00
imessaging_send_ptr ( brl - > imessaging_ctx , locks [ i ] . context . server ,
2004-11-03 13:09:48 +03:00
MSG_BRL_RETRY , locks [ i ] . notify_ptr ) ;
2004-10-18 11:40:17 +04:00
}
}
}
/*
send notifications for all pending locks - the file is being closed by this
user
*/
2007-01-19 06:58:16 +03:00
static void brl_tdb_notify_all ( struct brl_context * brl ,
2004-10-18 11:40:17 +04:00
struct lock_struct * locks , int count )
{
int i ;
for ( i = 0 ; i < count ; i + + ) {
if ( locks - > lock_type > = PENDING_READ_LOCK ) {
2007-01-19 06:58:16 +03:00
brl_tdb_notify_unlock ( brl , locks , count , & locks [ i ] ) ;
2004-10-18 11:40:17 +04:00
}
}
}
2004-10-17 06:55:47 +04:00
/*
Unlock a range of bytes .
*/
2007-01-19 06:58:16 +03:00
static NTSTATUS brl_tdb_unlock ( struct brl_context * brl ,
struct brl_handle * brlh ,
2008-06-02 05:03:19 +04:00
uint32_t smbpid ,
2007-01-19 06:58:16 +03:00
uint64_t start , uint64_t size )
2004-10-17 06:55:47 +04:00
{
TDB_DATA kbuf , dbuf ;
int count , i ;
2007-01-22 14:46:27 +03:00
struct lock_struct * locks , * lock ;
2004-10-17 06:55:47 +04:00
struct lock_context context ;
2013-04-11 11:42:15 +04:00
struct db_record * locked ;
2004-10-17 06:55:47 +04:00
NTSTATUS status ;
2006-05-15 16:22:00 +04:00
kbuf . dptr = brlh - > key . data ;
kbuf . dsize = brlh - > key . length ;
2004-10-17 06:55:47 +04:00
2009-11-27 10:08:51 +03:00
if ( brl_invalid_lock_range ( start , size ) ) {
return NT_STATUS_INVALID_LOCK_RANGE ;
}
2013-04-11 11:42:15 +04:00
locked = dbwrap_fetch_locked ( brl - > db , brl , kbuf ) ;
if ( ! locked ) {
2004-10-17 06:55:47 +04:00
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
2013-04-11 11:42:15 +04:00
dbuf = dbwrap_record_get_value ( locked ) ;
2004-10-17 06:55:47 +04:00
context . smbpid = smbpid ;
context . server = brl - > server ;
2006-05-15 15:43:25 +04:00
context . ctx = brl ;
2004-10-17 06:55:47 +04:00
/* there are existing locks - find a match */
locks = ( struct lock_struct * ) dbuf . dptr ;
count = dbuf . dsize / sizeof ( * locks ) ;
for ( i = 0 ; i < count ; i + + ) {
2007-01-22 14:46:27 +03:00
lock = & locks [ i ] ;
if ( brl_tdb_same_context ( & lock - > context , & context ) & &
lock - > ntvfs = = brlh - > ntvfs & &
lock - > start = = start & &
lock - > size = = size & &
lock - > lock_type = = WRITE_LOCK ) {
break ;
}
}
if ( i < count ) goto found ;
for ( i = 0 ; i < count ; i + + ) {
lock = & locks [ i ] ;
2007-01-19 06:58:16 +03:00
if ( brl_tdb_same_context ( & lock - > context , & context ) & &
2006-05-20 12:15:22 +04:00
lock - > ntvfs = = brlh - > ntvfs & &
2004-10-17 06:55:47 +04:00
lock - > start = = start & &
2004-10-18 11:40:17 +04:00
lock - > size = = size & &
2006-05-15 14:45:38 +04:00
lock - > lock_type < PENDING_READ_LOCK ) {
2007-01-22 14:46:27 +03:00
break ;
}
}
found :
2008-12-29 13:52:53 +03:00
if ( i < count ) {
/* found it - delete it */
if ( count = = 1 ) {
2013-04-11 11:42:15 +04:00
status = dbwrap_record_delete ( locked ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2008-12-29 13:52:53 +03:00
goto fail ;
}
} else {
struct lock_struct removed_lock = * lock ;
if ( i < count - 1 ) {
memmove ( & locks [ i ] , & locks [ i + 1 ] ,
sizeof ( * locks ) * ( ( count - 1 ) - i ) ) ;
}
count - - ;
/* send notifications for any relevant pending locks */
brl_tdb_notify_unlock ( brl , locks , count , & removed_lock ) ;
dbuf . dsize = count * sizeof ( * locks ) ;
2013-04-11 11:42:15 +04:00
status = dbwrap_record_store ( locked , dbuf , TDB_REPLACE ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2008-12-29 13:52:53 +03:00
goto fail ;
}
2004-10-18 11:40:17 +04:00
}
2013-04-11 11:42:15 +04:00
talloc_free ( locked ) ;
2008-12-29 13:52:53 +03:00
return NT_STATUS_OK ;
2004-10-18 11:40:17 +04:00
}
2008-12-29 13:52:53 +03:00
/* we didn't find it */
status = NT_STATUS_RANGE_NOT_LOCKED ;
2004-10-18 11:40:17 +04:00
2008-12-29 13:52:53 +03:00
fail :
2013-04-11 11:42:15 +04:00
talloc_free ( locked ) ;
2004-10-18 11:40:17 +04:00
return status ;
}
/*
remove a pending lock . This is called when the caller has either
given up trying to establish a lock or when they have succeeded in
getting it . In either case they no longer need to be notified .
*/
2007-01-19 06:58:16 +03:00
static NTSTATUS brl_tdb_remove_pending ( struct brl_context * brl ,
struct brl_handle * brlh ,
void * notify_ptr )
2004-10-18 11:40:17 +04:00
{
TDB_DATA kbuf , dbuf ;
int count , i ;
struct lock_struct * locks ;
NTSTATUS status ;
2013-04-11 11:42:15 +04:00
struct db_record * locked ;
2004-10-18 11:40:17 +04:00
2006-05-15 16:22:00 +04:00
kbuf . dptr = brlh - > key . data ;
kbuf . dsize = brlh - > key . length ;
2004-10-18 11:40:17 +04:00
2013-04-11 11:42:15 +04:00
locked = dbwrap_fetch_locked ( brl - > db , brl , kbuf ) ;
if ( ! locked ) {
2004-10-18 11:40:17 +04:00
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
2013-04-11 11:42:15 +04:00
dbuf = dbwrap_record_get_value ( locked ) ;
2008-12-29 13:52:53 +03:00
if ( ! dbuf . dptr ) {
2013-04-11 11:42:15 +04:00
talloc_free ( locked ) ;
2008-12-29 13:52:53 +03:00
return NT_STATUS_RANGE_NOT_LOCKED ;
}
2004-10-18 11:40:17 +04:00
/* there are existing locks - find a match */
locks = ( struct lock_struct * ) dbuf . dptr ;
count = dbuf . dsize / sizeof ( * locks ) ;
for ( i = 0 ; i < count ; i + + ) {
struct lock_struct * lock = & locks [ i ] ;
2006-05-15 14:45:38 +04:00
if ( lock - > lock_type > = PENDING_READ_LOCK & &
lock - > notify_ptr = = notify_ptr & &
2007-01-10 13:52:09 +03:00
cluster_id_equal ( & lock - > context . server , & brl - > server ) ) {
2004-10-17 06:55:47 +04:00
/* found it - delete it */
if ( count = = 1 ) {
2013-04-11 11:42:15 +04:00
status = dbwrap_record_delete ( locked ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2008-12-29 13:52:53 +03:00
goto fail ;
}
2004-10-17 06:55:47 +04:00
} else {
if ( i < count - 1 ) {
memmove ( & locks [ i ] , & locks [ i + 1 ] ,
sizeof ( * locks ) * ( ( count - 1 ) - i ) ) ;
}
2004-10-18 11:40:17 +04:00
count - - ;
dbuf . dsize = count * sizeof ( * locks ) ;
2013-04-11 11:42:15 +04:00
status = dbwrap_record_store ( locked , dbuf ,
TDB_REPLACE ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
2008-12-29 13:52:53 +03:00
goto fail ;
}
}
2013-04-11 11:42:15 +04:00
talloc_free ( locked ) ;
2008-12-29 13:52:53 +03:00
return NT_STATUS_OK ;
2004-10-17 06:55:47 +04:00
}
}
2008-12-29 13:52:53 +03:00
/* we didn't find it */
status = NT_STATUS_RANGE_NOT_LOCKED ;
2004-10-17 06:55:47 +04:00
2008-12-29 13:52:53 +03:00
fail :
2013-04-11 11:42:15 +04:00
talloc_free ( locked ) ;
2004-10-17 06:55:47 +04:00
return status ;
}
/*
Test if we are allowed to perform IO on a region of an open file
*/
2007-01-19 06:58:16 +03:00
static NTSTATUS brl_tdb_locktest ( struct brl_context * brl ,
struct brl_handle * brlh ,
2008-06-02 05:03:19 +04:00
uint32_t smbpid ,
2007-01-19 06:58:16 +03:00
uint64_t start , uint64_t size ,
enum brl_type lock_type )
2004-10-17 06:55:47 +04:00
{
TDB_DATA kbuf , dbuf ;
int count , i ;
struct lock_struct lock , * locks ;
2013-04-11 11:42:15 +04:00
NTSTATUS status ;
2004-10-17 06:55:47 +04:00
2006-05-15 16:22:00 +04:00
kbuf . dptr = brlh - > key . data ;
kbuf . dsize = brlh - > key . length ;
2004-10-17 06:55:47 +04:00
2009-11-27 10:08:51 +03:00
if ( brl_invalid_lock_range ( start , size ) ) {
return NT_STATUS_INVALID_LOCK_RANGE ;
}
2013-04-11 11:42:15 +04:00
status = dbwrap_fetch ( brl - > db , brl , kbuf , & dbuf ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NOT_FOUND ) ) {
2004-10-17 06:55:47 +04:00
return NT_STATUS_OK ;
2013-04-11 11:42:15 +04:00
} else if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2004-10-17 06:55:47 +04:00
}
lock . context . smbpid = smbpid ;
lock . context . server = brl - > server ;
2006-05-15 15:43:25 +04:00
lock . context . ctx = brl ;
2006-05-20 12:15:22 +04:00
lock . ntvfs = brlh - > ntvfs ;
2004-10-17 06:55:47 +04:00
lock . start = start ;
lock . size = size ;
lock . lock_type = lock_type ;
/* 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 + + ) {
2007-01-19 06:58:16 +03:00
if ( brl_tdb_conflict_other ( & locks [ i ] , & lock ) ) {
2013-04-11 11:42:15 +04:00
talloc_free ( dbuf . dptr ) ;
2008-12-29 13:52:53 +03:00
return NT_STATUS_FILE_LOCK_CONFLICT ;
2004-10-17 06:55:47 +04:00
}
}
2013-04-11 11:42:15 +04:00
talloc_free ( dbuf . dptr ) ;
2008-12-29 13:52:53 +03:00
return NT_STATUS_OK ;
2004-10-17 06:55:47 +04:00
}
/*
Remove any locks associated with a open file .
*/
2007-01-19 06:58:16 +03:00
static NTSTATUS brl_tdb_close ( struct brl_context * brl ,
struct brl_handle * brlh )
2004-10-17 06:55:47 +04:00
{
TDB_DATA kbuf , dbuf ;
int count , i , dcount = 0 ;
struct lock_struct * locks ;
2013-04-11 11:42:15 +04:00
struct db_record * locked ;
2004-10-17 06:55:47 +04:00
NTSTATUS status ;
2006-05-15 16:22:00 +04:00
kbuf . dptr = brlh - > key . data ;
kbuf . dsize = brlh - > key . length ;
2004-10-17 06:55:47 +04:00
2013-04-11 11:42:15 +04:00
locked = dbwrap_fetch_locked ( brl - > db , brl , kbuf ) ;
if ( ! locked ) {
2004-10-17 06:55:47 +04:00
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
2013-04-11 11:42:15 +04:00
dbuf = dbwrap_record_get_value ( locked ) ;
2004-10-17 06:55:47 +04:00
if ( ! dbuf . dptr ) {
2013-04-11 11:42:15 +04:00
talloc_free ( locked ) ;
2004-10-17 06:55:47 +04:00
return NT_STATUS_OK ;
}
/* 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 + + ) {
struct lock_struct * lock = & locks [ i ] ;
2006-05-15 15:43:25 +04:00
if ( lock - > context . ctx = = brl & &
2007-01-10 13:52:09 +03:00
cluster_id_equal ( & lock - > context . server , & brl - > server ) & &
2006-05-20 12:15:22 +04:00
lock - > ntvfs = = brlh - > ntvfs ) {
2004-10-17 06:55:47 +04:00
/* found it - delete it */
if ( count > 1 & & i < count - 1 ) {
memmove ( & locks [ i ] , & locks [ i + 1 ] ,
sizeof ( * locks ) * ( ( count - 1 ) - i ) ) ;
}
count - - ;
i - - ;
dcount + + ;
}
}
status = NT_STATUS_OK ;
if ( count = = 0 ) {
2013-04-11 11:42:15 +04:00
status = dbwrap_record_delete ( locked ) ;
2004-10-17 06:55:47 +04:00
} else if ( dcount ! = 0 ) {
2004-10-18 11:40:17 +04:00
/* tell all pending lock holders for this file that
they have a chance now . This is a bit indiscriminant ,
but works OK */
2007-01-19 06:58:16 +03:00
brl_tdb_notify_all ( brl , locks , count ) ;
2004-10-18 11:40:17 +04:00
dbuf . dsize = count * sizeof ( * locks ) ;
2013-04-11 11:42:15 +04:00
status = dbwrap_record_store ( locked , dbuf , TDB_REPLACE ) ;
2004-10-17 06:55:47 +04:00
}
2013-04-11 11:42:15 +04:00
talloc_free ( locked ) ;
2004-10-17 06:55:47 +04:00
return status ;
}
2011-01-31 20:20:24 +03:00
static NTSTATUS brl_tdb_count ( struct brl_context * brl , struct brl_handle * brlh ,
int * count )
{
TDB_DATA kbuf , dbuf ;
2013-04-11 11:42:15 +04:00
NTSTATUS status ;
2011-01-31 20:20:24 +03:00
kbuf . dptr = brlh - > key . data ;
kbuf . dsize = brlh - > key . length ;
* count = 0 ;
2013-04-11 11:42:15 +04:00
status = dbwrap_fetch ( brl - > db , brl , kbuf , & dbuf ) ;
if ( NT_STATUS_EQUAL ( status , NT_STATUS_NOT_FOUND ) ) {
2011-01-31 20:20:24 +03:00
return NT_STATUS_OK ;
2013-04-11 11:42:15 +04:00
} else if ( ! NT_STATUS_IS_OK ( status ) ) {
return status ;
2011-01-31 20:20:24 +03:00
}
* count = dbuf . dsize / sizeof ( struct lock_struct ) ;
2013-04-11 11:42:15 +04:00
talloc_free ( dbuf . dptr ) ;
2011-01-31 20:20:24 +03:00
return NT_STATUS_OK ;
}
2007-01-19 06:58:16 +03:00
static const struct brlock_ops brlock_tdb_ops = {
. brl_init = brl_tdb_init ,
. brl_create_handle = brl_tdb_create_handle ,
. brl_lock = brl_tdb_lock ,
. brl_unlock = brl_tdb_unlock ,
. brl_remove_pending = brl_tdb_remove_pending ,
. brl_locktest = brl_tdb_locktest ,
2011-01-31 20:20:24 +03:00
. brl_close = brl_tdb_close ,
. brl_count = brl_tdb_count
2007-01-19 06:58:16 +03:00
} ;
void brl_tdb_init_ops ( void )
{
2011-05-07 10:12:54 +04:00
brlock_set_ops ( & brlock_tdb_ops ) ;
2007-01-19 06:58:16 +03:00
}