2008-02-25 10:32:35 +03:00
/*
Unix SMB / CIFS implementation .
POSIX NTVFS backend - oplock handling
Copyright ( C ) Stefan Metzmacher 2008
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 3 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 , see < http : //www.gnu.org/licenses/>.
*/
# include "includes.h"
# include "lib/messaging/messaging.h"
# include "lib/messaging/irpc.h"
2008-03-04 16:16:17 +03:00
# include "system/time.h"
2008-02-25 10:32:35 +03:00
# include "vfs_posix.h"
struct pvfs_oplock {
struct pvfs_file_handle * handle ;
struct pvfs_file * file ;
uint32_t level ;
2008-03-04 16:16:17 +03:00
struct timeval break_to_level_II ;
struct timeval break_to_none ;
2011-05-03 04:40:33 +04:00
struct imessaging_context * msg_ctx ;
2008-02-25 10:32:35 +03:00
} ;
2008-03-04 16:10:13 +03:00
static NTSTATUS pvfs_oplock_release_internal ( struct pvfs_file_handle * h ,
uint8_t oplock_break )
{
struct odb_lock * olck ;
NTSTATUS status ;
if ( h - > fd = = - 1 ) {
return NT_STATUS_FILE_IS_A_DIRECTORY ;
}
if ( ! h - > have_opendb_entry ) {
return NT_STATUS_FOOBAR ;
}
if ( ! h - > oplock ) {
return NT_STATUS_FOOBAR ;
}
olck = odb_lock ( h , h - > pvfs - > odb_context , & h - > odb_locking_key ) ;
if ( olck = = NULL ) {
DEBUG ( 0 , ( " Unable to lock opendb for oplock update \n " ) ) ;
return NT_STATUS_FOOBAR ;
}
if ( oplock_break = = OPLOCK_BREAK_TO_NONE ) {
h - > oplock - > level = OPLOCK_NONE ;
} else if ( oplock_break = = OPLOCK_BREAK_TO_LEVEL_II ) {
h - > oplock - > level = OPLOCK_LEVEL_II ;
} else {
/* fallback to level II in case of a invalid value */
DEBUG ( 1 , ( " unexpected oplock break level[0x%02X] \n " , oplock_break ) ) ;
h - > oplock - > level = OPLOCK_LEVEL_II ;
}
status = odb_update_oplock ( olck , h , h - > oplock - > level ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " Unable to update oplock level for '%s' - %s \n " ,
h - > name - > full_name , nt_errstr ( status ) ) ) ;
talloc_free ( olck ) ;
return status ;
}
talloc_free ( olck ) ;
/* after a break to none, we no longer have an oplock attached */
if ( h - > oplock - > level = = OPLOCK_NONE ) {
talloc_free ( h - > oplock ) ;
h - > oplock = NULL ;
}
return NT_STATUS_OK ;
}
2008-02-25 10:32:35 +03:00
/*
receive oplock breaks and forward them to the client
*/
static void pvfs_oplock_break ( struct pvfs_oplock * opl , uint8_t level )
{
NTSTATUS status ;
struct pvfs_file * f = opl - > file ;
struct pvfs_file_handle * h = opl - > handle ;
struct pvfs_state * pvfs = h - > pvfs ;
2008-03-04 16:16:17 +03:00
struct timeval cur = timeval_current ( ) ;
struct timeval * last = NULL ;
struct timeval end ;
2008-02-25 10:32:35 +03:00
2008-03-04 16:16:17 +03:00
switch ( level ) {
case OPLOCK_BREAK_TO_LEVEL_II :
last = & opl - > break_to_level_II ;
break ;
case OPLOCK_BREAK_TO_NONE :
last = & opl - > break_to_none ;
break ;
}
if ( ! last ) {
DEBUG ( 0 , ( " %s: got unexpected level[0x%02X] \n " ,
__FUNCTION__ , level ) ) ;
return ;
}
if ( timeval_is_zero ( last ) ) {
/*
* this is the first break we for this level
* remember the time
*/
* last = cur ;
2009-08-12 09:20:02 +04:00
DEBUG ( 5 , ( " %s: sending oplock break level %d for '%s' %p \n " ,
2008-03-04 16:16:17 +03:00
__FUNCTION__ , level , h - > name - > original_name , h ) ) ;
status = ntvfs_send_oplock_break ( pvfs - > ntvfs , f - > ntvfs , level ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " %s: sending oplock break failed: %s \n " ,
__FUNCTION__ , nt_errstr ( status ) ) ) ;
}
return ;
}
end = timeval_add ( last , pvfs - > oplock_break_timeout , 0 ) ;
if ( timeval_compare ( & cur , & end ) < 0 ) {
/*
* If it ' s not expired just ignore the break
* as we already sent the break request to the client
*/
DEBUG ( 0 , ( " %s: do not resend oplock break level %d for '%s' %p \n " ,
__FUNCTION__ , level , h - > name - > original_name , h ) ) ;
return ;
}
/*
* If the client did not send a release within the
* oplock break timeout time frame we auto release
* the oplock
*/
DEBUG ( 0 , ( " %s: auto release oplock level %d for '%s' %p \n " ,
__FUNCTION__ , level , h - > name - > original_name , h ) ) ;
status = pvfs_oplock_release_internal ( h , level ) ;
2008-02-25 10:32:35 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2008-03-04 16:16:17 +03:00
DEBUG ( 0 , ( " %s: failed to auto release the oplock[0x%02X]: %s \n " ,
__FUNCTION__ , level , nt_errstr ( status ) ) ) ;
2008-02-25 10:32:35 +03:00
}
}
2011-05-03 04:40:33 +04:00
static void pvfs_oplock_break_dispatch ( struct imessaging_context * msg ,
2008-02-25 10:32:35 +03:00
void * private_data , uint32_t msg_type ,
struct server_id src , DATA_BLOB * data )
{
struct pvfs_oplock * opl = talloc_get_type ( private_data ,
struct pvfs_oplock ) ;
struct opendb_oplock_break opb ;
ZERO_STRUCT ( opb ) ;
/* we need to check that this one is for us. See
2011-05-03 04:40:33 +04:00
imessaging_send_ptr ( ) for the other side of this .
2008-02-25 10:32:35 +03:00
*/
if ( data - > length = = sizeof ( struct opendb_oplock_break ) ) {
struct opendb_oplock_break * p ;
p = ( struct opendb_oplock_break * ) data - > data ;
opb = * p ;
} else {
DEBUG ( 0 , ( " %s: ignore oplock break with length[%u] \n " ,
2008-06-02 05:05:06 +04:00
__location__ , ( unsigned ) data - > length ) ) ;
2008-02-25 10:32:35 +03:00
return ;
}
if ( opb . file_handle ! = opl - > handle ) {
return ;
}
/*
* maybe we should use ntvfs_setup_async ( )
*/
pvfs_oplock_break ( opl , opb . level ) ;
}
static int pvfs_oplock_destructor ( struct pvfs_oplock * opl )
{
2011-05-03 04:40:33 +04:00
imessaging_deregister ( opl - > msg_ctx , MSG_NTVFS_OPLOCK_BREAK , opl ) ;
2008-02-25 10:32:35 +03:00
return 0 ;
}
NTSTATUS pvfs_setup_oplock ( struct pvfs_file * f , uint32_t oplock_granted )
{
NTSTATUS status ;
struct pvfs_oplock * opl ;
uint32_t level = OPLOCK_NONE ;
f - > handle - > oplock = NULL ;
switch ( oplock_granted ) {
case EXCLUSIVE_OPLOCK_RETURN :
level = OPLOCK_EXCLUSIVE ;
break ;
case BATCH_OPLOCK_RETURN :
level = OPLOCK_BATCH ;
break ;
case LEVEL_II_OPLOCK_RETURN :
level = OPLOCK_LEVEL_II ;
break ;
}
if ( level = = OPLOCK_NONE ) {
return NT_STATUS_OK ;
}
2008-03-04 16:16:17 +03:00
opl = talloc_zero ( f - > handle , struct pvfs_oplock ) ;
2008-02-25 10:32:35 +03:00
NT_STATUS_HAVE_NO_MEMORY ( opl ) ;
opl - > handle = f - > handle ;
opl - > file = f ;
opl - > level = level ;
opl - > msg_ctx = f - > pvfs - > ntvfs - > ctx - > msg_ctx ;
2011-05-03 04:40:33 +04:00
status = imessaging_register ( opl - > msg_ctx ,
2008-02-25 10:32:35 +03:00
opl ,
MSG_NTVFS_OPLOCK_BREAK ,
pvfs_oplock_break_dispatch ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
/* destructor */
talloc_set_destructor ( opl , pvfs_oplock_destructor ) ;
f - > handle - > oplock = opl ;
return NT_STATUS_OK ;
}
2008-02-25 11:51:58 +03:00
NTSTATUS pvfs_oplock_release ( struct ntvfs_module_context * ntvfs ,
struct ntvfs_request * req , union smb_lock * lck )
{
2009-02-04 10:52:41 +03:00
struct pvfs_state * pvfs = talloc_get_type ( ntvfs - > private_data ,
struct pvfs_state ) ;
2008-02-25 11:51:58 +03:00
struct pvfs_file * f ;
uint8_t oplock_break ;
NTSTATUS status ;
f = pvfs_find_fd ( pvfs , req , lck - > lockx . in . file . ntvfs ) ;
if ( ! f ) {
return NT_STATUS_INVALID_HANDLE ;
}
oplock_break = ( lck - > lockx . in . mode > > 8 ) & 0xFF ;
2008-03-04 16:10:13 +03:00
status = pvfs_oplock_release_internal ( f - > handle , oplock_break ) ;
2008-02-25 11:51:58 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2008-03-04 16:10:13 +03:00
DEBUG ( 0 , ( " %s: failed to release the oplock[0x%02X]: %s \n " ,
__FUNCTION__ , oplock_break , nt_errstr ( status ) ) ) ;
2008-02-25 11:51:58 +03:00
return status ;
}
return NT_STATUS_OK ;
}
2008-02-22 18:34:50 +03:00
NTSTATUS pvfs_break_level2_oplocks ( struct pvfs_file * f )
{
struct pvfs_file_handle * h = f - > handle ;
struct odb_lock * olck ;
NTSTATUS status ;
2008-03-04 16:11:53 +03:00
if ( h - > oplock & & h - > oplock - > level ! = OPLOCK_LEVEL_II ) {
2008-02-22 18:34:50 +03:00
return NT_STATUS_OK ;
}
olck = odb_lock ( h , h - > pvfs - > odb_context , & h - > odb_locking_key ) ;
if ( olck = = NULL ) {
DEBUG ( 0 , ( " Unable to lock opendb for oplock update \n " ) ) ;
return NT_STATUS_FOOBAR ;
}
status = odb_break_oplocks ( olck ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " Unable to break level2 oplocks to none for '%s' - %s \n " ,
h - > name - > full_name , nt_errstr ( status ) ) ) ;
talloc_free ( olck ) ;
return status ;
}
talloc_free ( olck ) ;
return NT_STATUS_OK ;
}