1998-08-17 17:11:34 +04:00
/*
2002-01-30 09:08:46 +03:00
Unix SMB / CIFS implementation .
1998-08-17 17:11:34 +04:00
oplock processing
Copyright ( C ) Andrew Tridgell 1992 - 1998
2001-10-21 01:59:34 +04:00
Copyright ( C ) Jeremy Allison 1998 - 2001
2005-09-30 21:13:37 +04:00
Copyright ( C ) Volker Lendecke 2005
1998-08-17 17:11:34 +04: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 .
*/
# include "includes.h"
1998-08-17 17:44:05 +04:00
/* Current number of oplocks we have outstanding. */
1999-12-13 16:27:58 +03:00
static int32 exclusive_oplocks_open = 0 ;
static int32 level_II_oplocks_open = 0 ;
BOOL global_client_failed_oplock_break = False ;
1998-08-17 17:44:05 +04:00
2005-04-06 20:28:04 +04:00
extern struct timeval smb_last_time ;
extern uint32 global_client_caps ;
1998-08-17 17:11:34 +04:00
extern int smb_read_error ;
2000-06-10 17:38:07 +04:00
static struct kernel_oplocks * koplocks ;
1999-12-13 16:27:58 +03:00
/****************************************************************************
Get the number of current exclusive oplocks .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int32 get_number_of_exclusive_open_oplocks ( void )
{
return exclusive_oplocks_open ;
}
1998-09-05 09:07:05 +04:00
2001-09-06 02:45:48 +04:00
/****************************************************************************
Return True if an oplock message is pending .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
BOOL oplock_message_waiting ( fd_set * fds )
{
2006-01-13 01:17:54 +03:00
if ( koplocks & & koplocks - > msg_waiting ( fds ) ) {
2001-09-06 02:45:48 +04:00
return True ;
2006-01-13 01:17:54 +03:00
}
2001-09-06 02:45:48 +04:00
return False ;
}
2000-06-09 10:58:06 +04:00
1998-09-23 05:48:45 +04:00
/****************************************************************************
2000-06-09 10:58:06 +04:00
Read an oplock break message from either the oplock UDP fd or the
2000-06-10 17:38:07 +04:00
kernel ( if kernel oplocks are supported ) .
1998-09-23 05:48:45 +04:00
If timeout is zero then * fds contains the file descriptors that
are ready to be read and acted upon . If timeout is non - zero then
* fds contains the file descriptors to be selected on for read .
The timeout is in milliseconds
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-09-30 21:13:37 +04:00
void process_kernel_oplocks ( void )
1998-09-23 05:48:45 +04:00
{
2002-07-15 14:35:28 +04:00
fd_set fds ;
2001-10-21 01:59:34 +04:00
2002-07-15 14:35:28 +04:00
FD_ZERO ( & fds ) ;
2001-10-21 01:59:34 +04:00
smb_read_error = 0 ;
2002-07-15 14:35:28 +04:00
/*
* We need to check for kernel oplocks before going into the select
* here , as the EINTR generated by the linux kernel oplock may have
* already been eaten . JRA .
*/
2005-09-30 21:13:37 +04:00
if ( ! koplocks ) {
return ;
2002-07-15 14:35:28 +04:00
}
2005-09-30 21:13:37 +04:00
while ( koplocks - > msg_waiting ( & fds ) ) {
files_struct * fsp ;
2006-01-13 01:17:54 +03:00
char msg [ MSG_SMB_KERNEL_BREAK_SIZE ] ;
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
fsp = koplocks - > receive_message ( & fds ) ;
2002-07-15 14:35:28 +04:00
2005-09-30 21:13:37 +04:00
if ( fsp = = NULL ) {
DEBUG ( 3 , ( " Kernel oplock message announced, but none "
" received \n " ) ) ;
return ;
2001-10-21 01:59:34 +04:00
}
2006-01-13 01:17:54 +03:00
/* Put the kernel break info into the message. */
SDEV_T_VAL ( msg , 0 , fsp - > dev ) ;
SINO_T_VAL ( msg , 8 , fsp - > inode ) ;
SIVAL ( msg , 16 , fsp - > file_id ) ;
/* Don't need to be root here as we're only ever
sending to ourselves . */
2005-09-30 21:13:37 +04:00
message_send_pid ( pid_to_procid ( sys_getpid ( ) ) ,
MSG_SMB_KERNEL_BREAK ,
2006-01-13 01:17:54 +03:00
& msg , MSG_SMB_KERNEL_BREAK_SIZE , True ) ;
2001-10-21 01:59:34 +04:00
}
1998-09-23 05:48:45 +04:00
}
1999-12-13 16:27:58 +03:00
/****************************************************************************
Attempt to set an oplock on a file . Always succeeds if kernel oplocks are
disabled ( just sets flags ) . Returns True if oplock set .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-10-21 01:59:34 +04:00
1999-12-13 16:27:58 +03:00
BOOL set_file_oplock ( files_struct * fsp , int oplock_type )
{
2006-01-13 01:17:54 +03:00
if ( koplocks & & ! koplocks - > set_oplock ( fsp , oplock_type ) ) {
2000-06-09 10:58:06 +04:00
return False ;
2006-01-13 01:17:54 +03:00
}
1999-12-13 16:27:58 +03:00
2000-06-09 10:58:06 +04:00
fsp - > oplock_type = oplock_type ;
fsp - > sent_oplock_break = NO_BREAK_SENT ;
2006-01-13 01:17:54 +03:00
if ( oplock_type = = LEVEL_II_OPLOCK ) {
2000-06-09 10:58:06 +04:00
level_II_oplocks_open + + ;
2006-01-13 01:17:54 +03:00
} else {
2000-06-09 10:58:06 +04:00
exclusive_oplocks_open + + ;
2006-01-13 01:17:54 +03:00
}
1999-12-13 16:27:58 +03:00
2001-10-21 01:59:34 +04:00
DEBUG ( 5 , ( " set_file_oplock: granted oplock on file %s, dev = %x, inode = %.0f, file_id = %lu, \
tv_sec = % x , tv_usec = % x \ n " ,
fsp - > fsp_name , ( unsigned int ) fsp - > dev , ( double ) fsp - > inode , fsp - > file_id ,
2000-06-09 10:58:06 +04:00
( int ) fsp - > open_time . tv_sec , ( int ) fsp - > open_time . tv_usec ) ) ;
1998-08-17 17:11:34 +04:00
2000-06-09 10:58:06 +04:00
return True ;
1998-08-17 17:11:34 +04:00
}
1999-12-13 16:27:58 +03:00
/****************************************************************************
Attempt to release an oplock on a file . Decrements oplock count .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void release_file_oplock ( files_struct * fsp )
{
2005-09-30 21:13:37 +04:00
if ( ( fsp - > oplock_type ! = NO_OPLOCK ) & &
( fsp - > oplock_type ! = FAKE_LEVEL_II_OPLOCK ) & &
koplocks ) {
2001-10-21 01:59:34 +04:00
koplocks - > release_oplock ( fsp ) ;
2005-09-30 21:13:37 +04:00
}
2000-06-09 10:58:06 +04:00
2006-01-13 01:17:54 +03:00
if ( fsp - > oplock_type = = LEVEL_II_OPLOCK ) {
2000-06-09 10:58:06 +04:00
level_II_oplocks_open - - ;
2006-01-13 01:17:54 +03:00
} else if ( EXCLUSIVE_OPLOCK_TYPE ( fsp - > oplock_type ) ) {
2000-06-09 10:58:06 +04:00
exclusive_oplocks_open - - ;
2006-01-13 01:17:54 +03:00
}
2005-09-30 21:13:37 +04:00
SMB_ASSERT ( exclusive_oplocks_open > = 0 ) ;
SMB_ASSERT ( level_II_oplocks_open > = 0 ) ;
2000-06-09 10:58:06 +04:00
fsp - > oplock_type = NO_OPLOCK ;
fsp - > sent_oplock_break = NO_BREAK_SENT ;
flush_write_cache ( fsp , OPLOCK_RELEASE_FLUSH ) ;
1999-12-13 16:27:58 +03:00
}
/****************************************************************************
Attempt to downgrade an oplock on a file . Doesn ' t decrement oplock count .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void downgrade_file_oplock ( files_struct * fsp )
{
2006-01-13 01:17:54 +03:00
if ( koplocks ) {
2001-10-21 01:59:34 +04:00
koplocks - > release_oplock ( fsp ) ;
2006-01-13 01:17:54 +03:00
}
2000-06-10 17:38:07 +04:00
fsp - > oplock_type = LEVEL_II_OPLOCK ;
exclusive_oplocks_open - - ;
level_II_oplocks_open + + ;
fsp - > sent_oplock_break = NO_BREAK_SENT ;
1999-12-13 16:27:58 +03:00
}
/****************************************************************************
Remove a file oplock . Copes with level II and exclusive .
2000-11-16 03:59:18 +03:00
Locks then unlocks the share mode lock . Client can decide to go directly
to none even if a " break-to-level II " was sent .
1999-12-13 16:27:58 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-09-30 21:13:37 +04:00
BOOL remove_oplock ( files_struct * fsp )
1999-12-13 16:27:58 +03:00
{
2000-06-10 17:38:07 +04:00
SMB_DEV_T dev = fsp - > dev ;
SMB_INO_T inode = fsp - > inode ;
2005-09-30 21:13:37 +04:00
BOOL ret ;
struct share_mode_lock * lck ;
2000-06-10 17:38:07 +04:00
/* Remove the oplock flag from the sharemode. */
2005-12-13 01:07:36 +03:00
lck = get_share_mode_lock ( NULL , fsp - > dev , fsp - > inode , NULL , NULL ) ;
2005-09-30 21:13:37 +04:00
if ( lck = = NULL ) {
DEBUG ( 0 , ( " remove_oplock: failed to lock share entry for "
" file %s \n " , fsp - > fsp_name ) ) ;
2003-04-22 05:12:52 +04:00
return False ;
2000-06-10 17:38:07 +04:00
}
2005-09-30 21:13:37 +04:00
ret = remove_share_oplock ( lck , fsp ) ;
if ( ! ret ) {
DEBUG ( 0 , ( " remove_oplock: failed to remove share oplock for "
" file %s fnum %d, dev = %x, inode = %.0f \n " ,
fsp - > fsp_name , fsp - > fnum , ( unsigned int ) dev ,
( double ) inode ) ) ;
}
release_file_oplock ( fsp ) ;
talloc_free ( lck ) ;
return ret ;
}
1999-12-13 16:27:58 +03:00
2005-09-30 21:13:37 +04:00
/*
* Deal with a reply when a break - to - level II was sent .
*/
BOOL downgrade_oplock ( files_struct * fsp )
{
SMB_DEV_T dev = fsp - > dev ;
SMB_INO_T inode = fsp - > inode ;
BOOL ret ;
struct share_mode_lock * lck ;
1999-12-13 16:27:58 +03:00
2005-12-13 01:07:36 +03:00
lck = get_share_mode_lock ( NULL , fsp - > dev , fsp - > inode , NULL , NULL ) ;
2005-09-30 21:13:37 +04:00
if ( lck = = NULL ) {
DEBUG ( 0 , ( " downgrade_oplock: failed to lock share entry for "
" file %s \n " , fsp - > fsp_name ) ) ;
return False ;
2000-06-10 17:38:07 +04:00
}
2005-09-30 21:13:37 +04:00
ret = downgrade_share_oplock ( lck , fsp ) ;
if ( ! ret ) {
DEBUG ( 0 , ( " downgrade_oplock: failed to downgrade share oplock "
" for file %s fnum %d, dev = %x, inode = %.0f \n " ,
fsp - > fsp_name , fsp - > fnum , ( unsigned int ) dev ,
( double ) inode ) ) ;
}
2006-01-13 01:17:54 +03:00
2005-09-30 21:13:37 +04:00
downgrade_file_oplock ( fsp ) ;
talloc_free ( lck ) ;
2000-06-10 17:38:07 +04:00
return ret ;
1998-09-23 05:48:45 +04:00
}
/****************************************************************************
Setup the listening set of file descriptors for an oplock break
message either from the UDP socket or from the kernel . Returns the maximum
fd used .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
int setup_oplock_select_set ( fd_set * fds )
{
2005-09-30 21:13:37 +04:00
int maxfd = 0 ;
1998-09-23 05:48:45 +04:00
2000-06-10 17:38:07 +04:00
if ( koplocks & & koplocks - > notification_fd ! = - 1 ) {
FD_SET ( koplocks - > notification_fd , fds ) ;
maxfd = MAX ( maxfd , koplocks - > notification_fd ) ;
}
1998-09-23 05:48:45 +04:00
2000-06-10 17:38:07 +04:00
return maxfd ;
1998-09-23 05:48:45 +04:00
}
/****************************************************************************
2005-09-30 21:13:37 +04:00
Set up an oplock break message .
1998-08-17 17:11:34 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
static char * new_break_smb_message ( TALLOC_CTX * mem_ctx ,
2006-01-04 15:48:54 +03:00
files_struct * fsp , uint8 cmd )
1998-08-17 17:11:34 +04:00
{
2005-09-30 21:13:37 +04:00
char * result = TALLOC_ARRAY ( mem_ctx , char , smb_size + 8 * 2 + 0 ) ;
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
if ( result = = NULL ) {
DEBUG ( 0 , ( " talloc failed \n " ) ) ;
return NULL ;
2001-10-21 01:59:34 +04:00
}
2005-09-30 21:13:37 +04:00
memset ( result , ' \0 ' , smb_size ) ;
set_message ( result , 8 , 0 , True ) ;
SCVAL ( result , smb_com , SMBlockingX ) ;
SSVAL ( result , smb_tid , fsp - > conn - > cnum ) ;
SSVAL ( result , smb_pid , 0xFFFF ) ;
SSVAL ( result , smb_uid , 0 ) ;
SSVAL ( result , smb_mid , 0xFFFF ) ;
SCVAL ( result , smb_vwv0 , 0xFF ) ;
SSVAL ( result , smb_vwv2 , fsp - > fnum ) ;
SCVAL ( result , smb_vwv3 , LOCKING_ANDX_OPLOCK_RELEASE ) ;
SCVAL ( result , smb_vwv3 + 1 , cmd ) ;
return result ;
1999-12-13 16:27:58 +03:00
}
/****************************************************************************
Function to do the waiting before sending a local break .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2005-07-17 13:56:25 +04:00
static void wait_before_sending_break ( void )
1999-12-13 16:27:58 +03:00
{
2005-07-17 13:56:25 +04:00
struct timeval cur_tv ;
long wait_left = ( long ) lp_oplock_break_wait_time ( ) ;
1999-12-13 16:27:58 +03:00
2006-01-13 01:17:54 +03:00
if ( wait_left = = 0 ) {
2005-07-17 13:56:25 +04:00
return ;
2006-01-13 01:17:54 +03:00
}
2001-01-30 03:37:12 +03:00
2005-07-17 13:56:25 +04:00
GetTimeOfDay ( & cur_tv ) ;
1999-12-13 16:27:58 +03:00
2005-07-17 13:56:25 +04:00
wait_left - = ( ( cur_tv . tv_sec - smb_last_time . tv_sec ) * 1000 ) +
1999-12-13 16:27:58 +03:00
( ( cur_tv . tv_usec - smb_last_time . tv_usec ) / 1000 ) ;
2005-07-17 13:56:25 +04:00
if ( wait_left > 0 ) {
wait_left = MIN ( wait_left , 1000 ) ;
sys_usleep ( wait_left * 1000 ) ;
2001-10-21 01:59:34 +04:00
}
1999-12-13 16:27:58 +03:00
}
/****************************************************************************
Ensure that we have a valid oplock .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-10-21 01:59:34 +04:00
static files_struct * initial_break_processing ( SMB_DEV_T dev , SMB_INO_T inode , unsigned long file_id )
1998-08-17 17:11:34 +04:00
{
2001-10-21 01:59:34 +04:00
files_struct * fsp = NULL ;
if ( DEBUGLVL ( 3 ) ) {
2006-01-13 01:17:54 +03:00
dbgtext ( " initial_break_processing: called for dev = 0x%x, inode = %.0f file_id = %lu \n " ,
2001-10-21 01:59:34 +04:00
( unsigned int ) dev , ( double ) inode , file_id ) ;
dbgtext ( " Current oplocks_open (exclusive = %d, levelII = %d) \n " ,
exclusive_oplocks_open , level_II_oplocks_open ) ;
}
/*
* We need to search the file open table for the
* entry containing this dev and inode , and ensure
* we have an oplock on it .
*/
fsp = file_find_dif ( dev , inode , file_id ) ;
if ( fsp = = NULL ) {
/* The file could have been closed in the meantime - return success. */
if ( DEBUGLVL ( 3 ) ) {
dbgtext ( " initial_break_processing: cannot find open file with " ) ;
2006-01-13 01:17:54 +03:00
dbgtext ( " dev = 0x%x, inode = %.0f file_id = %lu " , ( unsigned int ) dev ,
2001-10-21 01:59:34 +04:00
( double ) inode , file_id ) ;
dbgtext ( " allowing break to succeed. \n " ) ;
}
return NULL ;
}
/* Ensure we have an oplock on the file */
/*
* There is a potential race condition in that an oplock could
* have been broken due to another udp request , and yet there are
* still oplock break messages being sent in the udp message
* queue for this file . So return true if we don ' t have an oplock ,
* as we may have just freed it .
*/
if ( fsp - > oplock_type = = NO_OPLOCK ) {
if ( DEBUGLVL ( 3 ) ) {
dbgtext ( " initial_break_processing: file %s " , fsp - > fsp_name ) ;
dbgtext ( " (dev = %x, inode = %.0f, file_id = %lu) has no oplock. \n " ,
( unsigned int ) dev , ( double ) inode , fsp - > file_id ) ;
dbgtext ( " Allowing break to succeed regardless. \n " ) ;
}
return NULL ;
}
return fsp ;
1999-12-13 16:27:58 +03:00
}
2005-09-30 21:13:37 +04:00
static void oplock_timeout_handler ( struct timed_event * te ,
const struct timeval * now ,
void * private_data )
1999-12-13 16:27:58 +03:00
{
2005-09-30 21:13:37 +04:00
files_struct * fsp = private_data ;
2004-06-12 03:54:52 +04:00
2005-09-30 21:13:37 +04:00
DEBUG ( 0 , ( " Oplock break failed -- replying anyway \n " ) ) ;
global_client_failed_oplock_break = True ;
remove_oplock ( fsp ) ;
reply_to_oplock_break_requests ( fsp ) ;
1999-12-13 16:27:58 +03:00
}
2005-09-30 21:13:37 +04:00
static void process_oplock_break_message ( int msg_type , struct process_id src ,
void * buf , size_t len )
1999-12-13 16:27:58 +03:00
{
2006-01-13 01:17:54 +03:00
struct share_mode_entry msg ;
2005-09-30 21:13:37 +04:00
files_struct * fsp ;
char * break_msg ;
BOOL break_to_level2 = False ;
2003-07-17 04:58:14 +04:00
BOOL sign_state ;
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
if ( buf = = NULL ) {
DEBUG ( 0 , ( " Got NULL buffer \n " ) ) ;
return ;
2005-01-26 03:13:15 +03:00
}
2001-10-21 01:59:34 +04:00
2006-01-13 01:17:54 +03:00
if ( len ! = MSG_SMB_SHARE_MODE_ENTRY_SIZE ) {
2005-09-30 21:13:37 +04:00
DEBUG ( 0 , ( " Got invalid msg len %d \n " , ( int ) len ) ) ;
return ;
2001-10-21 01:59:34 +04:00
}
2006-01-13 01:17:54 +03:00
/* De-linearize incoming message. */
message_to_share_mode_entry ( & msg , buf ) ;
DEBUG ( 10 , ( " Got oplock break message from pid %d: 0x%x/%.0f/%d \n " ,
( int ) procid_to_pid ( & src ) , ( unsigned int ) msg . dev , ( double ) msg . inode ,
( int ) msg . share_file_id ) ) ;
2001-10-21 01:59:34 +04:00
2006-01-13 01:17:54 +03:00
fsp = initial_break_processing ( msg . dev , msg . inode ,
msg . share_file_id ) ;
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
if ( fsp = = NULL ) {
/* We hit race here. Break messages are sent, and before we
* get to process this message , we have closed the file . Reply
* with ' ok , oplock broken ' */
DEBUG ( 3 , ( " Did not find fsp \n " ) ) ;
2005-12-25 00:06:41 +03:00
become_root ( ) ;
2006-01-13 01:17:54 +03:00
/* We just send the same message back. */
2005-09-30 21:13:37 +04:00
message_send_pid ( src , MSG_SMB_BREAK_RESPONSE ,
2006-01-13 01:17:54 +03:00
buf , MSG_SMB_SHARE_MODE_ENTRY_SIZE , True ) ;
2005-12-25 00:06:41 +03:00
unbecome_root ( ) ;
2005-09-30 21:13:37 +04:00
return ;
2001-10-21 01:59:34 +04:00
}
2005-09-30 21:13:37 +04:00
if ( fsp - > sent_oplock_break ! = NO_BREAK_SENT ) {
/* Remember we have to inform the requesting PID when the
* client replies */
2006-01-13 01:17:54 +03:00
msg . pid = src ;
ADD_TO_ARRAY ( NULL , struct share_mode_entry , msg ,
2005-09-30 21:13:37 +04:00
& fsp - > pending_break_messages ,
& fsp - > num_pending_break_messages ) ;
return ;
2001-10-21 01:59:34 +04:00
}
2006-01-13 01:17:54 +03:00
if ( EXCLUSIVE_OPLOCK_TYPE ( msg . op_type ) & &
2005-09-30 21:13:37 +04:00
! EXCLUSIVE_OPLOCK_TYPE ( fsp - > oplock_type ) ) {
2006-01-13 01:17:54 +03:00
DEBUG ( 3 , ( " Already downgraded oplock on 0x%x/%.0f: %s \n " ,
( unsigned int ) fsp - > dev , ( double ) fsp - > inode ,
2005-09-30 21:13:37 +04:00
fsp - > fsp_name ) ) ;
2005-12-25 00:06:41 +03:00
become_root ( ) ;
2006-01-13 01:17:54 +03:00
/* We just send the same message back. */
2005-09-30 21:13:37 +04:00
message_send_pid ( src , MSG_SMB_BREAK_RESPONSE ,
2006-01-13 01:17:54 +03:00
buf , MSG_SMB_SHARE_MODE_ENTRY_SIZE , True ) ;
2005-12-25 00:06:41 +03:00
unbecome_root ( ) ;
2005-09-30 21:13:37 +04:00
return ;
2005-07-17 13:56:25 +04:00
}
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
if ( ( msg_type = = MSG_SMB_BREAK_REQUEST ) & &
( global_client_caps & CAP_LEVEL_II_OPLOCKS ) & &
! koplocks & & /* NOTE: we force levelII off for kernel oplocks -
* this will change when it is supported */
lp_level2_oplocks ( SNUM ( fsp - > conn ) ) ) {
break_to_level2 = True ;
}
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
break_msg = new_break_smb_message ( NULL , fsp , break_to_level2 ?
OPLOCKLEVEL_II : OPLOCKLEVEL_NONE ) ;
if ( break_msg = = NULL ) {
exit_server ( " Could not talloc break_msg \n " ) ;
2001-10-21 01:59:34 +04:00
}
2005-09-30 21:13:37 +04:00
/* Need to wait before sending a break message to a file of our own */
if ( procid_to_pid ( & src ) = = sys_getpid ( ) ) {
wait_before_sending_break ( ) ;
}
2001-10-21 01:59:34 +04:00
2003-07-17 04:58:14 +04:00
/* Save the server smb signing state. */
sign_state = srv_oplock_set_signing ( False ) ;
2005-09-30 21:13:37 +04:00
show_msg ( break_msg ) ;
if ( ! send_smb ( smbd_server_fd ( ) , break_msg ) ) {
2001-11-05 03:02:38 +03:00
exit_server ( " oplock_break: send_smb failed. " ) ;
2003-07-17 01:06:21 +04:00
}
2003-07-17 04:58:14 +04:00
/* Restore the sign state to what it was. */
srv_oplock_set_signing ( sign_state ) ;
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
talloc_free ( break_msg ) ;
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
if ( msg_type = = MSG_SMB_BREAK_REQUEST ) {
fsp - > sent_oplock_break = break_to_level2 ?
LEVEL_II_BREAK_SENT : BREAK_TO_NONE_SENT ;
} else {
/* Async level2 request, don't send a reply */
fsp - > sent_oplock_break = ASYNC_LEVEL_II_BREAK_SENT ;
}
2006-01-13 01:17:54 +03:00
msg . pid = src ;
ADD_TO_ARRAY ( NULL , struct share_mode_entry , msg ,
2005-09-30 21:13:37 +04:00
& fsp - > pending_break_messages ,
& fsp - > num_pending_break_messages ) ;
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
if ( fsp - > oplock_timeout ! = NULL ) {
DEBUG ( 0 , ( " Logic problem -- have an oplock event hanging "
" around \n " ) ) ;
}
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
fsp - > oplock_timeout =
add_timed_event ( NULL ,
timeval_current_ofs ( OPLOCK_BREAK_TIMEOUT , 0 ) ,
" oplock_timeout_handler " ,
oplock_timeout_handler , fsp ) ;
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
if ( fsp - > oplock_timeout = = NULL ) {
DEBUG ( 0 , ( " Could not add oplock timeout handler \n " ) ) ;
}
}
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
static void process_kernel_oplock_break ( int msg_type , struct process_id src ,
void * buf , size_t len )
{
2006-01-13 01:17:54 +03:00
SMB_DEV_T dev ;
SMB_INO_T inode ;
unsigned long file_id ;
2005-09-30 21:13:37 +04:00
files_struct * fsp ;
char * break_msg ;
BOOL sign_state ;
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
if ( buf = = NULL ) {
DEBUG ( 0 , ( " Got NULL buffer \n " ) ) ;
return ;
2001-10-21 01:59:34 +04:00
}
2006-01-13 01:17:54 +03:00
if ( len ! = MSG_SMB_KERNEL_BREAK_SIZE ) {
2005-09-30 21:13:37 +04:00
DEBUG ( 0 , ( " Got invalid msg len %d \n " , ( int ) len ) ) ;
return ;
2001-10-21 01:59:34 +04:00
}
2006-01-13 01:17:54 +03:00
/* Pull the data from the message. */
dev = DEV_T_VAL ( buf , 0 ) ;
inode = INO_T_VAL ( buf , 8 ) ;
file_id = ( unsigned long ) IVAL ( buf , 16 ) ;
2001-10-21 01:59:34 +04:00
2006-01-13 01:17:54 +03:00
DEBUG ( 10 , ( " Got kernel oplock break message from pid %d: 0x%x/%.0f/%u \n " ,
( int ) procid_to_pid ( & src ) , ( unsigned int ) dev , ( double ) inode ,
( unsigned int ) file_id ) ) ;
fsp = initial_break_processing ( dev , inode , file_id ) ;
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
if ( fsp = = NULL ) {
DEBUG ( 3 , ( " Got a kernel oplock break message for a file "
" I don't know about \n " ) ) ;
return ;
2001-10-21 01:59:34 +04:00
}
2005-09-30 21:13:37 +04:00
if ( fsp - > sent_oplock_break ! = NO_BREAK_SENT ) {
/* This is ok, kernel oplocks come in completely async */
DEBUG ( 3 , ( " Got a kernel oplock request while waiting for a "
" break reply \n " ) ) ;
return ;
2001-10-21 01:59:34 +04:00
}
2005-09-30 21:13:37 +04:00
break_msg = new_break_smb_message ( NULL , fsp , OPLOCKLEVEL_NONE ) ;
if ( break_msg = = NULL ) {
exit_server ( " Could not talloc break_msg \n " ) ;
2001-10-21 01:59:34 +04:00
}
2005-09-30 21:13:37 +04:00
/* Save the server smb signing state. */
sign_state = srv_oplock_set_signing ( False ) ;
2005-04-02 03:11:28 +04:00
2005-09-30 21:13:37 +04:00
show_msg ( break_msg ) ;
if ( ! send_smb ( smbd_server_fd ( ) , break_msg ) ) {
exit_server ( " oplock_break: send_smb failed. " ) ;
2001-10-21 01:59:34 +04:00
}
2005-09-30 21:13:37 +04:00
/* Restore the sign state to what it was. */
srv_oplock_set_signing ( sign_state ) ;
1998-08-17 17:11:34 +04:00
2005-09-30 21:13:37 +04:00
talloc_free ( break_msg ) ;
1998-08-17 17:11:34 +04:00
2005-09-30 21:13:37 +04:00
fsp - > sent_oplock_break = BREAK_TO_NONE_SENT ;
}
1998-08-17 17:11:34 +04:00
2005-09-30 21:13:37 +04:00
void reply_to_oplock_break_requests ( files_struct * fsp )
{
int i ;
2001-08-21 05:25:45 +04:00
2005-12-25 00:06:41 +03:00
become_root ( ) ;
2005-09-30 21:13:37 +04:00
for ( i = 0 ; i < fsp - > num_pending_break_messages ; i + + ) {
2006-01-13 01:17:54 +03:00
struct share_mode_entry * e = & fsp - > pending_break_messages [ i ] ;
char msg [ MSG_SMB_SHARE_MODE_ENTRY_SIZE ] ;
share_mode_entry_to_message ( msg , e ) ;
message_send_pid ( e - > pid , MSG_SMB_BREAK_RESPONSE ,
msg , MSG_SMB_SHARE_MODE_ENTRY_SIZE , True ) ;
2001-10-21 01:59:34 +04:00
}
2005-12-25 00:06:41 +03:00
unbecome_root ( ) ;
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
SAFE_FREE ( fsp - > pending_break_messages ) ;
fsp - > num_pending_break_messages = 0 ;
if ( fsp - > oplock_timeout ! = NULL ) {
talloc_free ( fsp - > oplock_timeout ) ;
fsp - > oplock_timeout = NULL ;
2001-10-21 01:59:34 +04:00
}
2005-09-30 21:13:37 +04:00
return ;
}
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
static void process_oplock_break_response ( int msg_type , struct process_id src ,
void * buf , size_t len )
{
2006-01-13 01:17:54 +03:00
struct share_mode_entry msg ;
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
if ( buf = = NULL ) {
DEBUG ( 0 , ( " Got NULL buffer \n " ) ) ;
return ;
2001-10-21 01:59:34 +04:00
}
2006-01-13 01:17:54 +03:00
if ( len ! = MSG_SMB_SHARE_MODE_ENTRY_SIZE ) {
DEBUG ( 0 , ( " Got invalid msg len %u \n " , ( unsigned int ) len ) ) ;
2005-09-30 21:13:37 +04:00
return ;
2001-10-21 01:59:34 +04:00
}
2006-01-13 01:17:54 +03:00
/* De-linearize incoming message. */
message_to_share_mode_entry ( & msg , buf ) ;
DEBUG ( 10 , ( " Got oplock break response from pid %d: 0x%x/%.0f/%u mid %u \n " ,
( int ) procid_to_pid ( & src ) , ( unsigned int ) msg . dev , ( double ) msg . inode ,
( unsigned int ) msg . share_file_id , ( unsigned int ) msg . op_mid ) ) ;
2001-10-21 01:59:34 +04:00
2005-09-30 21:13:37 +04:00
/* Here's the hack from open.c, store the mid in the 'port' field */
2006-01-13 01:17:54 +03:00
schedule_deferred_open_smb_message ( msg . op_mid ) ;
1998-08-17 17:11:34 +04:00
}
2005-09-30 21:13:37 +04:00
static void process_open_retry_message ( int msg_type , struct process_id src ,
void * buf , size_t len )
1998-08-17 17:11:34 +04:00
{
2006-01-13 01:17:54 +03:00
struct share_mode_entry msg ;
2005-09-30 21:13:37 +04:00
if ( buf = = NULL ) {
DEBUG ( 0 , ( " Got NULL buffer \n " ) ) ;
return ;
2001-10-21 01:59:34 +04:00
}
1998-08-17 17:11:34 +04:00
2006-01-13 01:17:54 +03:00
if ( len ! = MSG_SMB_SHARE_MODE_ENTRY_SIZE ) {
2005-09-30 21:13:37 +04:00
DEBUG ( 0 , ( " Got invalid msg len %d \n " , ( int ) len ) ) ;
return ;
2005-01-26 23:01:21 +03:00
}
2006-01-13 01:17:54 +03:00
/* De-linearize incoming message. */
message_to_share_mode_entry ( & msg , buf ) ;
2005-01-26 23:01:21 +03:00
2006-01-13 01:17:54 +03:00
DEBUG ( 10 , ( " Got open retry msg from pid %d: 0x%x/%.0f mid %u \n " ,
( int ) procid_to_pid ( & src ) , ( unsigned int ) msg . dev , ( double ) msg . inode ,
( unsigned int ) msg . op_mid ) ) ;
schedule_deferred_open_smb_message ( msg . op_mid ) ;
2005-01-26 23:01:21 +03:00
}
2000-11-16 03:59:18 +03:00
/****************************************************************************
This function is called on any file modification or lock request . If a file
2005-09-30 21:13:37 +04:00
is level 2 oplocked then it must tell all other level 2 holders to break to
none .
2000-11-16 03:59:18 +03:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void release_level_2_oplocks_on_change ( files_struct * fsp )
{
int i ;
2005-09-30 21:13:37 +04:00
struct share_mode_lock * lck ;
2000-11-16 03:59:18 +03:00
/*
* If this file is level II oplocked then we need
* to grab the shared memory lock and inform all
* other files with a level II lock that they need
* to flush their read caches . We keep the lock over
* the shared memory area whilst doing this .
*/
if ( ! LEVEL_II_OPLOCK_TYPE ( fsp - > oplock_type ) )
return ;
2005-12-13 01:07:36 +03:00
lck = get_share_mode_lock ( NULL , fsp - > dev , fsp - > inode , NULL , NULL ) ;
2005-09-30 21:13:37 +04:00
if ( lck = = NULL ) {
DEBUG ( 0 , ( " release_level_2_oplocks_on_change: failed to lock "
" share mode entry for file %s. \n " , fsp - > fsp_name ) ) ;
2000-11-16 03:59:18 +03:00
}
2001-08-22 04:29:40 +04:00
DEBUG ( 10 , ( " release_level_2_oplocks_on_change: num_share_modes = %d \n " ,
2005-09-30 21:13:37 +04:00
lck - > num_share_modes ) ) ;
if ( fsp - > oplock_type = = FAKE_LEVEL_II_OPLOCK ) {
/* See if someone else has already downgraded us, then we
don ' t have to do anything */
for ( i = 0 ; i < lck - > num_share_modes ; i + + ) {
struct share_mode_entry * e = & lck - > share_modes [ i ] ;
2006-01-27 22:54:39 +03:00
if ( ! is_valid_share_mode_entry ( e ) ) {
continue ;
}
2005-09-30 21:13:37 +04:00
if ( ( e - > op_type = = NO_OPLOCK ) & &
( e - > share_file_id = = fsp - > file_id ) & &
( e - > dev = = fsp - > dev ) & &
( e - > inode = = fsp - > inode ) & &
( procid_is_me ( & e - > pid ) ) ) {
/* We're done */
fsp - > oplock_type = NO_OPLOCK ;
talloc_free ( lck ) ;
return ;
}
}
}
2001-08-22 04:29:40 +04:00
2005-09-30 21:13:37 +04:00
for ( i = 0 ; i < lck - > num_share_modes ; i + + ) {
struct share_mode_entry * share_entry = & lck - > share_modes [ i ] ;
2006-01-13 01:17:54 +03:00
char msg [ MSG_SMB_SHARE_MODE_ENTRY_SIZE ] ;
2000-11-16 03:59:18 +03:00
2006-01-27 22:54:39 +03:00
if ( ! is_valid_share_mode_entry ( share_entry ) ) {
continue ;
}
2000-11-16 03:59:18 +03:00
/*
2005-09-30 21:13:37 +04:00
* As there could have been multiple writes waiting at the
* lock_share_entry gate we may not be the first to
* enter . Hence the state of the op_types in the share mode
* entries may be partly NO_OPLOCK and partly LEVEL_II
* oplock . It will do no harm to re - send break messages to
* those smbd ' s that are still waiting their turn to remove
* their LEVEL_II state , and also no harm to ignore existing
* NO_OPLOCK states . JRA .
2000-11-16 03:59:18 +03:00
*/
2005-09-30 21:13:37 +04:00
DEBUG ( 10 , ( " release_level_2_oplocks_on_change: "
" share_entry[%i]->op_type == %d \n " ,
i , share_entry - > op_type ) ) ;
2001-08-22 04:29:40 +04:00
2005-09-30 21:13:37 +04:00
if ( ( share_entry - > op_type = = NO_OPLOCK ) | |
( share_entry - > op_type = = FAKE_LEVEL_II_OPLOCK ) ) {
2000-11-16 03:59:18 +03:00
continue ;
2005-09-30 21:13:37 +04:00
}
2000-11-16 03:59:18 +03:00
/* Paranoia .... */
if ( EXCLUSIVE_OPLOCK_TYPE ( share_entry - > op_type ) ) {
2005-09-30 21:13:37 +04:00
DEBUG ( 0 , ( " release_level_2_oplocks_on_change: PANIC. "
" share mode entry %d is an exlusive "
" oplock ! \n " , i ) ) ;
talloc_free ( lck ) ;
2000-11-16 03:59:18 +03:00
abort ( ) ;
}
2006-01-13 01:17:54 +03:00
share_mode_entry_to_message ( msg , share_entry ) ;
2005-12-25 00:06:41 +03:00
become_root ( ) ;
2005-09-30 21:13:37 +04:00
message_send_pid ( share_entry - > pid , MSG_SMB_ASYNC_LEVEL2_BREAK ,
2006-01-13 01:17:54 +03:00
msg , MSG_SMB_SHARE_MODE_ENTRY_SIZE , True ) ;
2005-12-25 00:06:41 +03:00
unbecome_root ( ) ;
2004-06-08 20:14:31 +04:00
}
2005-09-30 21:13:37 +04:00
remove_all_share_oplocks ( lck , fsp ) ;
talloc_free ( lck ) ;
2004-06-08 20:14:31 +04:00
}
2006-01-13 01:17:54 +03:00
/****************************************************************************
Linearize a share mode entry struct to an internal oplock break message .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void share_mode_entry_to_message ( char * msg , struct share_mode_entry * e )
{
SIVAL ( msg , 0 , ( uint32 ) e - > pid . pid ) ;
SSVAL ( msg , 4 , e - > op_mid ) ;
SSVAL ( msg , 6 , e - > op_type ) ;
SIVAL ( msg , 8 , e - > access_mask ) ;
SIVAL ( msg , 12 , e - > share_access ) ;
SIVAL ( msg , 16 , e - > private_options ) ;
SIVAL ( msg , 20 , ( uint32 ) e - > time . tv_sec ) ;
SIVAL ( msg , 24 , ( uint32 ) e - > time . tv_usec ) ;
SDEV_T_VAL ( msg , 28 , e - > dev ) ;
SINO_T_VAL ( msg , 36 , e - > inode ) ;
SIVAL ( msg , 44 , e - > share_file_id ) ;
}
/****************************************************************************
De - linearize an internal oplock break message to a share mode entry struct .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void message_to_share_mode_entry ( struct share_mode_entry * e , char * msg )
{
e - > pid . pid = ( pid_t ) IVAL ( msg , 0 ) ;
e - > op_mid = SVAL ( msg , 4 ) ;
e - > op_type = SVAL ( msg , 6 ) ;
e - > access_mask = IVAL ( msg , 8 ) ;
e - > share_access = IVAL ( msg , 12 ) ;
e - > private_options = IVAL ( msg , 16 ) ;
e - > time . tv_sec = ( time_t ) IVAL ( msg , 20 ) ;
e - > time . tv_usec = ( int ) IVAL ( msg , 24 ) ;
e - > dev = DEV_T_VAL ( msg , 28 ) ;
e - > inode = INO_T_VAL ( msg , 36 ) ;
e - > share_file_id = ( unsigned long ) IVAL ( msg , 44 ) ;
}
2004-06-08 20:14:31 +04:00
/****************************************************************************
Setup oplocks for this process .
1998-09-23 05:48:45 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-10-21 01:59:34 +04:00
2000-06-10 17:38:07 +04:00
BOOL init_oplocks ( void )
1998-09-18 21:50:18 +04:00
{
2000-06-10 17:38:07 +04:00
DEBUG ( 3 , ( " open_oplock_ipc: opening loopback UDP socket. \n " ) ) ;
2005-09-30 21:13:37 +04:00
message_register ( MSG_SMB_BREAK_REQUEST ,
process_oplock_break_message ) ;
message_register ( MSG_SMB_ASYNC_LEVEL2_BREAK ,
process_oplock_break_message ) ;
message_register ( MSG_SMB_BREAK_RESPONSE ,
process_oplock_break_response ) ;
message_register ( MSG_SMB_KERNEL_BREAK ,
process_kernel_oplock_break ) ;
message_register ( MSG_SMB_OPEN_RETRY ,
process_open_retry_message ) ;
2000-06-10 17:38:07 +04:00
if ( lp_kernel_oplocks ( ) ) {
# if HAVE_KERNEL_OPLOCKS_IRIX
koplocks = irix_init_kernel_oplocks ( ) ;
2000-06-11 09:57:58 +04:00
# elif HAVE_KERNEL_OPLOCKS_LINUX
koplocks = linux_init_kernel_oplocks ( ) ;
2000-06-10 17:38:07 +04:00
# endif
}
return True ;
1998-09-18 21:50:18 +04:00
}