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
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
/* Oplock ipc UDP socket. */
1998-09-23 05:48:45 +04:00
static int oplock_sock = - 1 ;
uint16 global_oplock_port = 0 ;
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
BOOL global_oplock_break = False ;
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 ;
2001-10-21 01:59:34 +04:00
static BOOL oplock_break ( SMB_DEV_T dev , SMB_INO_T inode , unsigned long file_id , BOOL local ) ;
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 )
{
if ( koplocks & & koplocks - > msg_waiting ( fds ) )
return True ;
if ( FD_ISSET ( oplock_sock , fds ) )
return True ;
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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2002-07-15 14:35:28 +04:00
BOOL receive_local_message ( char * buffer , int buffer_len , int timeout )
1998-09-23 05:48:45 +04:00
{
2001-10-21 01:59:34 +04:00
struct sockaddr_in from ;
2002-07-15 14:35:28 +04:00
socklen_t fromlen = sizeof ( from ) ;
2001-10-21 01:59:34 +04:00
int32 msg_len = 0 ;
2002-07-15 14:35:28 +04:00
fd_set fds ;
int selrtn = - 1 ;
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 .
*/
if ( koplocks & & koplocks - > msg_waiting ( & fds ) ) {
return koplocks - > receive_message ( & fds , buffer , buffer_len ) ;
}
while ( timeout > 0 & & selrtn = = - 1 ) {
2001-10-21 01:59:34 +04:00
struct timeval to ;
int maxfd = oplock_sock ;
2002-07-15 14:35:28 +04:00
time_t starttime = time ( NULL ) ;
2001-10-21 01:59:34 +04:00
2002-07-15 14:35:28 +04:00
FD_ZERO ( & fds ) ;
maxfd = setup_oplock_select_set ( & fds ) ;
2001-10-21 01:59:34 +04:00
to . tv_sec = timeout / 1000 ;
to . tv_usec = ( timeout % 1000 ) * 1000 ;
2002-07-15 14:35:28 +04:00
DEBUG ( 5 , ( " receive_local_message: doing select with timeout of %d ms \n " , timeout ) ) ;
selrtn = sys_select ( maxfd + 1 , & fds , NULL , NULL , & to ) ;
2001-10-21 01:59:34 +04:00
if ( selrtn = = - 1 & & errno = = EINTR ) {
2002-07-15 14:35:28 +04:00
2001-10-21 01:59:34 +04:00
/* could be a kernel oplock interrupt */
2002-07-15 14:35:28 +04:00
if ( koplocks & & koplocks - > msg_waiting ( & fds ) ) {
return koplocks - > receive_message ( & fds , buffer , buffer_len ) ;
2001-10-21 01:59:34 +04:00
}
2002-07-15 14:35:28 +04:00
/*
* Linux 2.0 . x seems to have a bug in that
* it can return - 1 , EINTR with a timeout of zero .
* Make sure we bail out here with a read timeout
* if we got EINTR on a timeout of 1 or less .
*/
if ( timeout < = 1 ) {
smb_read_error = READ_TIMEOUT ;
return False ;
}
/* Not a kernel interrupt - could be a SIGUSR1 message. We must restart. */
/* We need to decrement the timeout here. */
timeout - = ( ( time ( NULL ) - starttime ) * 1000 ) ;
if ( timeout < 0 )
timeout = 1 ;
DEBUG ( 5 , ( " receive_local_message: EINTR : new timeout %d ms \n " , timeout ) ) ;
continue ;
2001-10-21 01:59:34 +04:00
}
/* Check if error */
if ( selrtn = = - 1 ) {
/* something is wrong. Maybe the socket is dead? */
smb_read_error = READ_ERROR ;
return False ;
}
/* Did we timeout ? */
if ( selrtn = = 0 ) {
smb_read_error = READ_TIMEOUT ;
return False ;
}
}
2002-07-15 14:35:28 +04:00
if ( koplocks & & koplocks - > msg_waiting ( & fds ) ) {
return koplocks - > receive_message ( & fds , buffer , buffer_len ) ;
2001-10-21 01:59:34 +04:00
}
2002-07-15 14:35:28 +04:00
if ( ! FD_ISSET ( oplock_sock , & fds ) )
2001-10-21 01:59:34 +04:00
return False ;
/*
* From here down we deal with the smbd < - - > smbd
* oplock break protocol only .
*/
/*
* Read a loopback udp message .
*/
2002-07-15 14:35:28 +04:00
msg_len = sys_recvfrom ( oplock_sock , & buffer [ OPBRK_CMD_HEADER_LEN ] ,
2001-10-21 01:59:34 +04:00
buffer_len - OPBRK_CMD_HEADER_LEN , 0 , ( struct sockaddr * ) & from , & fromlen ) ;
if ( msg_len < 0 ) {
DEBUG ( 0 , ( " receive_local_message. Error in recvfrom. (%s). \n " , strerror ( errno ) ) ) ;
return False ;
}
/* Validate message length. */
if ( msg_len > ( buffer_len - OPBRK_CMD_HEADER_LEN ) ) {
DEBUG ( 0 , ( " receive_local_message: invalid msg_len (%d) max can be %d \n " , msg_len ,
buffer_len - OPBRK_CMD_HEADER_LEN ) ) ;
return False ;
}
/* Validate message from address (must be localhost). */
if ( from . sin_addr . s_addr ! = htonl ( INADDR_LOOPBACK ) ) {
DEBUG ( 0 , ( " receive_local_message: invalid 'from' address \
( was % lx should be 127.0 .0 .1 ) \ n " , (long)from.sin_addr.s_addr));
return False ;
}
/* Setup the message header */
SIVAL ( buffer , OPBRK_CMD_LEN_OFFSET , msg_len ) ;
SSVAL ( buffer , OPBRK_CMD_PORT_OFFSET , ntohs ( from . sin_port ) ) ;
return True ;
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 )
{
2000-06-10 17:38:07 +04:00
if ( koplocks & & ! koplocks - > set_oplock ( fsp , oplock_type ) )
2000-06-09 10:58:06 +04:00
return False ;
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 ;
if ( oplock_type = = LEVEL_II_OPLOCK )
level_II_oplocks_open + + ;
else
exclusive_oplocks_open + + ;
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 )
{
2003-04-22 05:12:52 +04:00
if ( ( fsp - > oplock_type ! = NO_OPLOCK ) & & koplocks )
2001-10-21 01:59:34 +04:00
koplocks - > release_oplock ( fsp ) ;
2000-06-09 10:58:06 +04:00
if ( fsp - > oplock_type = = LEVEL_II_OPLOCK )
level_II_oplocks_open - - ;
2003-04-22 05:12:52 +04:00
else if ( fsp - > oplock_type )
2000-06-09 10:58:06 +04:00
exclusive_oplocks_open - - ;
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 )
{
2001-10-21 01:59:34 +04:00
if ( koplocks )
koplocks - > release_oplock ( fsp ) ;
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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2000-11-16 03:59:18 +03:00
BOOL remove_oplock ( files_struct * fsp , BOOL break_to_none )
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 ;
BOOL ret = True ;
/* Remove the oplock flag from the sharemode. */
if ( lock_share_entry_fsp ( fsp ) = = False ) {
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
}
1999-12-13 16:27:58 +03:00
2000-11-16 03:59:18 +03:00
if ( fsp - > sent_oplock_break = = EXCLUSIVE_BREAK_SENT | | break_to_none ) {
2000-06-10 17:38:07 +04:00
/*
* Deal with a reply when a break - to - none was sent .
*/
1999-12-13 16:27:58 +03:00
2000-06-10 17:38:07 +04:00
if ( remove_share_oplock ( fsp ) = = False ) {
DEBUG ( 0 , ( " remove_oplock: failed to remove share oplock for file %s fnum %d, \
1999-12-13 16:27:58 +03:00
dev = % x , inode = % .0f \ n " , fsp->fsp_name, fsp->fnum, (unsigned int)dev, (double)inode));
2000-06-10 17:38:07 +04:00
ret = False ;
}
1999-12-13 16:27:58 +03:00
2000-06-10 17:38:07 +04:00
release_file_oplock ( fsp ) ;
} else {
/*
* Deal with a reply when a break - to - level II was sent .
*/
if ( downgrade_share_oplock ( fsp ) = = False ) {
DEBUG ( 0 , ( " remove_oplock: failed to downgrade share oplock for file %s fnum %d, \
1999-12-13 16:27:58 +03:00
dev = % x , inode = % .0f \ n " , fsp->fsp_name, fsp->fnum, (unsigned int)dev, (double)inode));
2000-06-10 17:38:07 +04:00
ret = False ;
}
downgrade_file_oplock ( fsp ) ;
}
1999-12-13 16:27:58 +03:00
2000-06-10 17:38:07 +04:00
unlock_share_entry_fsp ( fsp ) ;
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 )
{
2000-06-10 17:38:07 +04:00
int maxfd = oplock_sock ;
1999-12-13 16:27:58 +03:00
2000-06-10 17:38:07 +04:00
if ( oplock_sock = = - 1 )
return 0 ;
1999-12-13 16:27:58 +03:00
2000-06-10 17:38:07 +04:00
FD_SET ( oplock_sock , fds ) ;
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
}
/****************************************************************************
Process an oplock break message - whether it came from the UDP socket
or from the kernel .
1998-08-17 17:11:34 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-10-21 01:59:34 +04:00
1998-09-23 05:48:45 +04:00
BOOL process_local_message ( char * buffer , int buf_size )
1998-08-17 17:11:34 +04:00
{
2001-10-21 01:59:34 +04:00
int32 msg_len ;
uint16 from_port ;
char * msg_start ;
pid_t remotepid ;
SMB_DEV_T dev ;
SMB_INO_T inode ;
unsigned long file_id ;
uint16 break_cmd_type ;
msg_len = IVAL ( buffer , OPBRK_CMD_LEN_OFFSET ) ;
from_port = SVAL ( buffer , OPBRK_CMD_PORT_OFFSET ) ;
msg_start = & buffer [ OPBRK_CMD_HEADER_LEN ] ;
DEBUG ( 5 , ( " process_local_message: Got a message of length %d from port (%d) \n " ,
msg_len , from_port ) ) ;
/*
* Pull the info out of the requesting packet .
*/
break_cmd_type = SVAL ( msg_start , OPBRK_MESSAGE_CMD_OFFSET ) ;
switch ( break_cmd_type ) {
case KERNEL_OPLOCK_BREAK_CMD :
if ( ! koplocks ) {
DEBUG ( 0 , ( " unexpected kernel oplock break! \n " ) ) ;
break ;
}
if ( ! koplocks - > parse_message ( msg_start , msg_len , & inode , & dev , & file_id ) ) {
DEBUG ( 0 , ( " kernel oplock break parse failure! \n " ) ) ;
}
break ;
case OPLOCK_BREAK_CMD :
case LEVEL_II_OPLOCK_BREAK_CMD :
2003-01-30 04:41:46 +03:00
case ASYNC_LEVEL_II_OPLOCK_BREAK_CMD :
2001-10-21 01:59:34 +04:00
/* Ensure that the msg length is correct. */
if ( msg_len ! = OPLOCK_BREAK_MSG_LEN ) {
DEBUG ( 0 , ( " process_local_message: incorrect length for OPLOCK_BREAK_CMD (was %d, should be %d). \n " ,
( int ) msg_len , ( int ) OPLOCK_BREAK_MSG_LEN ) ) ;
return False ;
}
memcpy ( ( char * ) & remotepid , msg_start + OPLOCK_BREAK_PID_OFFSET , sizeof ( remotepid ) ) ;
memcpy ( ( char * ) & inode , msg_start + OPLOCK_BREAK_INODE_OFFSET , sizeof ( inode ) ) ;
memcpy ( ( char * ) & dev , msg_start + OPLOCK_BREAK_DEV_OFFSET , sizeof ( dev ) ) ;
memcpy ( ( char * ) & file_id , msg_start + OPLOCK_BREAK_FILEID_OFFSET , sizeof ( file_id ) ) ;
DEBUG ( 5 , ( " process_local_message: (%s) oplock break request from \
pid % d , port % d , dev = % x , inode = % .0f , file_id = % lu \ n " ,
( break_cmd_type = = OPLOCK_BREAK_CMD ) ? " exclusive " : " level II " ,
( int ) remotepid , from_port , ( unsigned int ) dev , ( double ) inode , file_id ) ) ;
break ;
/*
* Keep this as a debug case - eventually we can remove it .
*/
case 0x8001 :
DEBUG ( 0 , ( " process_local_message: Received unsolicited break \
1998-08-17 17:11:34 +04:00
reply - dumping info . \ n " ));
2001-10-21 01:59:34 +04:00
if ( msg_len ! = OPLOCK_BREAK_MSG_LEN ) {
DEBUG ( 0 , ( " process_local_message: ubr: incorrect length for reply \
1999-12-13 16:27:58 +03:00
( was % d , should be % d ) . \ n " , (int)msg_len, (int)OPLOCK_BREAK_MSG_LEN));
2001-10-21 01:59:34 +04:00
return False ;
}
memcpy ( ( char * ) & inode , msg_start + OPLOCK_BREAK_INODE_OFFSET , sizeof ( inode ) ) ;
memcpy ( ( char * ) & remotepid , msg_start + OPLOCK_BREAK_PID_OFFSET , sizeof ( remotepid ) ) ;
memcpy ( ( char * ) & dev , msg_start + OPLOCK_BREAK_DEV_OFFSET , sizeof ( dev ) ) ;
memcpy ( ( char * ) & file_id , msg_start + OPLOCK_BREAK_FILEID_OFFSET , sizeof ( file_id ) ) ;
DEBUG ( 0 , ( " process_local_message: unsolicited oplock break reply from \
pid % d , port % d , dev = % x , inode = % .0f , file_id = % lu \ n " ,
( int ) remotepid , from_port , ( unsigned int ) dev , ( double ) inode , file_id ) ) ;
return False ;
default :
DEBUG ( 0 , ( " process_local_message: unknown UDP message command code (%x) - ignoring. \n " ,
( unsigned int ) SVAL ( msg_start , 0 ) ) ) ;
return False ;
}
/*
* Now actually process the break request .
*/
if ( ( exclusive_oplocks_open + level_II_oplocks_open ) ! = 0 ) {
if ( oplock_break ( dev , inode , file_id , False ) = = False ) {
DEBUG ( 0 , ( " process_local_message: oplock break failed. \n " ) ) ;
return False ;
}
} else {
/*
* If we have no record of any currently open oplocks ,
* it ' s not an error , as a close command may have
* just been issued on the file that was oplocked .
* Just log a message and return success in this case .
*/
DEBUG ( 3 , ( " process_local_message: oplock break requested with no outstanding \
1998-09-23 05:48:45 +04:00
oplocks . Returning success . \ n " ));
2001-10-21 01:59:34 +04:00
}
/*
2003-01-30 04:41:46 +03:00
* Do the appropriate reply - none in the kernel or async level II case .
2001-10-21 01:59:34 +04:00
*/
2003-01-30 04:41:46 +03:00
if ( break_cmd_type = = OPLOCK_BREAK_CMD | | break_cmd_type = = LEVEL_II_OPLOCK_BREAK_CMD ) {
2001-10-21 01:59:34 +04:00
struct sockaddr_in toaddr ;
/* Send the message back after OR'ing in the 'REPLY' bit. */
2003-01-30 04:41:46 +03:00
SSVAL ( msg_start , OPBRK_MESSAGE_CMD_OFFSET , break_cmd_type | CMD_REPLY ) ;
2001-10-21 01:59:34 +04:00
memset ( ( char * ) & toaddr , ' \0 ' , sizeof ( toaddr ) ) ;
toaddr . sin_addr . s_addr = htonl ( INADDR_LOOPBACK ) ;
toaddr . sin_port = htons ( from_port ) ;
toaddr . sin_family = AF_INET ;
2002-07-15 14:35:28 +04:00
if ( sys_sendto ( oplock_sock , msg_start , OPLOCK_BREAK_MSG_LEN , 0 ,
2001-10-21 01:59:34 +04:00
( struct sockaddr * ) & toaddr , sizeof ( toaddr ) ) < 0 ) {
DEBUG ( 0 , ( " process_local_message: sendto process %d failed. Errno was %s \n " ,
( int ) remotepid , strerror ( errno ) ) ) ;
return False ;
}
DEBUG ( 5 , ( " process_local_message: oplock break reply sent to \
pid % d , port % d , for file dev = % x , inode = % .0f , file_id = % lu \ n " ,
( int ) remotepid , from_port , ( unsigned int ) dev , ( double ) inode , file_id ) ) ;
}
return True ;
1998-08-17 17:11:34 +04:00
}
/****************************************************************************
1999-12-13 16:27:58 +03:00
Set up an oplock break message .
1998-08-17 17:11:34 +04:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
1998-09-23 05:48:45 +04:00
1999-12-13 16:27:58 +03:00
static void prepare_break_message ( char * outbuf , files_struct * fsp , BOOL level2 )
{
2000-06-10 17:38:07 +04:00
memset ( outbuf , ' \0 ' , smb_size ) ;
set_message ( outbuf , 8 , 0 , True ) ;
SCVAL ( outbuf , smb_com , SMBlockingX ) ;
SSVAL ( outbuf , smb_tid , fsp - > conn - > cnum ) ;
SSVAL ( outbuf , smb_pid , 0xFFFF ) ;
SSVAL ( outbuf , smb_uid , 0 ) ;
SSVAL ( outbuf , smb_mid , 0xFFFF ) ;
SCVAL ( outbuf , smb_vwv0 , 0xFF ) ;
SSVAL ( outbuf , smb_vwv2 , fsp - > fnum ) ;
SCVAL ( outbuf , smb_vwv3 , LOCKING_ANDX_OPLOCK_RELEASE ) ;
SCVAL ( outbuf , smb_vwv3 + 1 , level2 ? OPLOCKLEVEL_II : OPLOCKLEVEL_NONE ) ;
1999-12-13 16:27:58 +03:00
}
/****************************************************************************
Function to do the waiting before sending a local break .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void wait_before_sending_break ( BOOL local_request )
{
2001-10-21 01:59:34 +04:00
extern struct timeval smb_last_time ;
1999-12-13 16:27:58 +03:00
2001-10-21 01:59:34 +04:00
if ( local_request ) {
struct timeval cur_tv ;
long wait_left = ( long ) lp_oplock_break_wait_time ( ) ;
1999-12-13 16:27:58 +03:00
2001-10-21 01:59:34 +04:00
if ( wait_left = = 0 )
return ;
2001-01-30 03:37:12 +03:00
2001-10-21 01:59:34 +04:00
GetTimeOfDay ( & cur_tv ) ;
1999-12-13 16:27:58 +03:00
2001-10-21 01:59:34 +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 ) ;
2001-10-21 01:59:34 +04:00
if ( wait_left > 0 ) {
wait_left = MIN ( wait_left , 1000 ) ;
sys_usleep ( wait_left * 1000 ) ;
}
}
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 ) ) {
dbgtext ( " initial_break_processing: called for dev = %x, inode = %.0f file_id = %lu \n " ,
( 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 " ) ;
dbgtext ( " dev = %x, inode = %.0f file_id = %lu " , ( unsigned int ) dev ,
( 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
}
/****************************************************************************
Process a level II oplock break directly .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
BOOL oplock_break_level2 ( files_struct * fsp , BOOL local_request , int token )
{
2001-10-21 01:59:34 +04:00
extern uint32 global_client_caps ;
char outbuf [ 128 ] ;
BOOL got_lock = False ;
SMB_DEV_T dev = fsp - > dev ;
SMB_INO_T inode = fsp - > inode ;
/*
* We can have a level II oplock even if the client is not
* level II oplock aware . In this case just remove the
* flags and don ' t send the break - to - none message to
* the client .
*/
if ( global_client_caps & CAP_LEVEL_II_OPLOCKS ) {
/*
* If we are sending an oplock break due to an SMB sent
* by our own client we ensure that we wait at leat
* lp_oplock_break_wait_time ( ) milliseconds before sending
* the packet . Sending the packet sooner can break Win9x
* and has reported to cause problems on NT . JRA .
*/
wait_before_sending_break ( local_request ) ;
/* Prepare the SMBlockingX message. */
prepare_break_message ( outbuf , fsp , False ) ;
if ( ! send_smb ( smbd_server_fd ( ) , outbuf ) )
2001-11-05 03:02:38 +03:00
exit_server ( " oplock_break_level2: send_smb failed. " ) ;
2001-10-21 01:59:34 +04:00
}
/*
* Now we must update the shared memory structure to tell
* everyone else we no longer have a level II oplock on
* this open file . If local_request is true then token is
* the existing lock on the shared memory area .
*/
if ( ! local_request & & lock_share_entry_fsp ( fsp ) = = False ) {
DEBUG ( 0 , ( " oplock_break_level2: unable to lock share entry for file %s \n " , fsp - > fsp_name ) ) ;
} else {
got_lock = True ;
}
if ( remove_share_oplock ( fsp ) = = False ) {
DEBUG ( 0 , ( " oplock_break_level2: unable to remove level II oplock for file %s \n " , fsp - > fsp_name ) ) ;
}
2003-04-22 05:12:52 +04:00
release_file_oplock ( fsp ) ;
2001-10-21 01:59:34 +04:00
if ( ! local_request & & got_lock )
unlock_share_entry_fsp ( fsp ) ;
if ( level_II_oplocks_open < 0 ) {
DEBUG ( 0 , ( " oplock_break_level2: level_II_oplocks_open < 0 (%d). PANIC ERROR \n " ,
level_II_oplocks_open ) ) ;
abort ( ) ;
}
if ( DEBUGLVL ( 3 ) ) {
dbgtext ( " oplock_break_level2: returning success for " ) ;
dbgtext ( " dev = %x, inode = %.0f, file_id = %lu \n " , ( unsigned int ) dev , ( double ) inode , fsp - > file_id ) ;
dbgtext ( " Current level II oplocks_open = %d \n " , level_II_oplocks_open ) ;
}
return True ;
1999-12-13 16:27:58 +03:00
}
/****************************************************************************
Process an oplock break directly .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-10-21 01:59:34 +04:00
static BOOL oplock_break ( SMB_DEV_T dev , SMB_INO_T inode , unsigned long file_id , BOOL local_request )
1999-12-13 16:27:58 +03:00
{
2001-10-21 01:59:34 +04:00
extern uint32 global_client_caps ;
extern struct current_user current_user ;
char * inbuf = NULL ;
char * outbuf = NULL ;
files_struct * fsp = NULL ;
time_t start_time ;
BOOL shutdown_server = False ;
BOOL oplock_timeout = False ;
connection_struct * saved_user_conn ;
connection_struct * saved_fsp_conn ;
int saved_vuid ;
pstring saved_dir ;
int timeout = ( OPLOCK_BREAK_TIMEOUT * 1000 ) ;
pstring file_name ;
BOOL using_levelII ;
if ( ( fsp = initial_break_processing ( dev , inode , file_id ) ) = = NULL )
return True ;
/*
* Deal with a level II oplock going break to none separately .
*/
if ( LEVEL_II_OPLOCK_TYPE ( fsp - > oplock_type ) )
return oplock_break_level2 ( fsp , local_request , - 1 ) ;
/* Mark the oplock break as sent - we don't want to send twice! */
if ( fsp - > sent_oplock_break ) {
if ( DEBUGLVL ( 0 ) ) {
dbgtext ( " oplock_break: ERROR: oplock_break already sent for " ) ;
dbgtext ( " file %s " , fsp - > fsp_name ) ;
dbgtext ( " (dev = %x, inode = %.0f, file_id = %lu) \n " , ( unsigned int ) dev , ( double ) inode , fsp - > file_id ) ;
}
/*
* We have to fail the open here as we cannot send another oplock break on
* this file whilst we are awaiting a response from the client - neither
* can we allow another open to succeed while we are waiting for the client .
*/
return False ;
}
if ( global_oplock_break ) {
DEBUG ( 0 , ( " ABORT : ABORT : recursion in oplock_break !!!!! \n " ) ) ;
abort ( ) ;
}
/*
* Now comes the horrid part . We must send an oplock break to the client ,
* and then process incoming messages until we get a close or oplock release .
* At this point we know we need a new inbuf / outbuf buffer pair .
* We cannot use these staticaly as we may recurse into here due to
* messages crossing on the wire .
*/
if ( ( inbuf = ( char * ) malloc ( BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN ) ) = = NULL ) {
DEBUG ( 0 , ( " oplock_break: malloc fail for input buffer. \n " ) ) ;
return False ;
}
if ( ( outbuf = ( char * ) malloc ( BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE + SAFETY_MARGIN ) ) = = NULL ) {
DEBUG ( 0 , ( " oplock_break: malloc fail for output buffer. \n " ) ) ;
SAFE_FREE ( inbuf ) ;
return False ;
}
/*
* If we are sending an oplock break due to an SMB sent
* by our own client we ensure that we wait at leat
* lp_oplock_break_wait_time ( ) milliseconds before sending
* the packet . Sending the packet sooner can break Win9x
* and has reported to cause problems on NT . JRA .
*/
wait_before_sending_break ( local_request ) ;
/* Prepare the SMBlockingX message. */
if ( ( 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 ) ) ) {
using_levelII = True ;
} else {
using_levelII = False ;
}
prepare_break_message ( outbuf , fsp , using_levelII ) ;
/* Remember if we just sent a break to level II on this file. */
fsp - > sent_oplock_break = using_levelII ? LEVEL_II_BREAK_SENT : EXCLUSIVE_BREAK_SENT ;
if ( ! send_smb ( smbd_server_fd ( ) , outbuf ) )
2001-11-05 03:02:38 +03:00
exit_server ( " oplock_break: send_smb failed. " ) ;
2001-10-21 01:59:34 +04:00
/* We need this in case a readraw crosses on the wire. */
global_oplock_break = True ;
1998-08-17 17:11:34 +04:00
2001-10-21 01:59:34 +04:00
/* Process incoming messages. */
/*
* JRA - If we don ' t get a break from the client in OPLOCK_BREAK_TIMEOUT
* seconds we should just die . . . .
*/
start_time = time ( NULL ) ;
/*
* Save the information we need to re - become the
* user , then unbecome the user whilst we ' re doing this .
*/
saved_user_conn = current_user . conn ;
saved_vuid = current_user . vuid ;
saved_fsp_conn = fsp - > conn ;
change_to_root_user ( ) ;
2002-03-14 03:58:53 +03:00
vfs_GetWd ( saved_fsp_conn , saved_dir ) ;
2001-10-21 01:59:34 +04:00
/* Save the chain fnum. */
file_chain_save ( ) ;
/*
* From Charles Hoch < hoch @ exemplary . com > . If the break processing
* code closes the file ( as it often does ) , then the fsp pointer here
* points to free ( ) ' d memory . We * must * revalidate fsp each time
* around the loop .
*/
pstrcpy ( file_name , fsp - > fsp_name ) ;
while ( ( fsp = initial_break_processing ( dev , inode , file_id ) ) & &
OPEN_FSP ( fsp ) & & EXCLUSIVE_OPLOCK_TYPE ( fsp - > oplock_type ) ) {
if ( receive_smb ( smbd_server_fd ( ) , inbuf , timeout ) = = False ) {
/*
* Die if we got an error .
*/
if ( smb_read_error = = READ_EOF ) {
DEBUG ( 0 , ( " oplock_break: end of file from client \n " ) ) ;
shutdown_server = True ;
} else if ( smb_read_error = = READ_ERROR ) {
DEBUG ( 0 , ( " oplock_break: receive_smb error (%s) \n " , strerror ( errno ) ) ) ;
shutdown_server = True ;
} else if ( smb_read_error = = READ_TIMEOUT ) {
DEBUG ( 0 , ( " oplock_break: receive_smb timed out after %d seconds. \n " , OPLOCK_BREAK_TIMEOUT ) ) ;
oplock_timeout = True ;
}
DEBUGADD ( 0 , ( " oplock_break failed for file %s " , file_name ) ) ;
DEBUGADD ( 0 , ( " (dev = %x, inode = %.0f, file_id = %lu). \n " ,
( unsigned int ) dev , ( double ) inode , file_id ) ) ;
break ;
}
/*
* There are certain SMB requests that we shouldn ' t allow
* to recurse . opens , renames and deletes are the obvious
* ones . This is handled in the switch_message ( ) function .
* If global_oplock_break is set they will push the packet onto
* the pending smb queue and return - 1 ( no reply ) .
* JRA .
*/
process_smb ( inbuf , outbuf ) ;
/*
* Die if we go over the time limit .
*/
if ( ( time ( NULL ) - start_time ) > OPLOCK_BREAK_TIMEOUT ) {
if ( DEBUGLVL ( 0 ) ) {
dbgtext ( " oplock_break: no break received from client " ) ;
dbgtext ( " within %d seconds. \n " , OPLOCK_BREAK_TIMEOUT ) ;
dbgtext ( " oplock_break failed for file %s " , fsp - > fsp_name ) ;
dbgtext ( " (dev = %x, inode = %.0f, file_id = %lu). \n " ,
( unsigned int ) dev , ( double ) inode , file_id ) ;
}
oplock_timeout = True ;
break ;
}
}
/*
* Go back to being the user who requested the oplock
* break .
*/
if ( ( saved_user_conn ! = NULL ) & & ( saved_vuid ! = UID_FIELD_INVALID ) & & ! change_to_user ( saved_user_conn , saved_vuid ) ) {
DEBUG ( 0 , ( " oplock_break: unable to re-become user! " ) ) ;
DEBUGADD ( 0 , ( " Shutting down server \n " ) ) ;
close ( oplock_sock ) ;
exit_server ( " unable to re-become user " ) ;
}
/* Including the directory. */
vfs_ChDir ( saved_fsp_conn , saved_dir ) ;
/* Restore the chain fnum. */
file_chain_restore ( ) ;
/* Free the buffers we've been using to recurse. */
SAFE_FREE ( inbuf ) ;
SAFE_FREE ( outbuf ) ;
/* We need this in case a readraw crossed on the wire. */
if ( global_oplock_break )
global_oplock_break = False ;
/*
* If the client timed out then clear the oplock ( or go to level II )
* and continue . This seems to be what NT does and is better than dropping
* the connection .
*/
if ( oplock_timeout & & ( fsp = initial_break_processing ( dev , inode , file_id ) ) & &
OPEN_FSP ( fsp ) & & EXCLUSIVE_OPLOCK_TYPE ( fsp - > oplock_type ) ) {
DEBUG ( 0 , ( " oplock_break: client failure in oplock break in file %s \n " , fsp - > fsp_name ) ) ;
remove_oplock ( fsp , True ) ;
2002-04-11 06:20:56 +04:00
# if FASCIST_OPLOCK_BACKOFF
2001-10-21 01:59:34 +04:00
global_client_failed_oplock_break = True ; /* Never grant this client an oplock again. */
2002-04-11 06:20:56 +04:00
# endif
2001-10-21 01:59:34 +04:00
}
/*
* If the client had an error we must die .
*/
if ( shutdown_server ) {
DEBUG ( 0 , ( " oplock_break: client failure in break - " ) ) ;
DEBUGADD ( 0 , ( " shutting down this smbd. \n " ) ) ;
close ( oplock_sock ) ;
exit_server ( " oplock break failure " ) ;
}
/* Santity check - remove this later. JRA */
if ( exclusive_oplocks_open < 0 ) {
DEBUG ( 0 , ( " oplock_break: exclusive_oplocks_open < 0 (%d). PANIC ERROR \n " , exclusive_oplocks_open ) ) ;
abort ( ) ;
}
if ( DEBUGLVL ( 3 ) ) {
dbgtext ( " oplock_break: returning success for " ) ;
dbgtext ( " dev = %x, inode = %.0f, file_id = %lu \n " , ( unsigned int ) dev , ( double ) inode , file_id ) ;
dbgtext ( " Current exclusive_oplocks_open = %d \n " , exclusive_oplocks_open ) ;
}
return True ;
1998-08-17 17:11:34 +04:00
}
/****************************************************************************
Send an oplock break message to another smbd process . If the oplock is held
by the local smbd then call the oplock break function directly .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2003-01-30 04:41:46 +03:00
BOOL request_oplock_break ( share_mode_entry * share_entry , BOOL async )
1998-08-17 17:11:34 +04:00
{
2001-10-21 01:59:34 +04:00
char op_break_msg [ OPLOCK_BREAK_MSG_LEN ] ;
struct sockaddr_in addr_out ;
pid_t pid = sys_getpid ( ) ;
time_t start_time ;
int time_left ;
SMB_DEV_T dev = share_entry - > dev ;
SMB_INO_T inode = share_entry - > inode ;
unsigned long file_id = share_entry - > share_file_id ;
2003-01-30 04:41:46 +03:00
uint16 break_cmd_type ;
2001-10-21 01:59:34 +04:00
if ( pid = = share_entry - > pid ) {
/* We are breaking our own oplock, make sure it's us. */
if ( share_entry - > op_port ! = global_oplock_port ) {
DEBUG ( 0 , ( " request_oplock_break: corrupt share mode entry - pid = %d, port = %d \
1999-12-13 16:27:58 +03:00
should be % d \ n " , (int)pid, share_entry->op_port, global_oplock_port));
2001-10-21 01:59:34 +04:00
return False ;
}
1998-08-17 17:11:34 +04:00
2001-10-21 01:59:34 +04:00
DEBUG ( 5 , ( " request_oplock_break: breaking our own oplock \n " ) ) ;
1998-08-17 17:11:34 +04:00
2001-08-21 05:25:45 +04:00
# if 1 /* JRA PARANOIA TEST.... */
2001-10-21 01:59:34 +04:00
{
files_struct * fsp = file_find_dif ( dev , inode , file_id ) ;
if ( ! fsp ) {
DEBUG ( 0 , ( " request_oplock_break: PANIC : breaking our own oplock requested for \
dev = % x , inode = % .0f , file_id = % lu and no fsp found ! \ n " ,
( unsigned int ) dev , ( double ) inode , file_id ) ) ;
smb_panic ( " request_oplock_break: no fsp found for our own oplock \n " ) ;
}
}
2001-08-21 05:25:45 +04:00
# endif /* END JRA PARANOIA TEST... */
2001-10-21 01:59:34 +04:00
/* Call oplock break direct. */
return oplock_break ( dev , inode , file_id , True ) ;
}
/* We need to send a OPLOCK_BREAK_CMD message to the port in the share mode entry. */
if ( LEVEL_II_OPLOCK_TYPE ( share_entry - > op_type ) ) {
2003-01-30 04:41:46 +03:00
break_cmd_type = async ? ASYNC_LEVEL_II_OPLOCK_BREAK_CMD : LEVEL_II_OPLOCK_BREAK_CMD ;
2001-10-21 01:59:34 +04:00
} else {
2003-01-30 04:41:46 +03:00
break_cmd_type = OPLOCK_BREAK_CMD ;
2001-10-21 01:59:34 +04:00
}
2003-01-30 04:41:46 +03:00
SSVAL ( op_break_msg , OPBRK_MESSAGE_CMD_OFFSET , break_cmd_type ) ;
2001-10-21 01:59:34 +04:00
memcpy ( op_break_msg + OPLOCK_BREAK_PID_OFFSET , ( char * ) & pid , sizeof ( pid ) ) ;
memcpy ( op_break_msg + OPLOCK_BREAK_DEV_OFFSET , ( char * ) & dev , sizeof ( dev ) ) ;
memcpy ( op_break_msg + OPLOCK_BREAK_INODE_OFFSET , ( char * ) & inode , sizeof ( inode ) ) ;
memcpy ( op_break_msg + OPLOCK_BREAK_FILEID_OFFSET , ( char * ) & file_id , sizeof ( file_id ) ) ;
/* Set the address and port. */
memset ( ( char * ) & addr_out , ' \0 ' , sizeof ( addr_out ) ) ;
addr_out . sin_addr . s_addr = htonl ( INADDR_LOOPBACK ) ;
addr_out . sin_port = htons ( share_entry - > op_port ) ;
addr_out . sin_family = AF_INET ;
1998-08-17 17:11:34 +04:00
2001-10-21 01:59:34 +04:00
if ( DEBUGLVL ( 3 ) ) {
2003-01-30 04:41:46 +03:00
dbgtext ( " request_oplock_break: sending a %s oplock break message to " , async ? " asynchronous " : " synchronous " ) ;
2001-10-21 01:59:34 +04:00
dbgtext ( " pid %d on port %d " , ( int ) share_entry - > pid , share_entry - > op_port ) ;
dbgtext ( " for dev = %x, inode = %.0f, file_id = %lu \n " ,
( unsigned int ) dev , ( double ) inode , file_id ) ;
}
2002-07-15 14:35:28 +04:00
if ( sys_sendto ( oplock_sock , op_break_msg , OPLOCK_BREAK_MSG_LEN , 0 ,
2001-10-21 01:59:34 +04:00
( struct sockaddr * ) & addr_out , sizeof ( addr_out ) ) < 0 ) {
if ( DEBUGLVL ( 0 ) ) {
dbgtext ( " request_oplock_break: failed when sending a oplock " ) ;
dbgtext ( " break message to pid %d " , ( int ) share_entry - > pid ) ;
dbgtext ( " on port %d " , share_entry - > op_port ) ;
dbgtext ( " for dev = %x, inode = %.0f, file_id = %lu \n " ,
2003-01-30 04:41:46 +03:00
( unsigned int ) dev , ( double ) inode , file_id ) ;
2001-10-21 01:59:34 +04:00
dbgtext ( " Error was %s \n " , strerror ( errno ) ) ;
}
return False ;
}
/*
2003-01-30 04:41:46 +03:00
* If we just sent a message to a level II oplock share entry in async mode then
2001-10-21 01:59:34 +04:00
* we are done and may return .
*/
2003-01-30 04:41:46 +03:00
if ( LEVEL_II_OPLOCK_TYPE ( share_entry - > op_type ) & & async ) {
DEBUG ( 3 , ( " request_oplock_break: sent async break message to level II entry. \n " ) ) ;
2001-10-21 01:59:34 +04:00
return True ;
}
/*
* Now we must await the oplock broken message coming back
* from the target smbd process . Timeout if it fails to
* return in ( OPLOCK_BREAK_TIMEOUT + OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR ) seconds .
* While we get messages that aren ' t ours , loop .
*/
start_time = time ( NULL ) ;
time_left = OPLOCK_BREAK_TIMEOUT + OPLOCK_BREAK_TIMEOUT_FUDGEFACTOR ;
while ( time_left > = 0 ) {
char op_break_reply [ OPBRK_CMD_HEADER_LEN + OPLOCK_BREAK_MSG_LEN ] ;
uint16 reply_from_port ;
char * reply_msg_start ;
2002-07-15 14:35:28 +04:00
if ( receive_local_message ( op_break_reply , sizeof ( op_break_reply ) ,
2001-10-21 01:59:34 +04:00
time_left ? time_left * 1000 : 1 ) = = False ) {
if ( smb_read_error = = READ_TIMEOUT ) {
if ( DEBUGLVL ( 0 ) ) {
dbgtext ( " request_oplock_break: no response received to oplock " ) ;
dbgtext ( " break request to pid %d " , ( int ) share_entry - > pid ) ;
dbgtext ( " on port %d " , share_entry - > op_port ) ;
dbgtext ( " for dev = %x, inode = %.0f, file_id = %lu \n " ,
( unsigned int ) dev , ( double ) inode , file_id ) ;
}
/*
* This is a hack to make handling of failing clients more robust .
* If a oplock break response message is not received in the timeout
* period we may assume that the smbd servicing that client holding
* the oplock has died and the client changes were lost anyway , so
* we should continue to try and open the file .
*/
break ;
} else {
if ( DEBUGLVL ( 0 ) ) {
dbgtext ( " request_oplock_break: error in response received " ) ;
dbgtext ( " to oplock break request to pid %d " , ( int ) share_entry - > pid ) ;
dbgtext ( " on port %d " , share_entry - > op_port ) ;
dbgtext ( " for dev = %x, inode = %.0f, file_id = %lu \n " ,
( unsigned int ) dev , ( double ) inode , file_id ) ;
dbgtext ( " Error was (%s). \n " , strerror ( errno ) ) ;
}
}
return False ;
}
reply_from_port = SVAL ( op_break_reply , OPBRK_CMD_PORT_OFFSET ) ;
reply_msg_start = & op_break_reply [ OPBRK_CMD_HEADER_LEN ] ;
/*
2003-01-30 04:41:46 +03:00
* Test to see if this is the reply we are awaiting ( ie . the one we sent with the CMD_REPLY flag OR ' ed in ) .
2001-10-21 01:59:34 +04:00
*/
if ( ( SVAL ( reply_msg_start , OPBRK_MESSAGE_CMD_OFFSET ) & CMD_REPLY ) & &
2003-01-30 04:41:46 +03:00
( ( SVAL ( reply_msg_start , OPBRK_MESSAGE_CMD_OFFSET ) & ~ CMD_REPLY ) = = break_cmd_type ) & &
2001-10-21 01:59:34 +04:00
( reply_from_port = = share_entry - > op_port ) & &
( memcmp ( & reply_msg_start [ OPLOCK_BREAK_PID_OFFSET ] , & op_break_msg [ OPLOCK_BREAK_PID_OFFSET ] ,
OPLOCK_BREAK_MSG_LEN - OPLOCK_BREAK_PID_OFFSET ) = = 0 ) ) {
/*
* This is the reply we ' ve been waiting for .
*/
break ;
} else {
/*
* This is another message - a break request .
* Note that both kernel oplock break requests
* and UDP inter - smbd oplock break requests will
* be processed here .
*
* Process it to prevent potential deadlock .
* Note that the code in switch_message ( ) prevents
* us from recursing into here as any SMB requests
* we might process that would cause another oplock
* break request to be made will be queued .
* JRA .
*/
process_local_message ( op_break_reply , sizeof ( op_break_reply ) ) ;
}
time_left - = ( time ( NULL ) - start_time ) ;
}
DEBUG ( 3 , ( " request_oplock_break: broke oplock. \n " ) ) ;
return True ;
1998-08-17 17:11:34 +04:00
}
/****************************************************************************
Attempt to break an oplock on a file ( if oplocked ) .
Returns True if the file was closed as a result of
the oplock break , False otherwise .
Used as a last ditch attempt to free a space in the
file table when we have run out .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2001-10-21 01:59:34 +04:00
1998-08-17 17:11:34 +04:00
BOOL attempt_close_oplocked_file ( files_struct * fsp )
{
2001-10-21 01:59:34 +04:00
DEBUG ( 5 , ( " attempt_close_oplocked_file: checking file %s. \n " , fsp - > fsp_name ) ) ;
1998-08-17 17:11:34 +04:00
2001-10-21 01:59:34 +04:00
if ( EXCLUSIVE_OPLOCK_TYPE ( fsp - > oplock_type ) & & ! fsp - > sent_oplock_break & & ( fsp - > fd ! = - 1 ) ) {
/* Try and break the oplock. */
if ( oplock_break ( fsp - > dev , fsp - > inode , fsp - > file_id , True ) ) {
if ( file_find_fsp ( fsp ) = = NULL ) /* Did the oplock break close the file ? */
return True ;
}
}
1998-08-17 17:11:34 +04:00
2001-10-21 01:59:34 +04:00
return False ;
1998-08-17 17:11:34 +04:00
}
2000-11-16 03:59:18 +03:00
/****************************************************************************
This function is called on any file modification or lock request . If a file
is level 2 oplocked then it must tell all other level 2 holders to break to none .
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
void release_level_2_oplocks_on_change ( files_struct * fsp )
{
share_mode_entry * share_list = NULL ;
pid_t pid = sys_getpid ( ) ;
int token = - 1 ;
int num_share_modes = 0 ;
int i ;
/*
* 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 ;
if ( lock_share_entry_fsp ( fsp ) = = False ) {
DEBUG ( 0 , ( " release_level_2_oplocks_on_change: failed to lock share mode entry for file %s. \n " , fsp - > fsp_name ) ) ;
}
num_share_modes = get_share_modes ( fsp - > conn , fsp - > dev , fsp - > inode , & share_list ) ;
2001-08-22 04:29:40 +04:00
DEBUG ( 10 , ( " release_level_2_oplocks_on_change: num_share_modes = %d \n " ,
num_share_modes ) ) ;
2000-11-16 03:59:18 +03:00
for ( i = 0 ; i < num_share_modes ; i + + ) {
share_mode_entry * share_entry = & share_list [ i ] ;
/*
* 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 .
*/
2001-08-22 04:29:40 +04:00
DEBUG ( 10 , ( " release_level_2_oplocks_on_change: share_entry[%i]->op_type == %d \n " ,
i , share_entry - > op_type ) ) ;
2000-11-16 03:59:18 +03:00
if ( share_entry - > op_type = = NO_OPLOCK )
continue ;
/* Paranoia .... */
if ( EXCLUSIVE_OPLOCK_TYPE ( share_entry - > op_type ) ) {
DEBUG ( 0 , ( " release_level_2_oplocks_on_change: PANIC. share mode entry %d is an exlusive oplock ! \n " , i ) ) ;
unlock_share_entry ( fsp - > conn , fsp - > dev , fsp - > inode ) ;
abort ( ) ;
}
/*
* Check if this is a file we have open ( including the
* file we ' ve been called to do write_file on . If so
* then break it directly without releasing the lock .
*/
if ( pid = = share_entry - > pid ) {
2001-10-21 01:59:34 +04:00
files_struct * new_fsp = file_find_dif ( share_entry - > dev , share_entry - > inode , share_entry - > share_file_id ) ;
2000-11-16 03:59:18 +03:00
/* Paranoia check... */
if ( new_fsp = = NULL ) {
DEBUG ( 0 , ( " release_level_2_oplocks_on_change: PANIC. share mode entry %d is not a local file ! \n " , i ) ) ;
unlock_share_entry ( fsp - > conn , fsp - > dev , fsp - > inode ) ;
abort ( ) ;
}
2001-08-22 04:29:40 +04:00
DEBUG ( 10 , ( " release_level_2_oplocks_on_change: breaking our own oplock. \n " ) ) ;
2000-11-16 03:59:18 +03:00
oplock_break_level2 ( new_fsp , True , token ) ;
} else {
/*
* This is a remote file and so we send an asynchronous
* message .
*/
2003-01-30 04:41:46 +03:00
DEBUG ( 10 , ( " release_level_2_oplocks_on_change: breaking remote oplock (async). \n " ) ) ;
request_oplock_break ( share_entry , True ) ;
2000-11-16 03:59:18 +03:00
}
}
2001-10-23 22:15:18 +04:00
SAFE_FREE ( share_list ) ;
2000-11-16 03:59:18 +03:00
unlock_share_entry_fsp ( fsp ) ;
/* Paranoia check... */
if ( LEVEL_II_OPLOCK_TYPE ( fsp - > oplock_type ) ) {
DEBUG ( 0 , ( " release_level_2_oplocks_on_change: PANIC. File %s still has a level II oplock. \n " , fsp - > fsp_name ) ) ;
2001-08-22 04:29:40 +04:00
smb_panic ( " release_level_2_oplocks_on_change " ) ;
2000-11-16 03:59:18 +03:00
}
}
2000-06-10 17:38:07 +04:00
1998-09-23 05:48:45 +04:00
/****************************************************************************
2000-06-10 17:38:07 +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
struct sockaddr_in sock_name ;
2001-06-25 06:53:13 +04:00
socklen_t len = sizeof ( sock_name ) ;
2000-06-10 17:38:07 +04:00
DEBUG ( 3 , ( " open_oplock_ipc: opening loopback UDP socket. \n " ) ) ;
/* Open a lookback UDP socket on a random port. */
oplock_sock = open_socket_in ( SOCK_DGRAM , 0 , 0 , htonl ( INADDR_LOOPBACK ) , False ) ;
if ( oplock_sock = = - 1 ) {
DEBUG ( 0 , ( " open_oplock_ipc: Failed to get local UDP socket for \
address % lx . Error was % s \ n " , (long)htonl(INADDR_LOOPBACK), strerror(errno)));
global_oplock_port = 0 ;
return ( False ) ;
}
/* Find out the transient UDP port we have been allocated. */
if ( getsockname ( oplock_sock , ( struct sockaddr * ) & sock_name , & len ) < 0 ) {
DEBUG ( 0 , ( " open_oplock_ipc: Failed to get local UDP port. Error was %s \n " ,
strerror ( errno ) ) ) ;
close ( oplock_sock ) ;
oplock_sock = - 1 ;
global_oplock_port = 0 ;
return False ;
2000-06-09 10:58:06 +04:00
}
2000-06-10 17:38:07 +04:00
global_oplock_port = ntohs ( sock_name . sin_port ) ;
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
}
DEBUG ( 3 , ( " open_oplock ipc: pid = %d, global_oplock_port = %u \n " ,
( int ) sys_getpid ( ) , global_oplock_port ) ) ;
return True ;
1998-09-18 21:50:18 +04:00
}