2000-04-28 23:38:38 +04:00
/*
2002-01-30 09:08:46 +03:00
Unix SMB / CIFS implementation .
2000-04-28 23:38:38 +04:00
Locking functions
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
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 .
Revision History :
POSIX locking support . Jeremy Allison ( jeremy @ valinux . com ) , Apr . 2000.
*/
# include "includes.h"
2005-04-27 22:32:37 +04:00
# undef DBGC_CLASS
# define DBGC_CLASS DBGC_LOCKING
2000-04-28 23:38:38 +04:00
/*
* The POSIX locking database handle .
*/
static TDB_CONTEXT * posix_lock_tdb ;
/*
* The pending close database handle .
*/
static TDB_CONTEXT * posix_pending_close_tdb ;
/*
* The data in POSIX lock 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 posix_lock {
int fd ;
SMB_OFF_T start ;
SMB_OFF_T size ;
int lock_type ;
} ;
/*
2000-04-29 00:54:23 +04:00
* The data in POSIX pending close records is an unsorted linear array of int
2000-04-28 23:38:38 +04:00
* records . It is unnecessary to store the count as tdb provides the
* size of the record .
*/
/* The key used in both the POSIX databases. */
struct posix_lock_key {
SMB_DEV_T device ;
SMB_INO_T inode ;
} ;
/*******************************************************************
Form a static locking key for a dev / inode pair .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static TDB_DATA locking_key ( SMB_DEV_T dev , SMB_INO_T inode )
{
static struct posix_lock_key key ;
TDB_DATA kbuf ;
2000-05-10 04:05:27 +04:00
memset ( & key , ' \0 ' , sizeof ( key ) ) ;
2000-04-29 00:54:23 +04:00
key . device = dev ;
2000-04-28 23:38:38 +04:00
key . inode = inode ;
kbuf . dptr = ( char * ) & key ;
kbuf . dsize = sizeof ( key ) ;
return kbuf ;
}
/*******************************************************************
Convenience function to get a key from an fsp .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static TDB_DATA locking_key_fsp ( files_struct * fsp )
{
return locking_key ( fsp - > dev , fsp - > inode ) ;
}
/****************************************************************************
Add an fd to the pending close tdb .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL add_fd_to_close_entry ( files_struct * fsp )
{
2000-04-29 00:54:23 +04:00
TDB_DATA kbuf = locking_key_fsp ( fsp ) ;
TDB_DATA dbuf ;
2001-08-12 21:30:01 +04:00
char * tp ;
2000-04-28 23:38:38 +04:00
dbuf . dptr = NULL ;
2005-06-28 05:37:19 +04:00
dbuf . dsize = 0 ;
2000-04-28 23:38:38 +04:00
dbuf = tdb_fetch ( posix_pending_close_tdb , kbuf ) ;
2004-12-07 21:25:53 +03:00
tp = SMB_REALLOC ( dbuf . dptr , dbuf . dsize + sizeof ( int ) ) ;
2001-08-12 21:30:01 +04:00
if ( ! tp ) {
2000-04-28 23:38:38 +04:00
DEBUG ( 0 , ( " add_fd_to_close_entry: Realloc fail ! \n " ) ) ;
2001-09-17 08:16:35 +04:00
SAFE_FREE ( dbuf . dptr ) ;
2000-04-28 23:38:38 +04:00
return False ;
2001-08-26 23:39:40 +04:00
} else
dbuf . dptr = tp ;
2005-07-08 08:51:27 +04:00
memcpy ( dbuf . dptr + dbuf . dsize , & fsp - > fh - > fd , sizeof ( int ) ) ;
2000-04-28 23:38:38 +04:00
dbuf . dsize + = sizeof ( int ) ;
if ( tdb_store ( posix_pending_close_tdb , kbuf , dbuf , TDB_REPLACE ) = = - 1 ) {
DEBUG ( 0 , ( " add_fd_to_close_entry: tdb_store fail ! \n " ) ) ;
}
2001-09-17 08:16:35 +04:00
SAFE_FREE ( dbuf . dptr ) ;
2000-04-28 23:38:38 +04:00
return True ;
}
/****************************************************************************
Remove all fd entries for a specific dev / inode pair from the tdb .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void delete_close_entries ( files_struct * fsp )
{
2000-04-29 00:54:23 +04:00
TDB_DATA kbuf = locking_key_fsp ( fsp ) ;
2000-04-28 23:38:38 +04:00
if ( tdb_delete ( posix_pending_close_tdb , kbuf ) = = - 1 )
DEBUG ( 0 , ( " delete_close_entries: tdb_delete fail ! \n " ) ) ;
}
/****************************************************************************
Get the array of POSIX pending close records for an open fsp . Caller must
free . Returns number of entries .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static size_t get_posix_pending_close_entries ( files_struct * fsp , int * * entries )
{
2000-04-29 00:54:23 +04:00
TDB_DATA kbuf = locking_key_fsp ( fsp ) ;
TDB_DATA dbuf ;
2000-04-28 23:38:38 +04:00
size_t count = 0 ;
* entries = NULL ;
dbuf . dptr = NULL ;
dbuf = tdb_fetch ( posix_pending_close_tdb , kbuf ) ;
2002-11-23 05:52:36 +03:00
if ( ! dbuf . dptr ) {
2000-04-28 23:38:38 +04:00
return 0 ;
}
* entries = ( int * ) dbuf . dptr ;
count = ( size_t ) ( dbuf . dsize / sizeof ( int ) ) ;
return count ;
}
/****************************************************************************
Get the array of POSIX locks for an fsp . Caller must free . Returns
number of entries .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static size_t get_posix_lock_entries ( files_struct * fsp , struct posix_lock * * entries )
{
2000-04-29 00:54:23 +04:00
TDB_DATA kbuf = locking_key_fsp ( fsp ) ;
TDB_DATA dbuf ;
2000-04-28 23:38:38 +04:00
size_t count = 0 ;
* entries = NULL ;
dbuf . dptr = NULL ;
dbuf = tdb_fetch ( posix_lock_tdb , kbuf ) ;
2002-11-23 05:52:36 +03:00
if ( ! dbuf . dptr ) {
2000-04-28 23:38:38 +04:00
return 0 ;
}
2000-04-29 00:54:23 +04:00
* entries = ( struct posix_lock * ) dbuf . dptr ;
count = ( size_t ) ( dbuf . dsize / sizeof ( struct posix_lock ) ) ;
2000-04-28 23:38:38 +04:00
return count ;
}
/****************************************************************************
Deal with pending closes needed by POSIX locking support .
2000-05-03 22:57:21 +04:00
Note that posix_locking_close_file ( ) is expected to have been called
to delete all locks on this fsp before this function is called .
2000-04-28 23:38:38 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int fd_close_posix ( struct connection_struct * conn , files_struct * fsp )
{
int saved_errno = 0 ;
int ret ;
size_t count , i ;
struct posix_lock * entries = NULL ;
int * fd_array = NULL ;
2000-05-03 22:57:21 +04:00
BOOL locks_on_other_fds = False ;
2000-04-28 23:38:38 +04:00
if ( ! lp_posix_locking ( SNUM ( conn ) ) ) {
/*
* No POSIX to worry about , just close .
*/
2005-07-08 08:51:27 +04:00
ret = SMB_VFS_CLOSE ( fsp , fsp - > fh - > fd ) ;
fsp - > fh - > fd = - 1 ;
2000-04-28 23:38:38 +04:00
return ret ;
}
/*
* Get the number of outstanding POSIX locks on this dev / inode pair .
*/
count = get_posix_lock_entries ( fsp , & entries ) ;
2000-05-03 22:57:21 +04:00
/*
* Check if there are any outstanding locks belonging to
* other fd ' s . This should never be the case if posix_locking_close_file ( )
* has been called first , but it never hurts to be * sure * .
*/
for ( i = 0 ; i < count ; i + + ) {
2005-07-08 08:51:27 +04:00
if ( entries [ i ] . fd ! = fsp - > fh - > fd ) {
2000-05-03 22:57:21 +04:00
locks_on_other_fds = True ;
break ;
}
}
if ( locks_on_other_fds ) {
2000-04-28 23:38:38 +04:00
/*
* There are outstanding locks on this dev / inode pair on other fds .
2005-07-08 08:51:27 +04:00
* Add our fd to the pending close tdb and set fsp - > fh - > fd to - 1.
2000-04-28 23:38:38 +04:00
*/
if ( ! add_fd_to_close_entry ( fsp ) ) {
2001-09-17 08:16:35 +04:00
SAFE_FREE ( entries ) ;
2004-10-21 22:39:16 +04:00
return - 1 ;
2000-04-28 23:38:38 +04:00
}
2001-09-17 08:16:35 +04:00
SAFE_FREE ( entries ) ;
2005-07-08 08:51:27 +04:00
fsp - > fh - > fd = - 1 ;
2000-04-28 23:38:38 +04:00
return 0 ;
}
2001-09-17 08:16:35 +04:00
SAFE_FREE ( entries ) ;
2000-04-28 23:38:38 +04:00
/*
* No outstanding POSIX locks . Get the pending close fd ' s
* from the tdb and close them all .
*/
2000-04-29 00:54:23 +04:00
count = get_posix_pending_close_entries ( fsp , & fd_array ) ;
2000-04-28 23:38:38 +04:00
if ( count ) {
DEBUG ( 10 , ( " fd_close_posix: doing close on %u fd's. \n " , ( unsigned int ) count ) ) ;
for ( i = 0 ; i < count ; i + + ) {
2003-05-14 14:59:01 +04:00
if ( SMB_VFS_CLOSE ( fsp , fd_array [ i ] ) = = - 1 ) {
2000-04-28 23:38:38 +04:00
saved_errno = errno ;
}
}
/*
* Delete all fd ' s stored in the tdb
* for this dev / inode pair .
*/
delete_close_entries ( fsp ) ;
}
2001-09-17 08:16:35 +04:00
SAFE_FREE ( fd_array ) ;
2000-04-28 23:38:38 +04:00
/*
* Finally close the fd associated with this fsp .
*/
2005-07-08 08:51:27 +04:00
ret = SMB_VFS_CLOSE ( fsp , fsp - > fh - > fd ) ;
2000-04-28 23:38:38 +04:00
if ( saved_errno ! = 0 ) {
2004-10-21 22:39:16 +04:00
errno = saved_errno ;
2000-04-28 23:38:38 +04:00
ret = - 1 ;
2004-10-21 22:39:16 +04:00
}
2000-04-28 23:38:38 +04:00
2005-07-08 08:51:27 +04:00
fsp - > fh - > fd = - 1 ;
2000-04-28 23:38:38 +04:00
return ret ;
}
/****************************************************************************
Debugging aid : - ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static const char * posix_lock_type_name ( int lock_type )
{
return ( lock_type = = F_RDLCK ) ? " READ " : " WRITE " ;
}
2000-05-04 23:01:14 +04:00
/****************************************************************************
Delete a POSIX lock entry by index number . Used if the tdb add succeeds , but
then the POSIX fcntl lock fails .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL delete_posix_lock_entry_by_index ( files_struct * fsp , size_t entry )
{
TDB_DATA kbuf = locking_key_fsp ( fsp ) ;
TDB_DATA dbuf ;
struct posix_lock * locks ;
size_t count ;
dbuf . dptr = NULL ;
dbuf = tdb_fetch ( posix_lock_tdb , kbuf ) ;
if ( ! dbuf . dptr ) {
DEBUG ( 10 , ( " delete_posix_lock_entry_by_index: tdb_fetch failed ! \n " ) ) ;
goto fail ;
}
count = ( size_t ) ( dbuf . dsize / sizeof ( struct posix_lock ) ) ;
locks = ( struct posix_lock * ) dbuf . dptr ;
if ( count = = 1 ) {
tdb_delete ( posix_lock_tdb , kbuf ) ;
} else {
if ( entry < count - 1 ) {
2005-06-28 05:37:19 +04:00
memmove ( & locks [ entry ] , & locks [ entry + 1 ] , sizeof ( struct posix_lock ) * ( ( count - 1 ) - entry ) ) ;
2000-05-04 23:01:14 +04:00
}
2005-06-28 05:05:59 +04:00
dbuf . dsize - = sizeof ( struct posix_lock ) ;
2000-05-04 23:01:14 +04:00
tdb_store ( posix_lock_tdb , kbuf , dbuf , TDB_REPLACE ) ;
}
2001-09-17 08:16:35 +04:00
SAFE_FREE ( dbuf . dptr ) ;
2000-05-04 23:01:14 +04:00
return True ;
fail :
2002-11-23 05:52:36 +03:00
SAFE_FREE ( dbuf . dptr ) ;
return False ;
2000-05-04 23:01:14 +04:00
}
2000-04-28 23:38:38 +04:00
/****************************************************************************
2000-05-26 00:05:50 +04:00
Add an entry into the POSIX locking tdb . We return the index number of the
added lock ( used in case we need to delete * exactly * this entry ) . Returns
False on fail , True on success .
2000-04-28 23:38:38 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-05-26 00:05:50 +04:00
static BOOL add_posix_lock_entry ( files_struct * fsp , SMB_OFF_T start , SMB_OFF_T size , int lock_type , size_t * pentry_num )
2000-04-28 23:38:38 +04:00
{
2000-04-29 00:54:23 +04:00
TDB_DATA kbuf = locking_key_fsp ( fsp ) ;
TDB_DATA dbuf ;
2000-05-03 06:27:41 +04:00
struct posix_lock pl ;
2001-08-12 21:30:01 +04:00
char * tp ;
2000-05-04 23:01:14 +04:00
2000-04-28 23:38:38 +04:00
dbuf . dptr = NULL ;
2005-06-28 05:05:59 +04:00
dbuf . dsize = 0 ;
2000-04-28 23:38:38 +04:00
dbuf = tdb_fetch ( posix_lock_tdb , kbuf ) ;
2005-06-28 05:05:59 +04:00
* pentry_num = ( size_t ) ( dbuf . dsize / sizeof ( struct posix_lock ) ) ;
2000-05-04 23:01:14 +04:00
2000-05-03 06:27:41 +04:00
/*
2000-05-04 23:01:14 +04:00
* Add new record .
2000-05-03 01:07:25 +04:00
*/
2005-07-08 08:51:27 +04:00
pl . fd = fsp - > fh - > fd ;
2000-05-03 06:27:41 +04:00
pl . start = start ;
pl . size = size ;
pl . lock_type = lock_type ;
2000-05-03 01:07:25 +04:00
2005-06-28 05:05:59 +04:00
tp = SMB_REALLOC ( dbuf . dptr , dbuf . dsize + sizeof ( struct posix_lock ) ) ;
2001-08-12 21:30:01 +04:00
if ( ! tp ) {
2000-05-03 06:27:41 +04:00
DEBUG ( 0 , ( " add_posix_lock_entry: Realloc fail ! \n " ) ) ;
goto fail ;
2001-08-26 23:39:40 +04:00
} else
dbuf . dptr = tp ;
2000-04-28 23:38:38 +04:00
2005-06-28 05:05:59 +04:00
memcpy ( dbuf . dptr + dbuf . dsize , & pl , sizeof ( struct posix_lock ) ) ;
dbuf . dsize + = sizeof ( struct posix_lock ) ;
2000-05-03 01:07:25 +04:00
2000-04-28 23:38:38 +04:00
if ( tdb_store ( posix_lock_tdb , kbuf , dbuf , TDB_REPLACE ) = = - 1 ) {
DEBUG ( 0 , ( " add_posix_lock: Failed to add lock entry on file %s \n " , fsp - > fsp_name ) ) ;
goto fail ;
}
2002-11-23 05:52:36 +03:00
SAFE_FREE ( dbuf . dptr ) ;
2000-04-28 23:38:38 +04:00
2000-05-26 00:05:50 +04:00
DEBUG ( 10 , ( " add_posix_lock: File %s: type = %s: start=%.0f size=%.0f: dev=%.0f inode=%.0f \n " ,
fsp - > fsp_name , posix_lock_type_name ( lock_type ) , ( double ) start , ( double ) size ,
2000-04-28 23:38:38 +04:00
( double ) fsp - > dev , ( double ) fsp - > inode ) ) ;
2002-11-23 05:52:36 +03:00
return True ;
2000-04-28 23:38:38 +04:00
fail :
2002-11-23 05:52:36 +03:00
SAFE_FREE ( dbuf . dptr ) ;
return False ;
2000-05-26 00:05:50 +04:00
}
/****************************************************************************
Calculate if locks have any overlap at all .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL does_lock_overlap ( SMB_OFF_T start1 , SMB_OFF_T size1 , SMB_OFF_T start2 , SMB_OFF_T size2 )
{
if ( start1 > = start2 & & start1 < = start2 + size2 )
return True ;
2000-10-06 22:13:52 +04:00
if ( start1 < start2 & & start1 + size1 > start2 )
2000-05-26 00:05:50 +04:00
return True ;
return False ;
2000-04-28 23:38:38 +04:00
}
/****************************************************************************
2000-05-03 06:27:41 +04:00
Delete an entry from the POSIX locking tdb . Returns a copy of the entry being
2000-05-26 00:05:50 +04:00
deleted and the number of records that are overlapped by this one , or - 1 on error .
2000-04-28 23:38:38 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-05-03 06:27:41 +04:00
static int delete_posix_lock_entry ( files_struct * fsp , SMB_OFF_T start , SMB_OFF_T size , struct posix_lock * pl )
2000-04-28 23:38:38 +04:00
{
2000-04-29 00:54:23 +04:00
TDB_DATA kbuf = locking_key_fsp ( fsp ) ;
TDB_DATA dbuf ;
2000-04-28 23:38:38 +04:00
struct posix_lock * locks ;
size_t i , count ;
2000-05-04 23:01:14 +04:00
BOOL found = False ;
int num_overlapping_records = 0 ;
2000-04-28 23:38:38 +04:00
dbuf . dptr = NULL ;
2000-05-03 06:27:41 +04:00
2000-04-28 23:38:38 +04:00
dbuf = tdb_fetch ( posix_lock_tdb , kbuf ) ;
if ( ! dbuf . dptr ) {
DEBUG ( 10 , ( " delete_posix_lock_entry: tdb_fetch failed ! \n " ) ) ;
goto fail ;
}
/* There are existing locks - find a match. */
2000-04-29 00:54:23 +04:00
locks = ( struct posix_lock * ) dbuf . dptr ;
2005-06-28 05:05:59 +04:00
count = ( size_t ) ( dbuf . dsize / sizeof ( struct posix_lock ) ) ;
2000-04-28 23:38:38 +04:00
2000-05-03 06:27:41 +04:00
/*
2000-05-04 23:01:14 +04:00
* Search for and delete the first record that matches the
* unlock criteria .
2000-05-03 06:27:41 +04:00
*/
2000-04-28 23:38:38 +04:00
for ( i = 0 ; i < count ; i + + ) {
2000-05-03 06:27:41 +04:00
struct posix_lock * entry = & locks [ i ] ;
2000-04-28 23:38:38 +04:00
2005-07-08 08:51:27 +04:00
if ( entry - > fd = = fsp - > fh - > fd & &
2000-05-03 06:27:41 +04:00
entry - > start = = start & &
entry - > size = = size ) {
2000-05-03 01:07:25 +04:00
2000-05-03 06:27:41 +04:00
/* Make a copy if requested. */
if ( pl )
* pl = * entry ;
/* Found it - delete it. */
if ( count = = 1 ) {
tdb_delete ( posix_lock_tdb , kbuf ) ;
2000-05-03 01:07:25 +04:00
} else {
2000-05-03 06:27:41 +04:00
if ( i < count - 1 ) {
2005-06-28 05:05:59 +04:00
memmove ( & locks [ i ] , & locks [ i + 1 ] , sizeof ( struct posix_lock ) * ( ( count - 1 ) - i ) ) ;
2000-05-03 06:27:41 +04:00
}
2005-06-28 05:05:59 +04:00
dbuf . dsize - = sizeof ( struct posix_lock ) ;
2000-04-29 00:54:23 +04:00
tdb_store ( posix_lock_tdb , kbuf , dbuf , TDB_REPLACE ) ;
2000-04-28 23:38:38 +04:00
}
2000-05-04 23:01:14 +04:00
count - - ;
found = True ;
break ;
2000-04-28 23:38:38 +04:00
}
}
2000-05-04 23:01:14 +04:00
if ( ! found )
goto fail ;
/*
* Count the number of entries that are
2000-05-26 00:05:50 +04:00
* overlapped by this unlock request .
2000-05-04 23:01:14 +04:00
*/
for ( i = 0 ; i < count ; i + + ) {
struct posix_lock * entry = & locks [ i ] ;
2005-07-08 08:51:27 +04:00
if ( fsp - > fh - > fd = = entry - > fd & &
2000-05-26 00:05:50 +04:00
does_lock_overlap ( start , size , entry - > start , entry - > size ) )
2000-05-04 23:01:14 +04:00
num_overlapping_records + + ;
}
2000-05-26 00:05:50 +04:00
DEBUG ( 10 , ( " delete_posix_lock_entry: type = %s: start=%.0f size=%.0f, num_records = %d \n " ,
posix_lock_type_name ( pl - > lock_type ) , ( double ) pl - > start , ( double ) pl - > size ,
( unsigned int ) num_overlapping_records ) ) ;
2000-05-04 23:01:14 +04:00
2002-11-23 05:52:36 +03:00
SAFE_FREE ( dbuf . dptr ) ;
2000-05-04 23:01:14 +04:00
return num_overlapping_records ;
2000-04-28 23:38:38 +04:00
fail :
2002-11-23 05:52:36 +03:00
SAFE_FREE ( dbuf . dptr ) ;
return - 1 ;
2000-04-28 23:38:38 +04:00
}
/****************************************************************************
Utility function to map a lock type correctly depending on the open
mode of a file .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static int map_posix_lock_type ( files_struct * fsp , enum brl_type lock_type )
{
if ( ( lock_type = = WRITE_LOCK ) & & ! fsp - > can_write ) {
/*
* Many UNIX ' s cannot get a write lock on a file opened read - only .
* Win32 locking semantics allow this .
* Do the best we can and attempt a read - only lock .
*/
DEBUG ( 10 , ( " map_posix_lock_type: Downgrading write lock to read due to read-only file. \n " ) ) ;
return F_RDLCK ;
2005-07-08 08:51:27 +04:00
}
#if 0
/* We no longer open files write-only. */
else if ( ( lock_type = = READ_LOCK ) & & ! fsp - > can_read ) {
2000-04-28 23:38:38 +04:00
/*
* Ditto for read locks on write only files .
*/
DEBUG ( 10 , ( " map_posix_lock_type: Changing read lock to write due to write-only file. \n " ) ) ;
return F_WRLCK ;
}
2005-07-08 08:51:27 +04:00
# endif
2000-04-28 23:38:38 +04:00
2005-06-28 05:05:59 +04:00
/*
* This return should be the most normal , as we attempt
* to always open files read / write .
*/
2000-04-28 23:38:38 +04:00
2005-06-28 05:05:59 +04:00
return ( lock_type = = READ_LOCK ) ? F_RDLCK : F_WRLCK ;
2000-04-28 23:38:38 +04:00
}
/****************************************************************************
Check to see if the given unsigned lock range is within the possible POSIX
range . Modifies the given args to be in range if possible , just returns
False if not .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL posix_lock_in_range ( SMB_OFF_T * offset_out , SMB_OFF_T * count_out ,
2005-06-28 05:05:59 +04:00
SMB_BIG_UINT u_offset , SMB_BIG_UINT u_count )
2000-04-28 23:38:38 +04:00
{
2000-05-26 00:05:50 +04:00
SMB_OFF_T offset = ( SMB_OFF_T ) u_offset ;
SMB_OFF_T count = ( SMB_OFF_T ) u_count ;
2000-04-28 23:38:38 +04:00
/*
2000-05-26 00:05:50 +04:00
* For the type of system we are , attempt to
* find the maximum positive lock offset as an SMB_OFF_T .
2000-04-28 23:38:38 +04:00
*/
2003-02-12 23:40:29 +03:00
# if defined(MAX_POSITIVE_LOCK_OFFSET) /* Some systems have arbitrary limits. */
SMB_OFF_T max_positive_lock_offset = ( MAX_POSITIVE_LOCK_OFFSET ) ;
# elif defined(LARGE_SMB_OFF_T) && !defined(HAVE_BROKEN_FCNTL64_LOCKS)
2000-04-28 23:38:38 +04:00
/*
2000-05-26 00:05:50 +04:00
* In this case SMB_OFF_T is 64 bits ,
* and the underlying system can handle 64 bit signed locks .
2000-04-28 23:38:38 +04:00
*/
2005-03-02 23:19:10 +03:00
SMB_OFF_T mask2 = ( ( SMB_OFF_T ) 0x4 ) < < ( SMB_OFF_T_BITS - 4 ) ;
SMB_OFF_T mask = ( mask2 < < 1 ) ;
SMB_OFF_T max_positive_lock_offset = ~ mask ;
2000-04-28 23:38:38 +04:00
# else /* !LARGE_SMB_OFF_T || HAVE_BROKEN_FCNTL64_LOCKS */
/*
* In this case either SMB_OFF_T is 32 bits ,
* or the underlying system cannot handle 64 bit signed locks .
2000-05-26 00:05:50 +04:00
* All offsets & counts must be 2 ^ 31 or less .
2000-04-28 23:38:38 +04:00
*/
2005-03-02 23:19:10 +03:00
SMB_OFF_T max_positive_lock_offset = 0x7FFFFFFF ;
2000-04-28 23:38:38 +04:00
2000-05-26 00:05:50 +04:00
# endif /* !LARGE_SMB_OFF_T || HAVE_BROKEN_FCNTL64_LOCKS */
2000-04-28 23:38:38 +04:00
2001-08-26 23:39:40 +04:00
/*
* POSIX locks of length zero mean lock to end - of - file .
* Win32 locks of length zero are point probes . Ignore
* any Win32 locks of length zero . JRA .
*/
if ( count = = ( SMB_OFF_T ) 0 ) {
DEBUG ( 10 , ( " posix_lock_in_range: count = 0, ignoring. \n " ) ) ;
return False ;
}
2000-04-28 23:38:38 +04:00
/*
2000-05-26 00:05:50 +04:00
* If the given offset was > max_positive_lock_offset then we cannot map this at all
* ignore this lock .
2000-04-28 23:38:38 +04:00
*/
2000-05-26 00:05:50 +04:00
if ( u_offset & ~ ( ( SMB_BIG_UINT ) max_positive_lock_offset ) ) {
DEBUG ( 10 , ( " posix_lock_in_range: (offset = %.0f) offset > %.0f and we cannot handle this. Ignoring lock. \n " ,
( double ) u_offset , ( double ) ( ( SMB_BIG_UINT ) max_positive_lock_offset ) ) ) ;
2000-04-28 23:38:38 +04:00
return False ;
}
/*
2003-02-12 23:40:29 +03:00
* We must truncate the count to less than max_positive_lock_offset .
2000-04-28 23:38:38 +04:00
*/
2003-02-12 23:40:29 +03:00
if ( u_count & ~ ( ( SMB_BIG_UINT ) max_positive_lock_offset ) )
2000-05-26 00:05:50 +04:00
count = max_positive_lock_offset ;
2000-04-28 23:38:38 +04:00
/*
2000-05-26 00:05:50 +04:00
* Truncate count to end at max lock offset .
2000-04-28 23:38:38 +04:00
*/
2000-05-26 00:05:50 +04:00
if ( offset + count < 0 | | offset + count > max_positive_lock_offset )
count = max_positive_lock_offset - offset ;
2000-04-28 23:38:38 +04:00
/*
2000-05-26 00:05:50 +04:00
* If we ate all the count , ignore this lock .
2000-04-28 23:38:38 +04:00
*/
2000-05-26 00:05:50 +04:00
if ( count = = 0 ) {
DEBUG ( 10 , ( " posix_lock_in_range: Count = 0. Ignoring lock u_offset = %.0f, u_count = %.0f \n " ,
( double ) u_offset , ( double ) u_count ) ) ;
2000-04-28 23:38:38 +04:00
return False ;
}
/*
* The mapping was successful .
*/
DEBUG ( 10 , ( " posix_lock_in_range: offset_out = %.0f, count_out = %.0f \n " ,
( double ) offset , ( double ) count ) ) ;
* offset_out = offset ;
* count_out = count ;
return True ;
}
2000-05-02 07:20:47 +04:00
/****************************************************************************
Actual function that does POSIX locks . Copes with 64 - > 32 bit cruft and
broken NFS implementations .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static BOOL posix_fcntl_lock ( files_struct * fsp , int op , SMB_OFF_T offset , SMB_OFF_T count , int type )
{
int ret ;
2005-07-08 08:51:27 +04:00
DEBUG ( 8 , ( " posix_fcntl_lock %d %d %.0f %.0f %d \n " , fsp - > fh - > fd , op , ( double ) offset , ( double ) count , type ) ) ;
2000-05-02 07:20:47 +04:00
2005-07-08 08:51:27 +04:00
ret = SMB_VFS_LOCK ( fsp , fsp - > fh - > fd , op , offset , count , type ) ;
2000-05-02 07:20:47 +04:00
2001-09-07 02:43:21 +04:00
if ( ! ret & & ( ( errno = = EFBIG ) | | ( errno = = ENOLCK ) | | ( errno = = EINVAL ) ) ) {
2000-05-02 07:20:47 +04:00
2001-09-07 02:43:21 +04:00
DEBUG ( 0 , ( " posix_fcntl_lock: WARNING: lock request at offset %.0f, length %.0f returned \n " ,
( double ) offset , ( double ) count ) ) ;
DEBUG ( 0 , ( " an %s error. This can happen when using 64 bit lock offsets \n " , strerror ( errno ) ) ) ;
DEBUG ( 0 , ( " on 32 bit NFS mounted file systems. \n " ) ) ;
2000-05-02 07:20:47 +04:00
2001-09-07 02:43:21 +04:00
/*
* If the offset is > 0x7FFFFFFF then this will cause problems on
* 32 bit NFS mounted filesystems . Just ignore it .
*/
if ( offset & ~ ( ( SMB_OFF_T ) 0x7fffffff ) ) {
DEBUG ( 0 , ( " Offset greater than 31 bits. Returning success. \n " ) ) ;
return True ;
2000-05-02 07:20:47 +04:00
}
2001-09-07 02:43:21 +04:00
if ( count & ~ ( ( SMB_OFF_T ) 0x7fffffff ) ) {
/* 32 bit NFS file system, retry with smaller offset */
DEBUG ( 0 , ( " Count greater than 31 bits - retrying with 31 bit truncated length. \n " ) ) ;
errno = 0 ;
count & = 0x7fffffff ;
2005-07-08 08:51:27 +04:00
ret = SMB_VFS_LOCK ( fsp , fsp - > fh - > fd , op , offset , count , type ) ;
2001-09-07 02:43:21 +04:00
}
2000-05-02 07:20:47 +04:00
}
2001-09-07 02:43:21 +04:00
DEBUG ( 8 , ( " posix_fcntl_lock: Lock call %s \n " , ret ? " successful " : " failed " ) ) ;
2000-05-02 07:20:47 +04:00
2001-09-07 02:43:21 +04:00
return ret ;
2000-05-02 07:20:47 +04:00
}
2000-04-28 23:38:38 +04:00
/****************************************************************************
POSIX function to see if a file region is locked . Returns True if the
region is locked , False otherwise .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
BOOL is_posix_locked ( files_struct * fsp , SMB_BIG_UINT u_offset , SMB_BIG_UINT u_count , enum brl_type lock_type )
{
SMB_OFF_T offset ;
SMB_OFF_T count ;
int posix_lock_type = map_posix_lock_type ( fsp , lock_type ) ;
DEBUG ( 10 , ( " is_posix_locked: File %s, offset = %.0f, count = %.0f, type = %s \n " ,
2000-04-29 00:54:23 +04:00
fsp - > fsp_name , ( double ) u_offset , ( double ) u_count , posix_lock_type_name ( lock_type ) ) ) ;
2000-04-28 23:38:38 +04:00
/*
* If the requested lock won ' t fit in the POSIX range , we will
* never set it , so presume it is not locked .
*/
if ( ! posix_lock_in_range ( & offset , & count , u_offset , u_count ) )
return False ;
/*
* Note that most UNIX ' s can * test * for a write lock on
* a read - only fd , just not * set * a write lock on a read - only
* fd . So we don ' t need to use map_lock_type here .
*/
2000-05-02 07:20:47 +04:00
return posix_fcntl_lock ( fsp , SMB_F_GETLK , offset , count , posix_lock_type ) ;
2000-04-28 23:38:38 +04:00
}
/*
* Structure used when splitting a lock range
* into a POSIX lock range . Doubly linked list .
*/
2000-05-26 00:05:50 +04:00
struct lock_list {
2005-06-28 05:05:59 +04:00
struct lock_list * next ;
struct lock_list * prev ;
SMB_OFF_T start ;
SMB_OFF_T size ;
2000-04-28 23:38:38 +04:00
} ;
/****************************************************************************
Create a list of lock ranges that don ' t overlap a given range . Used in calculating
2000-05-26 00:05:50 +04:00
POSIX locks and unlocks . This is a difficult function that requires ASCII art to
2000-04-28 23:38:38 +04:00
understand it : - ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-05-26 00:05:50 +04:00
static struct lock_list * posix_lock_list ( TALLOC_CTX * ctx , struct lock_list * lhead , files_struct * fsp )
2000-04-28 23:38:38 +04:00
{
2000-04-29 00:54:23 +04:00
TDB_DATA kbuf = locking_key_fsp ( fsp ) ;
TDB_DATA dbuf ;
struct posix_lock * locks ;
size_t num_locks , i ;
2000-04-28 23:38:38 +04:00
dbuf . dptr = NULL ;
2000-04-29 00:54:23 +04:00
dbuf = tdb_fetch ( posix_lock_tdb , kbuf ) ;
2000-04-28 23:38:38 +04:00
2000-05-26 00:05:50 +04:00
if ( ! dbuf . dptr )
return lhead ;
2000-04-28 23:38:38 +04:00
2000-04-29 00:54:23 +04:00
locks = ( struct posix_lock * ) dbuf . dptr ;
2005-06-28 05:05:59 +04:00
num_locks = ( size_t ) ( dbuf . dsize / sizeof ( struct posix_lock ) ) ;
2000-04-28 23:38:38 +04:00
/*
* Check the current lock list on this dev / inode pair .
* Quit if the list is deleted .
*/
2000-05-26 00:05:50 +04:00
DEBUG ( 10 , ( " posix_lock_list: curr: start=%.0f,size=%.0f \n " ,
( double ) lhead - > start , ( double ) lhead - > size ) ) ;
2000-04-28 23:38:38 +04:00
2000-05-26 00:05:50 +04:00
for ( i = 0 ; i < num_locks & & lhead ; i + + ) {
2000-04-28 23:38:38 +04:00
2000-04-29 00:54:23 +04:00
struct posix_lock * lock = & locks [ i ] ;
2000-05-26 00:05:50 +04:00
struct lock_list * l_curr ;
2000-04-28 23:38:38 +04:00
/*
2000-05-26 00:05:50 +04:00
* Walk the lock list , checking for overlaps . Note that
* the lock list can expand within this loop if the current
2000-04-28 23:38:38 +04:00
* range being examined needs to be split .
*/
2000-05-26 00:05:50 +04:00
for ( l_curr = lhead ; l_curr ; ) {
2000-04-28 23:38:38 +04:00
2000-05-26 00:05:50 +04:00
DEBUG ( 10 , ( " posix_lock_list: lock: fd=%d: start=%.0f,size=%.0f:type=%s " , lock - > fd ,
2000-05-04 23:01:14 +04:00
( double ) lock - > start , ( double ) lock - > size , posix_lock_type_name ( lock - > lock_type ) ) ) ;
2000-04-28 23:38:38 +04:00
2000-05-26 00:05:50 +04:00
if ( ( l_curr - > start > = ( lock - > start + lock - > size ) ) | |
( lock - > start > = ( l_curr - > start + l_curr - > size ) ) ) {
2000-04-28 23:38:38 +04:00
/* No overlap with this lock - leave this range alone. */
/*********************************************
+ - - - - - - - - - +
2000-05-26 00:05:50 +04:00
| l_curr |
2000-04-28 23:38:38 +04:00
+ - - - - - - - - - +
+ - - - - - - - +
| lock |
+ - - - - - - - +
OR . . . .
+ - - - - - - - - - +
2000-05-26 00:05:50 +04:00
| l_curr |
2000-04-28 23:38:38 +04:00
+ - - - - - - - - - +
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
DEBUG ( 10 , ( " no overlap case. \n " ) ) ;
2000-05-26 00:05:50 +04:00
l_curr = l_curr - > next ;
2000-04-28 23:38:38 +04:00
2000-05-26 00:05:50 +04:00
} else if ( ( l_curr - > start > = lock - > start ) & &
( l_curr - > start + l_curr - > size < = lock - > start + lock - > size ) ) {
2000-04-28 23:38:38 +04:00
/*
* This unlock is completely overlapped by this existing lock range
* and thus should have no effect ( not be unlocked ) . Delete it from the list .
*/
/*********************************************
+ - - - - - - - - - +
2000-05-26 00:05:50 +04:00
| l_curr |
2000-04-28 23:38:38 +04:00
+ - - - - - - - - - +
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - +
| lock |
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - +
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Save the next pointer */
2000-05-26 00:05:50 +04:00
struct lock_list * ul_next = l_curr - > next ;
2000-04-28 23:38:38 +04:00
DEBUG ( 10 , ( " delete case. \n " ) ) ;
2000-05-26 00:05:50 +04:00
DLIST_REMOVE ( lhead , l_curr ) ;
if ( lhead = = NULL )
2000-04-28 23:38:38 +04:00
break ; /* No more list... */
2000-05-26 00:05:50 +04:00
l_curr = ul_next ;
2000-04-28 23:38:38 +04:00
2000-05-26 00:05:50 +04:00
} else if ( ( l_curr - > start > = lock - > start ) & &
( l_curr - > start < lock - > start + lock - > size ) & &
( l_curr - > start + l_curr - > size > lock - > start + lock - > size ) ) {
2000-04-28 23:38:38 +04:00
/*
* This unlock overlaps the existing lock range at the high end .
* Truncate by moving start to existing range end and reducing size .
*/
/*********************************************
+ - - - - - - - - - - - - - - - +
2000-05-26 00:05:50 +04:00
| l_curr |
2000-04-28 23:38:38 +04:00
+ - - - - - - - - - - - - - - - +
+ - - - - - - - - - - - - - - - +
| lock |
+ - - - - - - - - - - - - - - - +
BECOMES . . . .
+ - - - - - - - +
2000-05-26 00:05:50 +04:00
| l_curr |
2000-04-28 23:38:38 +04:00
+ - - - - - - - +
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-05-26 00:05:50 +04:00
l_curr - > size = ( l_curr - > start + l_curr - > size ) - ( lock - > start + lock - > size ) ;
l_curr - > start = lock - > start + lock - > size ;
2000-04-28 23:38:38 +04:00
DEBUG ( 10 , ( " truncate high case: start=%.0f,size=%.0f \n " ,
2000-05-26 00:05:50 +04:00
( double ) l_curr - > start , ( double ) l_curr - > size ) ) ;
2000-04-28 23:38:38 +04:00
2000-05-26 00:05:50 +04:00
l_curr = l_curr - > next ;
2000-04-28 23:38:38 +04:00
2000-05-26 00:05:50 +04:00
} else if ( ( l_curr - > start < lock - > start ) & &
( l_curr - > start + l_curr - > size > lock - > start ) & &
( l_curr - > start + l_curr - > size < = lock - > start + lock - > size ) ) {
2000-04-28 23:38:38 +04:00
/*
* This unlock overlaps the existing lock range at the low end .
* Truncate by reducing size .
*/
/*********************************************
+ - - - - - - - - - - - - - - - +
2000-05-26 00:05:50 +04:00
| l_curr |
2000-04-28 23:38:38 +04:00
+ - - - - - - - - - - - - - - - +
+ - - - - - - - - - - - - - - - +
| lock |
+ - - - - - - - - - - - - - - - +
BECOMES . . . .
+ - - - - - - - +
2000-05-26 00:05:50 +04:00
| l_curr |
2000-04-28 23:38:38 +04:00
+ - - - - - - - +
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-05-26 00:05:50 +04:00
l_curr - > size = lock - > start - l_curr - > start ;
2000-04-28 23:38:38 +04:00
DEBUG ( 10 , ( " truncate low case: start=%.0f,size=%.0f \n " ,
2000-05-26 00:05:50 +04:00
( double ) l_curr - > start , ( double ) l_curr - > size ) ) ;
2000-04-28 23:38:38 +04:00
2000-05-26 00:05:50 +04:00
l_curr = l_curr - > next ;
2000-04-28 23:38:38 +04:00
2000-05-26 00:05:50 +04:00
} else if ( ( l_curr - > start < lock - > start ) & &
( l_curr - > start + l_curr - > size > lock - > start + lock - > size ) ) {
2000-04-28 23:38:38 +04:00
/*
* Worst case scenario . Unlock request completely overlaps an existing
* lock range . Split the request into two , push the new ( upper ) request
* into the dlink list , and continue with the entry after ul_new ( as we
* know that ul_new will not overlap with this lock ) .
*/
/*********************************************
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - +
2000-05-26 00:05:50 +04:00
| l_curr |
2000-04-28 23:38:38 +04:00
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - +
+ - - - - - - - - - +
| lock |
+ - - - - - - - - - +
BECOMES . . . . .
+ - - - - - - - + + - - - - - - - - - +
2000-05-26 00:05:50 +04:00
| l_curr | | l_new |
2000-04-28 23:38:38 +04:00
+ - - - - - - - + + - - - - - - - - - +
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2004-12-07 21:25:53 +03:00
struct lock_list * l_new = TALLOC_P ( ctx , struct lock_list ) ;
2000-04-28 23:38:38 +04:00
2000-05-26 00:05:50 +04:00
if ( l_new = = NULL ) {
DEBUG ( 0 , ( " posix_lock_list: talloc fail. \n " ) ) ;
2000-04-28 23:38:38 +04:00
return NULL ; /* The talloc_destroy takes care of cleanup. */
}
2000-05-26 00:05:50 +04:00
ZERO_STRUCTP ( l_new ) ;
l_new - > start = lock - > start + lock - > size ;
l_new - > size = l_curr - > start + l_curr - > size - l_new - > start ;
2000-04-28 23:38:38 +04:00
2000-05-26 00:05:50 +04:00
/* Truncate the l_curr. */
l_curr - > size = lock - > start - l_curr - > start ;
2000-04-28 23:38:38 +04:00
DEBUG ( 10 , ( " split case: curr: start=%.0f,size=%.0f \
2000-05-26 00:05:50 +04:00
new : start = % .0f , size = % .0f \ n " , (double)l_curr->start, (double)l_curr->size,
( double ) l_new - > start , ( double ) l_new - > size ) ) ;
2000-04-28 23:38:38 +04:00
2000-05-05 06:17:39 +04:00
/*
2000-05-26 00:05:50 +04:00
* Add into the dlink list after the l_curr point - NOT at lhead .
2000-05-05 06:17:39 +04:00
* Note we can ' t use DLINK_ADD here as this inserts at the head of the given list .
*/
2000-05-26 00:05:50 +04:00
l_new - > prev = l_curr ;
l_new - > next = l_curr - > next ;
l_curr - > next = l_new ;
2000-05-05 06:17:39 +04:00
/* And move after the link we added. */
2000-05-26 00:05:50 +04:00
l_curr = l_new - > next ;
2000-04-28 23:38:38 +04:00
} else {
/*
* This logic case should never happen . Ensure this is the
* case by forcing an abort . . . . Remove in production .
*/
2000-05-03 21:50:42 +04:00
pstring msg ;
2000-04-28 23:38:38 +04:00
2000-05-26 00:05:50 +04:00
slprintf ( msg , sizeof ( msg ) - 1 , " logic flaw in cases: l_curr: start = %.0f, size = %.0f : \
lock : start = % .0f , size = % .0f \ n " , (double)l_curr->start, (double)l_curr->size, (double)lock->start, (double)lock->size );
2000-05-03 21:50:42 +04:00
smb_panic ( msg ) ;
2000-04-28 23:38:38 +04:00
}
2000-05-26 00:05:50 +04:00
} /* end for ( l_curr = lhead; l_curr;) */
2000-04-28 23:38:38 +04:00
} /* end for (i=0; i<num_locks && ul_head; i++) */
2001-09-17 08:16:35 +04:00
SAFE_FREE ( dbuf . dptr ) ;
2000-04-28 23:38:38 +04:00
2000-05-26 00:05:50 +04:00
return lhead ;
}
/****************************************************************************
POSIX function to acquire a lock . Returns True if the
lock could be granted , False if not .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
BOOL set_posix_lock ( files_struct * fsp , SMB_BIG_UINT u_offset , SMB_BIG_UINT u_count , enum brl_type lock_type )
{
SMB_OFF_T offset ;
SMB_OFF_T count ;
BOOL ret = True ;
size_t entry_num = 0 ;
size_t lock_count ;
TALLOC_CTX * l_ctx = NULL ;
struct lock_list * llist = NULL ;
struct lock_list * ll = NULL ;
int posix_lock_type = map_posix_lock_type ( fsp , lock_type ) ;
DEBUG ( 5 , ( " set_posix_lock: File %s, offset = %.0f, count = %.0f, type = %s \n " ,
fsp - > fsp_name , ( double ) u_offset , ( double ) u_count , posix_lock_type_name ( lock_type ) ) ) ;
/*
* If the requested lock won ' t fit in the POSIX range , we will
* pretend it was successful .
*/
if ( ! posix_lock_in_range ( & offset , & count , u_offset , u_count ) )
return True ;
/*
* Windows is very strange . It allows read locks to be overlayed
* ( even over a write lock ) , but leaves the write lock in force until the first
* unlock . It also reference counts the locks . This means the following sequence :
*
* process1 process2
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* WRITE LOCK : start = 2 , len = 10
* READ LOCK : start = 0 , len = 10 - FAIL
* READ LOCK : start = 0 , len = 14
* READ LOCK : start = 0 , len = 10 - FAIL
* UNLOCK : start = 2 , len = 10
* READ LOCK : start = 0 , len = 10 - OK
*
* Under POSIX , the same sequence in steps 1 and 2 would not be reference counted , but
* would leave a single read lock over the 0 - 14 region . In order to
* re - create Windows semantics mapped to POSIX locks , we create multiple TDB
* entries , one for each overlayed lock request . We are guarenteed by the brlock
* semantics that if a write lock is added , then it will be first in the array .
*/
2002-12-20 23:21:31 +03:00
if ( ( l_ctx = talloc_init ( " set_posix_lock " ) ) = = NULL ) {
2000-05-26 00:05:50 +04:00
DEBUG ( 0 , ( " set_posix_lock: unable to init talloc context. \n " ) ) ;
return True ; /* Not a fatal error. */
}
2004-12-07 21:25:53 +03:00
if ( ( ll = TALLOC_P ( l_ctx , struct lock_list ) ) = = NULL ) {
2000-05-26 00:05:50 +04:00
DEBUG ( 0 , ( " set_posix_lock: unable to talloc unlock list. \n " ) ) ;
talloc_destroy ( l_ctx ) ;
return True ; /* Not a fatal error. */
}
/*
* Create the initial list entry containing the
* lock we want to add .
*/
ZERO_STRUCTP ( ll ) ;
ll - > start = offset ;
ll - > size = count ;
DLIST_ADD ( llist , ll ) ;
/*
* The following call calculates if there are any
* overlapping locks held by this process on
* fd ' s open on the same file and splits this list
* into a list of lock ranges that do not overlap with existing
* POSIX locks .
*/
llist = posix_lock_list ( l_ctx , llist , fsp ) ;
/*
* Now we have the list of ranges to lock it is safe to add the
* entry into the POSIX lock tdb . We take note of the entry we
* added here in case we have to remove it on POSIX lock fail .
*/
if ( ! add_posix_lock_entry ( fsp , offset , count , posix_lock_type , & entry_num ) ) {
DEBUG ( 0 , ( " set_posix_lock: Unable to create posix lock entry ! \n " ) ) ;
talloc_destroy ( l_ctx ) ;
return False ;
}
/*
* Add the POSIX locks on the list of ranges returned .
* As the lock is supposed to be added atomically , we need to
* back out all the locks if any one of these calls fail .
*/
for ( lock_count = 0 , ll = llist ; ll ; ll = ll - > next , lock_count + + ) {
offset = ll - > start ;
count = ll - > size ;
DEBUG ( 5 , ( " set_posix_lock: Real lock: Type = %s: offset = %.0f, count = %.0f \n " ,
posix_lock_type_name ( posix_lock_type ) , ( double ) offset , ( double ) count ) ) ;
if ( ! posix_fcntl_lock ( fsp , SMB_F_SETLK , offset , count , posix_lock_type ) ) {
2001-08-13 21:58:54 +04:00
DEBUG ( 5 , ( " set_posix_lock: Lock fail !: Type = %s: offset = %.0f, count = %.0f. Errno = %s \n " ,
posix_lock_type_name ( posix_lock_type ) , ( double ) offset , ( double ) count , strerror ( errno ) ) ) ;
2000-05-26 00:05:50 +04:00
ret = False ;
break ;
}
}
if ( ! ret ) {
/*
* Back out all the POSIX locks we have on fail .
*/
for ( ll = llist ; lock_count ; ll = ll - > next , lock_count - - ) {
offset = ll - > start ;
count = ll - > size ;
DEBUG ( 5 , ( " set_posix_lock: Backing out locks: Type = %s: offset = %.0f, count = %.0f \n " ,
posix_lock_type_name ( posix_lock_type ) , ( double ) offset , ( double ) count ) ) ;
posix_fcntl_lock ( fsp , SMB_F_SETLK , offset , count , F_UNLCK ) ;
}
/*
* Remove the tdb entry for this lock .
*/
delete_posix_lock_entry_by_index ( fsp , entry_num ) ;
}
talloc_destroy ( l_ctx ) ;
return ret ;
2000-04-28 23:38:38 +04:00
}
/****************************************************************************
2000-04-29 00:54:23 +04:00
POSIX function to release a lock . Returns True if the
2000-04-28 23:38:38 +04:00
lock could be released , False if not .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-04-29 00:54:23 +04:00
BOOL release_posix_lock ( files_struct * fsp , SMB_BIG_UINT u_offset , SMB_BIG_UINT u_count )
2000-04-28 23:38:38 +04:00
{
SMB_OFF_T offset ;
SMB_OFF_T count ;
BOOL ret = True ;
TALLOC_CTX * ul_ctx = NULL ;
2000-05-26 00:05:50 +04:00
struct lock_list * ulist = NULL ;
struct lock_list * ul = NULL ;
2000-05-03 06:27:41 +04:00
struct posix_lock deleted_lock ;
2000-05-04 23:01:14 +04:00
int num_overlapped_entries ;
2000-04-28 23:38:38 +04:00
DEBUG ( 5 , ( " release_posix_lock: File %s, offset = %.0f, count = %.0f \n " ,
2000-04-29 00:54:23 +04:00
fsp - > fsp_name , ( double ) u_offset , ( double ) u_count ) ) ;
2000-04-28 23:38:38 +04:00
/*
* If the requested lock won ' t fit in the POSIX range , we will
* pretend it was successful .
*/
if ( ! posix_lock_in_range ( & offset , & count , u_offset , u_count ) )
return True ;
2000-04-29 00:54:23 +04:00
/*
* We treat this as one unlock request for POSIX accounting purposes even
2000-05-04 23:01:14 +04:00
* if it may later be split into multiple smaller POSIX unlock ranges .
2000-05-26 00:05:50 +04:00
* num_overlapped_entries is the number of existing locks that have any
* overlap with this unlock request .
2000-04-29 00:54:23 +04:00
*/
2000-05-04 23:01:14 +04:00
num_overlapped_entries = delete_posix_lock_entry ( fsp , offset , count , & deleted_lock ) ;
2000-05-03 06:27:41 +04:00
2000-05-04 23:01:14 +04:00
if ( num_overlapped_entries = = - 1 ) {
2005-06-28 05:05:59 +04:00
smb_panic ( " release_posix_lock: unable find entry to delete ! \n " ) ;
2000-05-03 06:27:41 +04:00
}
/*
2000-05-04 23:01:14 +04:00
* If num_overlapped_entries is > 0 , and the lock_type we just deleted from the tdb was
* a POSIX write lock , then before doing the unlock we need to downgrade
2000-05-26 00:05:50 +04:00
* the POSIX lock to a read lock . This allows any overlapping read locks
* to be atomically maintained .
2000-05-03 06:27:41 +04:00
*/
2000-05-04 23:01:14 +04:00
if ( num_overlapped_entries > 0 & & deleted_lock . lock_type = = F_WRLCK ) {
if ( ! posix_fcntl_lock ( fsp , SMB_F_SETLK , offset , count , F_RDLCK ) ) {
2001-08-13 21:58:54 +04:00
DEBUG ( 0 , ( " release_posix_lock: downgrade of lock failed with error %s ! \n " , strerror ( errno ) ) ) ;
2000-05-04 23:01:14 +04:00
return False ;
}
2000-05-03 06:27:41 +04:00
}
2000-04-29 00:54:23 +04:00
2002-12-20 23:21:31 +03:00
if ( ( ul_ctx = talloc_init ( " release_posix_lock " ) ) = = NULL ) {
2000-05-26 00:05:50 +04:00
DEBUG ( 0 , ( " release_posix_lock: unable to init talloc context. \n " ) ) ;
2000-04-28 23:38:38 +04:00
return True ; /* Not a fatal error. */
}
2004-12-07 21:25:53 +03:00
if ( ( ul = TALLOC_P ( ul_ctx , struct lock_list ) ) = = NULL ) {
2000-04-28 23:38:38 +04:00
DEBUG ( 0 , ( " release_posix_lock: unable to talloc unlock list. \n " ) ) ;
talloc_destroy ( ul_ctx ) ;
return True ; /* Not a fatal error. */
}
/*
* Create the initial list entry containing the
* lock we want to remove .
*/
ZERO_STRUCTP ( ul ) ;
ul - > start = offset ;
ul - > size = count ;
DLIST_ADD ( ulist , ul ) ;
/*
* The following call calculates if there are any
2000-04-29 00:54:23 +04:00
* overlapping locks held by this process on
* fd ' s open on the same file and creates a
* list of unlock ranges that will allow
2000-04-28 23:38:38 +04:00
* POSIX lock ranges to remain on the file whilst the
* unlocks are performed .
*/
2000-05-26 00:05:50 +04:00
ulist = posix_lock_list ( ul_ctx , ulist , fsp ) ;
2000-04-28 23:38:38 +04:00
/*
* Release the POSIX locks on the list of ranges returned .
*/
for ( ; ulist ; ulist = ulist - > next ) {
2000-04-29 00:54:23 +04:00
offset = ulist - > start ;
count = ulist - > size ;
2000-04-28 23:38:38 +04:00
DEBUG ( 5 , ( " release_posix_lock: Real unlock: offset = %.0f, count = %.0f \n " ,
( double ) offset , ( double ) count ) ) ;
2000-05-02 07:20:47 +04:00
if ( ! posix_fcntl_lock ( fsp , SMB_F_SETLK , offset , count , F_UNLCK ) )
2000-04-29 00:54:23 +04:00
ret = False ;
2000-04-28 23:38:38 +04:00
}
2000-04-30 15:04:28 +04:00
talloc_destroy ( ul_ctx ) ;
2000-04-28 23:38:38 +04:00
return ret ;
}
/****************************************************************************
2000-04-29 00:54:23 +04:00
Remove all lock entries for a specific dev / inode pair from the tdb .
2000-04-28 23:38:38 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-04-29 00:54:23 +04:00
static void delete_posix_lock_entries ( files_struct * fsp )
2000-04-28 23:38:38 +04:00
{
2000-04-29 00:54:23 +04:00
TDB_DATA kbuf = locking_key_fsp ( fsp ) ;
2000-04-28 23:38:38 +04:00
2000-04-29 00:54:23 +04:00
if ( tdb_delete ( posix_lock_tdb , kbuf ) = = - 1 )
DEBUG ( 0 , ( " delete_close_entries: tdb_delete fail ! \n " ) ) ;
2000-04-29 05:09:31 +04:00
}
/****************************************************************************
Debug function .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-04-30 15:04:28 +04:00
static void dump_entry ( struct posix_lock * pl )
2000-04-29 05:09:31 +04:00
{
DEBUG ( 10 , ( " entry: start=%.0f, size=%.0f, type=%d, fd=%i \n " ,
( double ) pl - > start , ( double ) pl - > size , ( int ) pl - > lock_type , pl - > fd ) ) ;
2000-04-28 23:38:38 +04:00
}
/****************************************************************************
Remove any locks on this fd . Called from file_close ( ) .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void posix_locking_close_file ( files_struct * fsp )
{
2000-04-29 00:54:23 +04:00
struct posix_lock * entries = NULL ;
size_t count , i ;
2000-04-28 23:38:38 +04:00
2000-04-29 00:54:23 +04:00
/*
* Optimization for the common case where we are the only
* opener of a file . If all fd entries are our own , we don ' t
* need to explicitly release all the locks via the POSIX functions ,
* we can just remove all the entries in the tdb and allow the
* close to remove the real locks .
*/
2000-04-28 23:38:38 +04:00
2000-04-29 00:54:23 +04:00
count = get_posix_lock_entries ( fsp , & entries ) ;
2000-04-28 23:38:38 +04:00
2000-04-29 00:54:23 +04:00
if ( count = = 0 ) {
DEBUG ( 10 , ( " posix_locking_close_file: file %s has no outstanding locks. \n " , fsp - > fsp_name ) ) ;
return ;
}
2000-04-28 23:38:38 +04:00
2000-04-29 00:54:23 +04:00
for ( i = 0 ; i < count ; i + + ) {
2005-07-08 08:51:27 +04:00
if ( entries [ i ] . fd ! = fsp - > fh - > fd )
2000-04-29 00:54:23 +04:00
break ;
2000-04-29 05:09:31 +04:00
dump_entry ( & entries [ i ] ) ;
2000-04-29 00:54:23 +04:00
}
2000-04-28 23:38:38 +04:00
2000-04-29 00:54:23 +04:00
if ( i = = count ) {
/* All locks are ours. */
DEBUG ( 10 , ( " posix_locking_close_file: file %s has %u outstanding locks, but all on one fd. \n " ,
fsp - > fsp_name , ( unsigned int ) count ) ) ;
2001-09-17 08:16:35 +04:00
SAFE_FREE ( entries ) ;
2000-04-29 00:54:23 +04:00
delete_posix_lock_entries ( fsp ) ;
return ;
}
2000-04-28 23:38:38 +04:00
2000-04-29 00:54:23 +04:00
/*
* Difficult case . We need to delete all our locks , whilst leaving
* all other POSIX locks in place .
*/
2000-04-28 23:38:38 +04:00
2000-04-29 00:54:23 +04:00
for ( i = 0 ; i < count ; i + + ) {
struct posix_lock * pl = & entries [ i ] ;
2005-07-08 08:51:27 +04:00
if ( pl - > fd = = fsp - > fh - > fd )
2000-05-03 22:57:21 +04:00
release_posix_lock ( fsp , ( SMB_BIG_UINT ) pl - > start , ( SMB_BIG_UINT ) pl - > size ) ;
2000-04-28 23:38:38 +04:00
}
2001-09-17 08:16:35 +04:00
SAFE_FREE ( entries ) ;
2000-04-28 23:38:38 +04:00
}
/*******************************************************************
Create the in - memory POSIX lock databases .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-06-13 19:46:46 +04:00
BOOL posix_locking_init ( int read_only )
2000-04-28 23:38:38 +04:00
{
if ( posix_lock_tdb & & posix_pending_close_tdb )
2000-04-29 00:54:23 +04:00
return True ;
2000-06-13 19:46:46 +04:00
2000-04-28 23:38:38 +04:00
if ( ! posix_lock_tdb )
2001-06-04 09:13:59 +04:00
posix_lock_tdb = tdb_open_log ( NULL , 0 , TDB_INTERNAL ,
2000-06-13 19:46:46 +04:00
read_only ? O_RDONLY : ( O_RDWR | O_CREAT ) , 0644 ) ;
if ( ! posix_lock_tdb ) {
DEBUG ( 0 , ( " Failed to open POSIX byte range locking database. \n " ) ) ;
2000-04-29 00:54:23 +04:00
return False ;
2000-06-13 19:46:46 +04:00
}
2000-04-28 23:38:38 +04:00
if ( ! posix_pending_close_tdb )
2001-06-04 09:13:59 +04:00
posix_pending_close_tdb = tdb_open_log ( NULL , 0 , TDB_INTERNAL ,
2000-06-13 19:46:46 +04:00
read_only ? O_RDONLY : ( O_RDWR | O_CREAT ) , 0644 ) ;
if ( ! posix_pending_close_tdb ) {
DEBUG ( 0 , ( " Failed to open POSIX pending close database. \n " ) ) ;
2000-04-29 00:54:23 +04:00
return False ;
2000-06-13 19:46:46 +04:00
}
2000-04-29 00:54:23 +04:00
return True ;
}
/*******************************************************************
Delete the in - memory POSIX lock databases .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
BOOL posix_locking_end ( void )
{
if ( posix_lock_tdb & & tdb_close ( posix_lock_tdb ) ! = 0 )
return False ;
if ( posix_pending_close_tdb & & tdb_close ( posix_pending_close_tdb ) ! = 0 )
return False ;
return True ;
2000-04-28 23:38:38 +04:00
}