2007-01-31 15:24:59 +03:00
/*
Unix SMB / CIFS implementation .
Copyright ( C ) Andrew Tridgell 2006
2009-04-09 17:51:35 +04:00
2007-01-31 15:24:59 +03: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
2007-07-09 23:25:36 +04:00
the Free Software Foundation ; either version 3 of the License , or
2007-01-31 15:24:59 +03:00
( at your option ) any later version .
2009-04-09 17:51:35 +04:00
2007-01-31 15:24:59 +03:00
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 .
2009-04-09 17:51:35 +04:00
2007-01-31 15:24:59 +03:00
You should have received a copy of the GNU General Public License
2007-07-10 04:52:41 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2007-01-31 15:24:59 +03:00
*/
/*
this is the change notify database . It implements mechanisms for
storing current change notify waiters in a tdb , and checking if a
given event matches any of the stored notify waiiters .
*/
# include "includes.h"
2011-02-26 01:20:06 +03:00
# include "system/filesys.h"
2007-01-31 15:24:59 +03:00
# include "librpc/gen_ndr/ndr_notify.h"
2010-08-18 20:59:23 +04:00
# include "dbwrap.h"
2011-03-22 18:57:01 +03:00
# include "smbd/smbd.h"
2011-03-24 17:31:06 +03:00
# include "messages.h"
2011-05-04 04:28:15 +04:00
# include "lib/util/tdb_wrap.h"
2011-05-05 13:25:29 +04:00
# include "util_tdb.h"
2007-01-31 15:24:59 +03:00
struct notify_context {
2009-04-14 16:56:35 +04:00
struct db_context * db_recursive ;
2009-04-14 22:39:14 +04:00
struct db_context * db_onelevel ;
2007-01-31 15:24:59 +03:00
struct server_id server ;
struct messaging_context * messaging_ctx ;
struct notify_list * list ;
struct notify_array * array ;
int seqnum ;
struct sys_notify_context * sys_notify_ctx ;
2007-05-29 14:50:25 +04:00
TDB_DATA key ;
2007-01-31 15:24:59 +03:00
} ;
struct notify_list {
struct notify_list * next , * prev ;
void * private_data ;
void ( * callback ) ( void * , const struct notify_event * ) ;
void * sys_notify_handle ;
int depth ;
} ;
# define NOTIFY_KEY "notify array"
# define NOTIFY_ENABLE "notify:enable"
# define NOTIFY_ENABLE_DEFAULT True
2007-02-02 14:34:16 +03:00
static NTSTATUS notify_remove_all ( struct notify_context * notify ,
const struct server_id * server ) ;
2007-01-31 15:24:59 +03:00
static void notify_handler ( struct messaging_context * msg_ctx , void * private_data ,
uint32_t msg_type , struct server_id server_id , DATA_BLOB * data ) ;
/*
destroy the notify context
*/
static int notify_destructor ( struct notify_context * notify )
{
messaging_deregister ( notify - > messaging_ctx , MSG_PVFS_NOTIFY , notify ) ;
2007-02-02 14:34:16 +03:00
if ( notify - > list ! = NULL ) {
notify_remove_all ( notify , & notify - > server ) ;
}
2007-01-31 15:24:59 +03:00
return 0 ;
}
/*
Open up the notify . tdb database . You should close it down using
talloc_free ( ) . We need the messaging_ctx to allow for notifications
via internal messages
*/
struct notify_context * notify_init ( TALLOC_CTX * mem_ctx , struct server_id server ,
struct messaging_context * messaging_ctx ,
struct event_context * ev ,
2007-02-01 16:36:02 +03:00
connection_struct * conn )
2007-01-31 15:24:59 +03:00
{
struct notify_context * notify ;
2007-02-01 18:11:06 +03:00
if ( ! lp_change_notify ( conn - > params ) ) {
2007-01-31 15:24:59 +03:00
return NULL ;
}
notify = talloc ( mem_ctx , struct notify_context ) ;
if ( notify = = NULL ) {
return NULL ;
}
2009-04-14 16:56:35 +04:00
notify - > db_recursive = db_open ( notify , lock_path ( " notify.tdb " ) ,
2010-09-27 16:46:07 +04:00
0 , TDB_SEQNUM | TDB_CLEAR_IF_FIRST | TDB_INCOMPATIBLE_HASH ,
2009-04-14 16:56:35 +04:00
O_RDWR | O_CREAT , 0644 ) ;
if ( notify - > db_recursive = = NULL ) {
2007-01-31 15:24:59 +03:00
talloc_free ( notify ) ;
return NULL ;
}
2009-04-14 22:39:14 +04:00
notify - > db_onelevel = db_open ( notify , lock_path ( " notify_onelevel.tdb " ) ,
2010-09-27 16:46:07 +04:00
0 , TDB_CLEAR_IF_FIRST | TDB_INCOMPATIBLE_HASH ,
2009-04-14 22:39:14 +04:00
O_RDWR | O_CREAT , 0644 ) ;
if ( notify - > db_onelevel = = NULL ) {
talloc_free ( notify ) ;
return NULL ;
}
2007-01-31 15:24:59 +03:00
notify - > server = server ;
notify - > messaging_ctx = messaging_ctx ;
notify - > list = NULL ;
notify - > array = NULL ;
2009-04-14 16:56:35 +04:00
notify - > seqnum = notify - > db_recursive - > get_seqnum (
notify - > db_recursive ) ;
2007-05-29 14:50:25 +04:00
notify - > key = string_term_tdb_data ( NOTIFY_KEY ) ;
2007-01-31 15:24:59 +03:00
talloc_set_destructor ( notify , notify_destructor ) ;
/* register with the messaging subsystem for the notify
message type */
messaging_register ( notify - > messaging_ctx , notify ,
MSG_PVFS_NOTIFY , notify_handler ) ;
2007-02-01 16:36:02 +03:00
notify - > sys_notify_ctx = sys_notify_context_create ( conn , notify , ev ) ;
2007-01-31 15:24:59 +03:00
return notify ;
}
2010-09-26 05:34:03 +04:00
bool notify_internal_parent_init ( TALLOC_CTX * mem_ctx )
2010-03-25 18:01:54 +03:00
{
struct tdb_wrap * db1 , * db2 ;
if ( lp_clustering ( ) ) {
return true ;
}
2010-03-25 18:45:02 +03:00
/*
* Open the tdbs in the parent process ( smbd ) so that our
* CLEAR_IF_FIRST optimization in tdb_reopen_all can properly
* work .
*/
2010-09-26 05:34:03 +04:00
db1 = tdb_wrap_open ( mem_ctx , lock_path ( " notify.tdb " ) ,
2010-09-27 16:46:07 +04:00
0 , TDB_SEQNUM | TDB_CLEAR_IF_FIRST | TDB_INCOMPATIBLE_HASH ,
2010-03-25 18:01:54 +03:00
O_RDWR | O_CREAT , 0644 ) ;
if ( db1 = = NULL ) {
DEBUG ( 1 , ( " could not open notify.tdb: %s \n " , strerror ( errno ) ) ) ;
return false ;
}
2010-09-26 05:34:03 +04:00
db2 = tdb_wrap_open ( mem_ctx , lock_path ( " notify_onelevel.tdb " ) ,
2010-09-27 16:46:07 +04:00
0 , TDB_CLEAR_IF_FIRST | TDB_INCOMPATIBLE_HASH , O_RDWR | O_CREAT , 0644 ) ;
2010-03-25 18:01:54 +03:00
if ( db2 = = NULL ) {
DEBUG ( 1 , ( " could not open notify_onelevel.tdb: %s \n " ,
strerror ( errno ) ) ) ;
TALLOC_FREE ( db1 ) ;
return false ;
}
return true ;
}
2007-01-31 15:24:59 +03:00
/*
2007-05-29 14:50:25 +04:00
lock and fetch the record
2007-01-31 15:24:59 +03:00
*/
2007-05-29 14:50:25 +04:00
static NTSTATUS notify_fetch_locked ( struct notify_context * notify , struct db_record * * rec )
2007-01-31 15:24:59 +03:00
{
2009-04-14 16:56:35 +04:00
* rec = notify - > db_recursive - > fetch_locked ( notify - > db_recursive ,
notify , notify - > key ) ;
2007-05-29 14:50:25 +04:00
if ( * rec = = NULL ) {
2007-01-31 15:24:59 +03:00
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
return NT_STATUS_OK ;
}
/*
load the notify array
*/
2007-05-29 14:50:25 +04:00
static NTSTATUS notify_load ( struct notify_context * notify , struct db_record * rec )
2007-01-31 15:24:59 +03:00
{
TDB_DATA dbuf ;
DATA_BLOB blob ;
NTSTATUS status ;
int seqnum ;
2009-04-14 16:56:35 +04:00
seqnum = notify - > db_recursive - > get_seqnum ( notify - > db_recursive ) ;
2007-01-31 15:24:59 +03:00
if ( seqnum = = notify - > seqnum & & notify - > array ! = NULL ) {
return NT_STATUS_OK ;
}
notify - > seqnum = seqnum ;
talloc_free ( notify - > array ) ;
2011-06-07 05:44:43 +04:00
notify - > array = talloc_zero ( notify , struct notify_array ) ;
2007-01-31 15:24:59 +03:00
NT_STATUS_HAVE_NO_MEMORY ( notify - > array ) ;
2007-05-29 14:50:25 +04:00
if ( ! rec ) {
2009-04-14 16:56:35 +04:00
if ( notify - > db_recursive - > fetch ( notify - > db_recursive , notify ,
notify - > key , & dbuf ) ! = 0 ) {
2007-05-29 14:50:25 +04:00
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
} else {
dbuf = rec - > value ;
2007-01-31 15:24:59 +03:00
}
2007-01-31 16:47:37 +03:00
blob . data = ( uint8 * ) dbuf . dptr ;
2007-01-31 15:24:59 +03:00
blob . length = dbuf . dsize ;
2007-11-09 23:20:46 +03:00
status = NT_STATUS_OK ;
if ( blob . length > 0 ) {
2007-11-09 16:39:45 +03:00
enum ndr_err_code ndr_err ;
2010-05-10 02:42:06 +04:00
ndr_err = ndr_pull_struct_blob ( & blob , notify - > array , notify - > array ,
2007-11-09 16:39:45 +03:00
( ndr_pull_flags_fn_t ) ndr_pull_notify_array ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
2010-04-05 16:12:52 +04:00
/* 1. log that we got a corrupt notify_array
* 2. clear the variable the garbage was stored into to not trip
* over it next time this method is entered with the same seqnum
* 3. delete it from the database */
DEBUG ( 2 , ( " notify_array is corrupt, discarding it \n " ) ) ;
ZERO_STRUCTP ( notify - > array ) ;
if ( rec ! = NULL ) {
rec - > delete_rec ( rec ) ;
}
} else {
if ( DEBUGLEVEL > = 10 ) {
DEBUG ( 10 , ( " notify_load: \n " ) ) ;
NDR_PRINT_DEBUG ( notify_array , notify - > array ) ;
}
2007-11-09 16:39:45 +03:00
}
2007-05-29 14:50:25 +04:00
}
2007-02-02 14:34:16 +03:00
2007-05-29 14:50:25 +04:00
if ( ! rec ) {
talloc_free ( dbuf . dptr ) ;
}
2007-01-31 15:24:59 +03:00
return status ;
}
/*
compare notify entries for sorting
*/
2010-02-14 02:03:12 +03:00
static int notify_compare ( const struct notify_entry * e1 , const struct notify_entry * e2 )
2007-01-31 15:24:59 +03:00
{
return strcmp ( e1 - > path , e2 - > path ) ;
}
/*
save the notify array
*/
2007-05-29 14:50:25 +04:00
static NTSTATUS notify_save ( struct notify_context * notify , struct db_record * rec )
2007-01-31 15:24:59 +03:00
{
TDB_DATA dbuf ;
DATA_BLOB blob ;
NTSTATUS status ;
2007-11-09 16:39:45 +03:00
enum ndr_err_code ndr_err ;
2007-01-31 15:24:59 +03:00
TALLOC_CTX * tmp_ctx ;
/* if possible, remove some depth arrays */
while ( notify - > array - > num_depths > 0 & &
notify - > array - > depth [ notify - > array - > num_depths - 1 ] . num_entries = = 0 ) {
notify - > array - > num_depths - - ;
}
/* we might just be able to delete the record */
if ( notify - > array - > num_depths = = 0 ) {
2007-05-29 14:50:25 +04:00
return rec - > delete_rec ( rec ) ;
2007-01-31 15:24:59 +03:00
}
tmp_ctx = talloc_new ( notify ) ;
NT_STATUS_HAVE_NO_MEMORY ( tmp_ctx ) ;
2010-05-10 02:42:06 +04:00
ndr_err = ndr_push_struct_blob ( & blob , tmp_ctx , notify - > array ,
2007-01-31 15:24:59 +03:00
( ndr_push_flags_fn_t ) ndr_push_notify_array ) ;
2007-11-09 16:39:45 +03:00
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
2007-01-31 15:24:59 +03:00
talloc_free ( tmp_ctx ) ;
2007-11-09 16:39:45 +03:00
return ndr_map_error2ntstatus ( ndr_err ) ;
2007-01-31 15:24:59 +03:00
}
2007-02-06 20:28:03 +03:00
if ( DEBUGLEVEL > = 10 ) {
DEBUG ( 10 , ( " notify_save: \n " ) ) ;
NDR_PRINT_DEBUG ( notify_array , notify - > array ) ;
}
2007-03-29 13:35:51 +04:00
dbuf . dptr = blob . data ;
2007-01-31 15:24:59 +03:00
dbuf . dsize = blob . length ;
2007-05-29 14:50:25 +04:00
status = rec - > store ( rec , dbuf , TDB_REPLACE ) ;
2007-01-31 15:24:59 +03:00
talloc_free ( tmp_ctx ) ;
2007-05-29 14:50:25 +04:00
return status ;
2007-01-31 15:24:59 +03:00
}
/*
handle incoming notify messages
*/
static void notify_handler ( struct messaging_context * msg_ctx , void * private_data ,
uint32_t msg_type , struct server_id server_id , DATA_BLOB * data )
{
struct notify_context * notify = talloc_get_type ( private_data , struct notify_context ) ;
2007-11-09 16:39:45 +03:00
enum ndr_err_code ndr_err ;
2007-01-31 15:24:59 +03:00
struct notify_event ev ;
TALLOC_CTX * tmp_ctx = talloc_new ( notify ) ;
struct notify_list * listel ;
if ( tmp_ctx = = NULL ) {
return ;
}
2010-05-10 02:42:06 +04:00
ndr_err = ndr_pull_struct_blob ( data , tmp_ctx , & ev ,
2007-11-09 16:39:45 +03:00
( ndr_pull_flags_fn_t ) ndr_pull_notify_event ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
2007-01-31 15:24:59 +03:00
talloc_free ( tmp_ctx ) ;
return ;
}
for ( listel = notify - > list ; listel ; listel = listel - > next ) {
if ( listel - > private_data = = ev . private_data ) {
listel - > callback ( listel - > private_data , & ev ) ;
break ;
}
}
talloc_free ( tmp_ctx ) ;
}
/*
callback from sys_notify telling us about changes from the OS
*/
static void sys_notify_callback ( struct sys_notify_context * ctx ,
void * ptr , struct notify_event * ev )
{
struct notify_list * listel = talloc_get_type ( ptr , struct notify_list ) ;
ev - > private_data = listel ;
2007-01-31 16:47:37 +03:00
DEBUG ( 10 , ( " sys_notify_callback called with action=%d, for %s \n " ,
ev - > action , ev - > path ) ) ;
2007-01-31 15:24:59 +03:00
listel - > callback ( listel - > private_data , ev ) ;
}
/*
add an entry to the notify array
*/
2007-05-29 14:50:25 +04:00
static NTSTATUS notify_add_array ( struct notify_context * notify , struct db_record * rec ,
struct notify_entry * e ,
2007-01-31 15:24:59 +03:00
void * private_data , int depth )
{
int i ;
struct notify_depth * d ;
struct notify_entry * ee ;
/* possibly expand the depths array */
if ( depth > = notify - > array - > num_depths ) {
d = talloc_realloc ( notify - > array , notify - > array - > depth ,
struct notify_depth , depth + 1 ) ;
NT_STATUS_HAVE_NO_MEMORY ( d ) ;
for ( i = notify - > array - > num_depths ; i < = depth ; i + + ) {
ZERO_STRUCT ( d [ i ] ) ;
}
notify - > array - > depth = d ;
notify - > array - > num_depths = depth + 1 ;
}
d = & notify - > array - > depth [ depth ] ;
/* expand the entries array */
ee = talloc_realloc ( notify - > array - > depth , d - > entries , struct notify_entry ,
d - > num_entries + 1 ) ;
NT_STATUS_HAVE_NO_MEMORY ( ee ) ;
d - > entries = ee ;
d - > entries [ d - > num_entries ] = * e ;
d - > entries [ d - > num_entries ] . private_data = private_data ;
d - > entries [ d - > num_entries ] . server = notify - > server ;
d - > entries [ d - > num_entries ] . path_len = strlen ( e - > path ) ;
d - > num_entries + + ;
d - > max_mask | = e - > filter ;
d - > max_mask_subdir | = e - > subdir_filter ;
2010-02-14 02:03:12 +03:00
TYPESAFE_QSORT ( d - > entries , d - > num_entries , notify_compare ) ;
2007-01-31 15:24:59 +03:00
/* recalculate the maximum masks */
d - > max_mask = 0 ;
d - > max_mask_subdir = 0 ;
for ( i = 0 ; i < d - > num_entries ; i + + ) {
d - > max_mask | = d - > entries [ i ] . filter ;
d - > max_mask_subdir | = d - > entries [ i ] . subdir_filter ;
}
2007-05-29 14:50:25 +04:00
return notify_save ( notify , rec ) ;
2007-01-31 15:24:59 +03:00
}
2009-04-14 22:39:14 +04:00
/*
Add a non - recursive watch
*/
static void notify_add_onelevel ( struct notify_context * notify ,
struct notify_entry * e , void * private_data )
{
struct notify_entry_array * array ;
struct db_record * rec ;
DATA_BLOB blob ;
TDB_DATA dbuf ;
enum ndr_err_code ndr_err ;
NTSTATUS status ;
array = talloc_zero ( talloc_tos ( ) , struct notify_entry_array ) ;
if ( array = = NULL ) {
return ;
}
rec = notify - > db_onelevel - > fetch_locked (
notify - > db_onelevel , talloc_tos ( ) ,
make_tdb_data ( ( uint8_t * ) & e - > dir_id , sizeof ( e - > dir_id ) ) ) ;
if ( rec = = NULL ) {
DEBUG ( 10 , ( " notify_add_onelevel: fetch_locked for %s failed "
" \n " , file_id_string_tos ( & e - > dir_id ) ) ) ;
TALLOC_FREE ( array ) ;
return ;
}
blob . data = ( uint8_t * ) rec - > value . dptr ;
blob . length = rec - > value . dsize ;
if ( blob . length > 0 ) {
2010-05-10 02:42:06 +04:00
ndr_err = ndr_pull_struct_blob ( & blob , array , array ,
2009-04-14 22:39:14 +04:00
( ndr_pull_flags_fn_t ) ndr_pull_notify_entry_array ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DEBUG ( 10 , ( " ndr_pull_notify_entry_array failed: %s \n " ,
ndr_errstr ( ndr_err ) ) ) ;
TALLOC_FREE ( array ) ;
return ;
}
if ( DEBUGLEVEL > = 10 ) {
DEBUG ( 10 , ( " notify_add_onelevel: \n " ) ) ;
NDR_PRINT_DEBUG ( notify_entry_array , array ) ;
}
}
array - > entries = talloc_realloc ( array , array - > entries ,
struct notify_entry ,
array - > num_entries + 1 ) ;
if ( array - > entries = = NULL ) {
TALLOC_FREE ( array ) ;
return ;
}
array - > entries [ array - > num_entries ] = * e ;
array - > entries [ array - > num_entries ] . private_data = private_data ;
array - > entries [ array - > num_entries ] . server = notify - > server ;
array - > num_entries + = 1 ;
2010-05-10 02:42:06 +04:00
ndr_err = ndr_push_struct_blob ( & blob , rec , array ,
2009-04-14 22:39:14 +04:00
( ndr_push_flags_fn_t ) ndr_push_notify_entry_array ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DEBUG ( 10 , ( " ndr_push_notify_entry_array failed: %s \n " ,
ndr_errstr ( ndr_err ) ) ) ;
TALLOC_FREE ( array ) ;
return ;
}
if ( DEBUGLEVEL > = 10 ) {
DEBUG ( 10 , ( " notify_add_onelevel: \n " ) ) ;
NDR_PRINT_DEBUG ( notify_entry_array , array ) ;
}
dbuf . dptr = blob . data ;
dbuf . dsize = blob . length ;
status = rec - > store ( rec , dbuf , TDB_REPLACE ) ;
TALLOC_FREE ( array ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " notify_add_onelevel: store failed: %s \n " ,
nt_errstr ( status ) ) ) ;
return ;
}
e - > filter = 0 ;
return ;
}
2007-01-31 15:24:59 +03:00
/*
add a notify watch . This is called when a notify is first setup on a open
directory handle .
*/
NTSTATUS notify_add ( struct notify_context * notify , struct notify_entry * e0 ,
void ( * callback ) ( void * , const struct notify_event * ) ,
void * private_data )
{
struct notify_entry e = * e0 ;
NTSTATUS status ;
char * tmp_path = NULL ;
struct notify_list * listel ;
size_t len ;
int depth ;
2007-05-29 14:50:25 +04:00
struct db_record * rec ;
2007-01-31 15:24:59 +03:00
/* see if change notify is enabled at all */
if ( notify = = NULL ) {
return NT_STATUS_NOT_IMPLEMENTED ;
}
2007-05-29 14:50:25 +04:00
status = notify_fetch_locked ( notify , & rec ) ;
2007-01-31 15:24:59 +03:00
NT_STATUS_NOT_OK_RETURN ( status ) ;
2007-05-29 14:50:25 +04:00
status = notify_load ( notify , rec ) ;
2007-01-31 15:24:59 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2007-05-29 14:50:25 +04:00
talloc_free ( rec ) ;
return status ;
2007-01-31 15:24:59 +03:00
}
/* cope with /. on the end of the path */
len = strlen ( e . path ) ;
if ( len > 1 & & e . path [ len - 1 ] = = ' . ' & & e . path [ len - 2 ] = = ' / ' ) {
tmp_path = talloc_strndup ( notify , e . path , len - 2 ) ;
if ( tmp_path = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto done ;
}
e . path = tmp_path ;
}
depth = count_chars ( e . path , ' / ' ) ;
2011-06-07 05:44:43 +04:00
listel = talloc_zero ( notify , struct notify_list ) ;
2007-01-31 15:24:59 +03:00
if ( listel = = NULL ) {
status = NT_STATUS_NO_MEMORY ;
goto done ;
}
listel - > private_data = private_data ;
listel - > callback = callback ;
listel - > depth = depth ;
DLIST_ADD ( notify - > list , listel ) ;
/* ignore failures from sys_notify */
if ( notify - > sys_notify_ctx ! = NULL ) {
/*
this call will modify e . filter and e . subdir_filter
to remove bits handled by the backend
*/
status = sys_notify_watch ( notify - > sys_notify_ctx , & e ,
sys_notify_callback , listel ,
& listel - > sys_notify_handle ) ;
if ( NT_STATUS_IS_OK ( status ) ) {
talloc_steal ( listel , listel - > sys_notify_handle ) ;
}
}
2009-04-14 22:39:14 +04:00
if ( e . filter ! = 0 ) {
notify_add_onelevel ( notify , & e , private_data ) ;
status = NT_STATUS_OK ;
}
2007-01-31 15:24:59 +03:00
/* if the system notify handler couldn't handle some of the
filter bits , or couldn ' t handle a request for recursion
then we need to install it in the array used for the
intra - samba notify handling */
if ( e . filter ! = 0 | | e . subdir_filter ! = 0 ) {
2007-05-29 14:50:25 +04:00
status = notify_add_array ( notify , rec , & e , private_data , depth ) ;
2007-01-31 15:24:59 +03:00
}
done :
2007-05-29 14:50:25 +04:00
talloc_free ( rec ) ;
2007-01-31 15:24:59 +03:00
talloc_free ( tmp_path ) ;
return status ;
}
2009-04-14 22:39:14 +04:00
NTSTATUS notify_remove_onelevel ( struct notify_context * notify ,
const struct file_id * fid ,
void * private_data )
{
struct notify_entry_array * array ;
struct db_record * rec ;
DATA_BLOB blob ;
TDB_DATA dbuf ;
enum ndr_err_code ndr_err ;
NTSTATUS status ;
int i ;
2009-11-05 17:06:21 +03:00
if ( notify = = NULL ) {
return NT_STATUS_NOT_IMPLEMENTED ;
}
2009-04-14 22:39:14 +04:00
array = talloc_zero ( talloc_tos ( ) , struct notify_entry_array ) ;
if ( array = = NULL ) {
return NT_STATUS_NO_MEMORY ;
}
rec = notify - > db_onelevel - > fetch_locked (
2009-11-05 16:29:05 +03:00
notify - > db_onelevel , array ,
2011-05-06 01:22:11 +04:00
make_tdb_data ( ( const uint8_t * ) fid , sizeof ( * fid ) ) ) ;
2009-04-14 22:39:14 +04:00
if ( rec = = NULL ) {
DEBUG ( 10 , ( " notify_remove_onelevel: fetch_locked for %s failed "
" \n " , file_id_string_tos ( fid ) ) ) ;
TALLOC_FREE ( array ) ;
return NT_STATUS_INTERNAL_DB_CORRUPTION ;
}
blob . data = ( uint8_t * ) rec - > value . dptr ;
blob . length = rec - > value . dsize ;
if ( blob . length > 0 ) {
2010-05-10 02:42:06 +04:00
ndr_err = ndr_pull_struct_blob ( & blob , array , array ,
2009-04-14 22:39:14 +04:00
( ndr_pull_flags_fn_t ) ndr_pull_notify_entry_array ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DEBUG ( 10 , ( " ndr_pull_notify_entry_array failed: %s \n " ,
ndr_errstr ( ndr_err ) ) ) ;
TALLOC_FREE ( array ) ;
return ndr_map_error2ntstatus ( ndr_err ) ;
}
if ( DEBUGLEVEL > = 10 ) {
DEBUG ( 10 , ( " notify_remove_onelevel: \n " ) ) ;
NDR_PRINT_DEBUG ( notify_entry_array , array ) ;
}
}
for ( i = 0 ; i < array - > num_entries ; i + + ) {
if ( ( private_data = = array - > entries [ i ] . private_data ) & &
cluster_id_equal ( & notify - > server ,
& array - > entries [ i ] . server ) ) {
break ;
}
}
if ( i = = array - > num_entries ) {
TALLOC_FREE ( array ) ;
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
array - > entries [ i ] = array - > entries [ array - > num_entries - 1 ] ;
array - > num_entries - = 1 ;
if ( array - > num_entries = = 0 ) {
rec - > delete_rec ( rec ) ;
TALLOC_FREE ( array ) ;
return NT_STATUS_OK ;
}
2010-05-10 02:42:06 +04:00
ndr_err = ndr_push_struct_blob ( & blob , rec , array ,
2009-04-14 22:39:14 +04:00
( ndr_push_flags_fn_t ) ndr_push_notify_entry_array ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DEBUG ( 10 , ( " ndr_push_notify_entry_array failed: %s \n " ,
ndr_errstr ( ndr_err ) ) ) ;
TALLOC_FREE ( array ) ;
return ndr_map_error2ntstatus ( ndr_err ) ;
}
if ( DEBUGLEVEL > = 10 ) {
DEBUG ( 10 , ( " notify_add_onelevel: \n " ) ) ;
NDR_PRINT_DEBUG ( notify_entry_array , array ) ;
}
dbuf . dptr = blob . data ;
dbuf . dsize = blob . length ;
status = rec - > store ( rec , dbuf , TDB_REPLACE ) ;
TALLOC_FREE ( array ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 10 , ( " notify_add_onelevel: store failed: %s \n " ,
nt_errstr ( status ) ) ) ;
return status ;
}
return NT_STATUS_OK ;
}
2007-01-31 15:24:59 +03:00
/*
remove a notify watch . Called when the directory handle is closed
*/
NTSTATUS notify_remove ( struct notify_context * notify , void * private_data )
{
NTSTATUS status ;
struct notify_list * listel ;
int i , depth ;
struct notify_depth * d ;
2007-05-29 14:50:25 +04:00
struct db_record * rec ;
2007-01-31 15:24:59 +03:00
/* see if change notify is enabled at all */
if ( notify = = NULL ) {
return NT_STATUS_NOT_IMPLEMENTED ;
}
for ( listel = notify - > list ; listel ; listel = listel - > next ) {
if ( listel - > private_data = = private_data ) {
DLIST_REMOVE ( notify - > list , listel ) ;
break ;
}
}
if ( listel = = NULL ) {
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
depth = listel - > depth ;
talloc_free ( listel ) ;
2007-05-29 14:50:25 +04:00
status = notify_fetch_locked ( notify , & rec ) ;
2007-01-31 15:24:59 +03:00
NT_STATUS_NOT_OK_RETURN ( status ) ;
2007-05-29 14:50:25 +04:00
status = notify_load ( notify , rec ) ;
2007-01-31 15:24:59 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2007-05-29 14:50:25 +04:00
talloc_free ( rec ) ;
2007-01-31 15:24:59 +03:00
return status ;
}
if ( depth > = notify - > array - > num_depths ) {
2007-05-29 14:50:25 +04:00
talloc_free ( rec ) ;
2007-01-31 15:24:59 +03:00
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
/* we only have to search at the depth of this element */
d = & notify - > array - > depth [ depth ] ;
for ( i = 0 ; i < d - > num_entries ; i + + ) {
if ( private_data = = d - > entries [ i ] . private_data & &
cluster_id_equal ( & notify - > server , & d - > entries [ i ] . server ) ) {
break ;
}
}
if ( i = = d - > num_entries ) {
2007-05-29 14:50:25 +04:00
talloc_free ( rec ) ;
2007-01-31 15:24:59 +03:00
return NT_STATUS_OBJECT_NAME_NOT_FOUND ;
}
if ( i < d - > num_entries - 1 ) {
memmove ( & d - > entries [ i ] , & d - > entries [ i + 1 ] ,
sizeof ( d - > entries [ i ] ) * ( d - > num_entries - ( i + 1 ) ) ) ;
}
d - > num_entries - - ;
2007-05-29 14:50:25 +04:00
status = notify_save ( notify , rec ) ;
2007-01-31 15:24:59 +03:00
2007-05-29 14:50:25 +04:00
talloc_free ( rec ) ;
2007-01-31 15:24:59 +03:00
return status ;
}
/*
2007-02-02 14:34:16 +03:00
remove all notify watches for a messaging server
2007-01-31 15:24:59 +03:00
*/
2007-02-02 14:34:16 +03:00
static NTSTATUS notify_remove_all ( struct notify_context * notify ,
const struct server_id * server )
2007-01-31 15:24:59 +03:00
{
NTSTATUS status ;
int i , depth , del_count = 0 ;
2007-05-29 14:50:25 +04:00
struct db_record * rec ;
2007-01-31 15:24:59 +03:00
2007-05-29 14:50:25 +04:00
status = notify_fetch_locked ( notify , & rec ) ;
2007-01-31 15:24:59 +03:00
NT_STATUS_NOT_OK_RETURN ( status ) ;
2007-05-29 14:50:25 +04:00
status = notify_load ( notify , rec ) ;
2007-01-31 15:24:59 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
2007-05-29 14:50:25 +04:00
talloc_free ( rec ) ;
2007-01-31 15:24:59 +03:00
return status ;
}
/* we have to search for all entries across all depths, looking for matches
2007-02-02 14:34:16 +03:00
for the server id */
2007-01-31 15:24:59 +03:00
for ( depth = 0 ; depth < notify - > array - > num_depths ; depth + + ) {
struct notify_depth * d = & notify - > array - > depth [ depth ] ;
for ( i = 0 ; i < d - > num_entries ; i + + ) {
2007-02-02 14:34:16 +03:00
if ( cluster_id_equal ( server , & d - > entries [ i ] . server ) ) {
2007-01-31 15:24:59 +03:00
if ( i < d - > num_entries - 1 ) {
memmove ( & d - > entries [ i ] , & d - > entries [ i + 1 ] ,
sizeof ( d - > entries [ i ] ) * ( d - > num_entries - ( i + 1 ) ) ) ;
}
i - - ;
d - > num_entries - - ;
del_count + + ;
}
}
}
if ( del_count > 0 ) {
2007-05-29 14:50:25 +04:00
status = notify_save ( notify , rec ) ;
2007-01-31 15:24:59 +03:00
}
2007-05-29 14:50:25 +04:00
talloc_free ( rec ) ;
2007-01-31 15:24:59 +03:00
return status ;
}
/*
send a notify message to another messaging server
*/
2007-02-02 14:34:16 +03:00
static NTSTATUS notify_send ( struct notify_context * notify , struct notify_entry * e ,
const char * path , uint32_t action )
2007-01-31 15:24:59 +03:00
{
struct notify_event ev ;
DATA_BLOB data ;
NTSTATUS status ;
2007-11-09 16:39:45 +03:00
enum ndr_err_code ndr_err ;
2007-01-31 15:24:59 +03:00
TALLOC_CTX * tmp_ctx ;
ev . action = action ;
ev . path = path ;
ev . private_data = e - > private_data ;
tmp_ctx = talloc_new ( notify ) ;
2010-05-10 02:42:06 +04:00
ndr_err = ndr_push_struct_blob ( & data , tmp_ctx , & ev ,
2007-11-09 16:39:45 +03:00
( ndr_push_flags_fn_t ) ndr_push_notify_event ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
2007-01-31 15:24:59 +03:00
talloc_free ( tmp_ctx ) ;
2007-11-09 16:39:45 +03:00
return ndr_map_error2ntstatus ( ndr_err ) ;
2007-01-31 15:24:59 +03:00
}
status = messaging_send ( notify - > messaging_ctx , e - > server ,
MSG_PVFS_NOTIFY , & data ) ;
talloc_free ( tmp_ctx ) ;
2007-02-02 14:34:16 +03:00
return status ;
2007-01-31 15:24:59 +03:00
}
2009-04-14 22:39:14 +04:00
void notify_onelevel ( struct notify_context * notify , uint32_t action ,
uint32_t filter , struct file_id fid , const char * name )
{
struct notify_entry_array * array ;
TDB_DATA dbuf ;
DATA_BLOB blob ;
bool have_dead_entries = false ;
int i ;
2009-04-30 14:24:51 +04:00
if ( notify = = NULL ) {
return ;
}
2009-04-14 22:39:14 +04:00
array = talloc_zero ( talloc_tos ( ) , struct notify_entry_array ) ;
if ( array = = NULL ) {
return ;
}
if ( notify - > db_onelevel - > fetch (
notify - > db_onelevel , array ,
make_tdb_data ( ( uint8_t * ) & fid , sizeof ( fid ) ) ,
2011-06-20 13:10:33 +04:00
& dbuf ) ! = 0 ) {
2009-04-14 22:39:14 +04:00
TALLOC_FREE ( array ) ;
return ;
}
blob . data = ( uint8 * ) dbuf . dptr ;
blob . length = dbuf . dsize ;
if ( blob . length > 0 ) {
enum ndr_err_code ndr_err ;
2010-05-10 02:42:06 +04:00
ndr_err = ndr_pull_struct_blob ( & blob , array , array ,
2009-04-14 22:39:14 +04:00
( ndr_pull_flags_fn_t ) ndr_pull_notify_entry_array ) ;
if ( ! NDR_ERR_CODE_IS_SUCCESS ( ndr_err ) ) {
DEBUG ( 10 , ( " ndr_pull_notify_entry_array failed: %s \n " ,
ndr_errstr ( ndr_err ) ) ) ;
TALLOC_FREE ( array ) ;
return ;
}
if ( DEBUGLEVEL > = 10 ) {
DEBUG ( 10 , ( " notify_onelevel: \n " ) ) ;
NDR_PRINT_DEBUG ( notify_entry_array , array ) ;
}
}
for ( i = 0 ; i < array - > num_entries ; i + + ) {
struct notify_entry * e = & array - > entries [ i ] ;
if ( ( e - > filter & filter ) ! = 0 ) {
NTSTATUS status ;
status = notify_send ( notify , e , name , action ) ;
if ( NT_STATUS_EQUAL (
status , NT_STATUS_INVALID_HANDLE ) ) {
/*
* Mark the entry as dead . All entries have a
* path set . The marker used here is setting
* that to NULL .
*/
e - > path = NULL ;
have_dead_entries = true ;
}
}
}
if ( ! have_dead_entries ) {
TALLOC_FREE ( array ) ;
return ;
}
for ( i = 0 ; i < array - > num_entries ; i + + ) {
struct notify_entry * e = & array - > entries [ i ] ;
if ( e - > path ! = NULL ) {
continue ;
}
DEBUG ( 10 , ( " Deleting notify entries for process %s because "
" it's gone \n " , procid_str_static ( & e - > server ) ) ) ;
/*
* Potential TODO : This might need optimizing ,
* notify_remove_onelevel ( ) does a fetch_locked ( ) operation at
* every call . But this would only matter if a process with
* MANY notifies has died without shutting down properly .
*/
notify_remove_onelevel ( notify , & e - > dir_id , e - > private_data ) ;
}
TALLOC_FREE ( array ) ;
return ;
}
2007-01-31 15:24:59 +03:00
/*
trigger a notify message for anyone waiting on a matching event
This function is called a lot , and needs to be very fast . The unusual data structure
and traversal is designed to be fast in the average case , even for large numbers of
notifies
*/
void notify_trigger ( struct notify_context * notify ,
uint32_t action , uint32_t filter , const char * path )
{
NTSTATUS status ;
int depth ;
const char * p , * next_p ;
2007-01-31 16:47:37 +03:00
DEBUG ( 10 , ( " notify_trigger called action=0x%x, filter=0x%x, "
" path=%s \n " , ( unsigned ) action , ( unsigned ) filter , path ) ) ;
2007-01-31 15:24:59 +03:00
/* see if change notify is enabled at all */
if ( notify = = NULL ) {
return ;
}
2007-02-02 14:34:16 +03:00
again :
2007-05-29 14:50:25 +04:00
status = notify_load ( notify , NULL ) ;
2007-01-31 15:24:59 +03:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
return ;
}
/* loop along the given path, working with each directory depth separately */
for ( depth = 0 , p = path ;
p & & depth < notify - > array - > num_depths ;
p = next_p , depth + + ) {
int p_len = p - path ;
int min_i , max_i , i ;
struct notify_depth * d = & notify - > array - > depth [ depth ] ;
next_p = strchr ( p + 1 , ' / ' ) ;
/* see if there are any entries at this depth */
if ( d - > num_entries = = 0 ) continue ;
2009-04-09 17:51:35 +04:00
2007-01-31 15:24:59 +03:00
/* try to skip based on the maximum mask. If next_p is
NULL then we know it will be a ' this directory '
match , otherwise it must be a subdir match */
if ( next_p ! = NULL ) {
if ( 0 = = ( filter & d - > max_mask_subdir ) ) {
continue ;
}
} else {
if ( 0 = = ( filter & d - > max_mask ) ) {
continue ;
}
}
/* we know there is an entry here worth looking
for . Use a bisection search to find the first entry
with a matching path */
min_i = 0 ;
max_i = d - > num_entries - 1 ;
while ( min_i < max_i ) {
struct notify_entry * e ;
int cmp ;
i = ( min_i + max_i ) / 2 ;
e = & d - > entries [ i ] ;
cmp = strncmp ( path , e - > path , p_len ) ;
if ( cmp = = 0 ) {
if ( p_len = = e - > path_len ) {
max_i = i ;
} else {
max_i = i - 1 ;
}
} else if ( cmp < 0 ) {
max_i = i - 1 ;
} else {
min_i = i + 1 ;
}
}
if ( min_i ! = max_i ) {
/* none match */
continue ;
}
/* we now know that the entries start at min_i */
for ( i = min_i ; i < d - > num_entries ; i + + ) {
struct notify_entry * e = & d - > entries [ i ] ;
if ( p_len ! = e - > path_len | |
strncmp ( path , e - > path , p_len ) ! = 0 ) break ;
if ( next_p ! = NULL ) {
if ( 0 = = ( filter & e - > subdir_filter ) ) {
continue ;
}
} else {
if ( 0 = = ( filter & e - > filter ) ) {
continue ;
}
}
2007-02-02 14:34:16 +03:00
status = notify_send ( notify , e , path + e - > path_len + 1 ,
action ) ;
if ( NT_STATUS_EQUAL (
status , NT_STATUS_INVALID_HANDLE ) ) {
2007-02-06 20:28:03 +03:00
struct server_id server = e - > server ;
2007-02-02 14:34:16 +03:00
DEBUG ( 10 , ( " Deleting notify entries for "
" process %s because it's gone \n " ,
2007-05-07 13:35:35 +04:00
procid_str_static ( & e - > server ) ) ) ;
2007-02-06 20:28:03 +03:00
notify_remove_all ( notify , & server ) ;
2007-02-02 14:34:16 +03:00
goto again ;
}
2007-01-31 15:24:59 +03:00
}
}
}