2006-03-21 14:47:24 +03:00
/*
Unix SMB / CIFS implementation .
POSIX NTVFS backend - notify
Copyright ( C ) Andrew Tridgell 2006
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-10 06:07:03 +04:00
the Free Software Foundation ; either version 3 of the License , or
2006-03-21 14:47:24 +03:00
( 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
2007-07-10 06:07:03 +04:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2006-03-21 14:47:24 +03:00
*/
# include "includes.h"
# include "vfs_posix.h"
# include "lib/messaging/irpc.h"
# include "messaging/messaging.h"
2008-10-11 23:31:42 +04:00
# include "../lib/util/dlinklist.h"
2006-05-23 07:52:57 +04:00
# include "lib/events/events.h"
2006-03-21 14:47:24 +03:00
/* pending notifies buffer, hung off struct pvfs_file for open directories
that have used change notify */
struct pvfs_notify_buffer {
struct pvfs_file * f ;
uint32_t num_changes ;
struct notify_changes * changes ;
uint32_t max_buffer_size ;
uint32_t current_buffer_size ;
2009-06-05 10:25:44 +04:00
bool overflowed ;
2006-03-30 06:25:38 +04:00
/* a list of requests waiting for events on this handle */
struct notify_pending {
struct notify_pending * next , * prev ;
struct ntvfs_request * req ;
2006-07-12 18:25:50 +04:00
union smb_notify * info ;
2006-03-30 06:25:38 +04:00
} * pending ;
2006-03-21 14:47:24 +03:00
} ;
2006-05-23 07:52:57 +04:00
/*
send a notify on the next event run .
*/
2008-12-29 22:24:57 +03:00
static void pvfs_notify_send_next ( struct tevent_context * ev , struct tevent_timer * te ,
2006-07-17 15:15:10 +04:00
struct timeval t , void * ptr )
2006-05-23 07:52:57 +04:00
{
struct ntvfs_request * req = talloc_get_type ( ptr , struct ntvfs_request ) ;
req - > async_states - > send_fn ( req ) ;
}
2006-03-29 17:31:30 +04:00
/*
send a reply to a pending notify request
*/
2006-05-23 07:52:57 +04:00
static void pvfs_notify_send ( struct pvfs_notify_buffer * notify_buffer ,
2007-10-07 02:28:14 +04:00
NTSTATUS status , bool immediate )
2006-03-29 17:31:30 +04:00
{
2006-03-30 06:25:38 +04:00
struct notify_pending * pending = notify_buffer - > pending ;
struct ntvfs_request * req ;
2006-07-12 18:25:50 +04:00
union smb_notify * info ;
2006-03-30 06:25:38 +04:00
2006-03-30 07:14:38 +04:00
if ( notify_buffer - > current_buffer_size > notify_buffer - > max_buffer_size & &
notify_buffer - > num_changes ! = 0 ) {
/* on buffer overflow return no changes and destroys the notify buffer */
notify_buffer - > num_changes = 0 ;
while ( notify_buffer - > pending ) {
2006-05-23 07:52:57 +04:00
pvfs_notify_send ( notify_buffer , NT_STATUS_OK , immediate ) ;
2006-03-30 07:14:38 +04:00
}
2009-06-05 10:25:44 +04:00
notify_buffer - > overflowed = true ;
2006-03-30 07:14:38 +04:00
return ;
}
/* see if there is anyone waiting */
if ( notify_buffer - > pending = = NULL ) {
return ;
}
2006-03-30 06:25:38 +04:00
DLIST_REMOVE ( notify_buffer - > pending , pending ) ;
req = pending - > req ;
info = pending - > info ;
2006-03-29 17:31:30 +04:00
2006-07-12 18:25:50 +04:00
info - > nttrans . out . num_changes = notify_buffer - > num_changes ;
info - > nttrans . out . changes = talloc_steal ( req , notify_buffer - > changes ) ;
2006-03-29 17:31:30 +04:00
notify_buffer - > num_changes = 0 ;
2009-06-05 10:25:44 +04:00
notify_buffer - > overflowed = false ;
2006-03-29 17:31:30 +04:00
notify_buffer - > changes = NULL ;
notify_buffer - > current_buffer_size = 0 ;
2006-03-30 06:25:38 +04:00
talloc_free ( pending ) ;
2006-03-29 17:31:30 +04:00
2006-07-12 18:25:50 +04:00
if ( info - > nttrans . out . num_changes ! = 0 ) {
2006-03-30 06:06:06 +04:00
status = NT_STATUS_OK ;
2006-03-29 17:31:30 +04:00
}
2006-03-30 06:06:06 +04:00
req - > async_states - > status = status ;
2006-05-23 07:52:57 +04:00
if ( immediate ) {
req - > async_states - > send_fn ( req ) ;
return ;
}
/* we can't call pvfs_notify_send() directly here, as that
would free the request , and the ntvfs modules above us
could use it , so call it on the next event */
2010-05-25 23:27:41 +04:00
tevent_add_timer ( req - > ctx - > event_ctx ,
2006-05-23 07:52:57 +04:00
req , timeval_zero ( ) , pvfs_notify_send_next , req ) ;
2006-03-29 17:31:30 +04:00
}
2006-03-30 06:06:06 +04:00
/*
destroy a notify buffer . Called when the handle is closed
*/
2006-05-24 11:34:11 +04:00
static int pvfs_notify_destructor ( struct pvfs_notify_buffer * n )
2006-03-30 06:06:06 +04:00
{
notify_remove ( n - > f - > pvfs - > notify_context , n ) ;
n - > f - > notify_buffer = NULL ;
2007-10-07 02:28:14 +04:00
pvfs_notify_send ( n , NT_STATUS_OK , true ) ;
2006-03-30 06:06:06 +04:00
return 0 ;
}
2006-03-29 17:31:30 +04:00
2006-03-21 14:47:24 +03:00
/*
called when a async notify event comes in
*/
2009-02-02 10:34:16 +03:00
static void pvfs_notify_callback ( void * private_data , const struct notify_event * ev )
2006-03-21 14:47:24 +03:00
{
2009-02-02 10:34:16 +03:00
struct pvfs_notify_buffer * n = talloc_get_type ( private_data , struct pvfs_notify_buffer ) ;
2006-03-30 07:14:38 +04:00
size_t len ;
2006-04-07 16:32:51 +04:00
struct notify_changes * n2 ;
2006-04-07 16:11:50 +04:00
char * new_path ;
2006-03-29 17:31:30 +04:00
2009-06-05 10:25:44 +04:00
if ( n - > overflowed ) {
return ;
}
2006-04-07 16:32:51 +04:00
n2 = talloc_realloc ( n , n - > changes , struct notify_changes , n - > num_changes + 1 ) ;
if ( n2 = = NULL ) {
/* nothing much we can do for this */
return ;
}
n - > changes = n2 ;
2006-04-07 16:11:50 +04:00
new_path = talloc_strdup ( n - > changes , ev - > path ) ;
2006-04-07 16:32:51 +04:00
if ( new_path = = NULL ) {
return ;
}
2006-04-07 16:11:50 +04:00
string_replace ( new_path , ' / ' , ' \\ ' ) ;
2006-04-07 16:32:51 +04:00
n - > changes [ n - > num_changes ] . action = ev - > action ;
2006-04-07 16:11:50 +04:00
n - > changes [ n - > num_changes ] . name . s = new_path ;
2006-03-29 17:31:30 +04:00
n - > num_changes + + ;
2006-03-30 07:14:38 +04:00
/*
work out how much room this will take in the buffer
*/
len = 12 + strlen_m ( ev - > path ) * 2 ;
if ( len & 3 ) {
len + = 4 - ( len & 3 ) ;
}
n - > current_buffer_size + = len ;
2006-03-29 17:31:30 +04:00
2006-04-06 05:54:12 +04:00
/* send what we have, unless its the first part of a rename */
if ( ev - > action ! = NOTIFY_ACTION_OLD_NAME ) {
2007-10-07 02:28:14 +04:00
pvfs_notify_send ( n , NT_STATUS_OK , true ) ;
2006-04-06 05:54:12 +04:00
}
2006-03-21 14:47:24 +03:00
}
/*
setup a notify buffer on a directory handle
*/
static NTSTATUS pvfs_notify_setup ( struct pvfs_state * pvfs , struct pvfs_file * f ,
2007-10-07 02:28:14 +04:00
uint32_t buffer_size , uint32_t filter , bool recursive )
2006-03-21 14:47:24 +03:00
{
NTSTATUS status ;
struct notify_entry e ;
2010-05-05 06:47:07 +04:00
/* We may not fill in all the elements in this entry -
* structure may in future be shared with Samba3 */
ZERO_STRUCT ( e ) ;
2006-03-21 14:47:24 +03:00
2010-05-05 06:47:07 +04:00
/* We may not fill in all the elements in this entry -
* structure may in future be shared with Samba3 */
ZERO_STRUCT ( e ) ;
2006-03-21 14:47:24 +03:00
f - > notify_buffer = talloc_zero ( f , struct pvfs_notify_buffer ) ;
NT_STATUS_HAVE_NO_MEMORY ( f - > notify_buffer ) ;
f - > notify_buffer - > max_buffer_size = buffer_size ;
f - > notify_buffer - > f = f ;
e . filter = filter ;
e . path = f - > handle - > name - > full_name ;
2006-04-05 09:54:10 +04:00
if ( recursive ) {
e . subdir_filter = filter ;
} else {
e . subdir_filter = 0 ;
}
2006-03-21 14:47:24 +03:00
status = notify_add ( pvfs - > notify_context , & e ,
pvfs_notify_callback , f - > notify_buffer ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
talloc_set_destructor ( f - > notify_buffer , pvfs_notify_destructor ) ;
return NT_STATUS_OK ;
}
2006-03-29 17:31:30 +04:00
/*
called from the pvfs_wait code when either an event has come in , or
the notify request has been cancelled
*/
2009-02-02 10:34:16 +03:00
static void pvfs_notify_end ( void * private_data , enum pvfs_wait_notice reason )
2006-03-29 17:31:30 +04:00
{
2009-02-02 10:34:16 +03:00
struct pvfs_notify_buffer * notify_buffer = talloc_get_type ( private_data ,
2006-03-30 06:25:38 +04:00
struct pvfs_notify_buffer ) ;
2006-03-30 06:06:06 +04:00
if ( reason = = PVFS_WAIT_CANCEL ) {
2007-10-07 02:28:14 +04:00
pvfs_notify_send ( notify_buffer , NT_STATUS_CANCELLED , false ) ;
2006-03-30 06:06:06 +04:00
} else {
2007-10-07 02:28:14 +04:00
pvfs_notify_send ( notify_buffer , NT_STATUS_OK , true ) ;
2006-03-30 06:06:06 +04:00
}
2006-03-29 17:31:30 +04:00
}
2006-03-21 14:47:24 +03:00
/* change notify request - always async. This request blocks until the
event buffer is non - empty */
NTSTATUS pvfs_notify ( struct ntvfs_module_context * ntvfs ,
struct ntvfs_request * req ,
2006-07-12 18:25:50 +04:00
union smb_notify * info )
2006-03-21 14:47:24 +03:00
{
struct pvfs_state * pvfs = talloc_get_type ( ntvfs - > private_data ,
struct pvfs_state ) ;
struct pvfs_file * f ;
NTSTATUS status ;
2006-03-30 06:25:38 +04:00
struct notify_pending * pending ;
2006-03-21 14:47:24 +03:00
2006-07-12 18:25:50 +04:00
if ( info - > nttrans . level ! = RAW_NOTIFY_NTTRANS ) {
2006-07-17 15:17:32 +04:00
return ntvfs_map_notify ( ntvfs , req , info ) ;
2006-07-12 18:25:50 +04:00
}
f = pvfs_find_fd ( pvfs , req , info - > nttrans . in . file . ntvfs ) ;
2006-03-21 14:47:24 +03:00
if ( ! f ) {
return NT_STATUS_INVALID_HANDLE ;
}
/* this request doesn't make sense unless its async */
if ( ! ( req - > async_states - > state & NTVFS_ASYNC_STATE_MAY_ASYNC ) ) {
return NT_STATUS_INVALID_PARAMETER ;
}
/* its only valid for directories */
if ( f - > handle - > fd ! = - 1 ) {
2006-03-29 17:31:30 +04:00
return NT_STATUS_INVALID_PARAMETER ;
2006-03-21 14:47:24 +03:00
}
/* if the handle doesn't currently have a notify buffer then
create one */
if ( f - > notify_buffer = = NULL ) {
status = pvfs_notify_setup ( pvfs , f ,
2006-07-12 18:25:50 +04:00
info - > nttrans . in . buffer_size ,
info - > nttrans . in . completion_filter ,
info - > nttrans . in . recursive ) ;
2006-03-21 14:47:24 +03:00
NT_STATUS_NOT_OK_RETURN ( status ) ;
}
2006-04-05 13:06:09 +04:00
/* we update the max_buffer_size on each call, but we do not
update the recursive flag or filter */
2006-07-12 18:25:50 +04:00
f - > notify_buffer - > max_buffer_size = info - > nttrans . in . buffer_size ;
2006-03-30 07:14:38 +04:00
2006-03-30 06:25:38 +04:00
pending = talloc ( f - > notify_buffer , struct notify_pending ) ;
NT_STATUS_HAVE_NO_MEMORY ( pending ) ;
pending - > req = talloc_reference ( pending , req ) ;
2006-04-08 11:54:15 +04:00
NT_STATUS_HAVE_NO_MEMORY ( pending - > req ) ;
2006-03-30 06:25:38 +04:00
pending - > info = info ;
2006-03-29 17:31:30 +04:00
2016-02-05 13:32:18 +03:00
DLIST_ADD_END ( f - > notify_buffer - > pending , pending ) ;
2006-03-29 17:31:30 +04:00
2006-03-21 14:47:24 +03:00
/* if the buffer is empty then start waiting */
2009-06-05 10:25:44 +04:00
if ( f - > notify_buffer - > num_changes = = 0 & &
! f - > notify_buffer - > overflowed ) {
2008-02-21 11:02:17 +03:00
struct pvfs_wait * wait_handle ;
wait_handle = pvfs_wait_message ( pvfs , req , - 1 ,
timeval_zero ( ) ,
pvfs_notify_end ,
f - > notify_buffer ) ;
2006-03-29 17:31:30 +04:00
NT_STATUS_HAVE_NO_MEMORY ( wait_handle ) ;
talloc_steal ( req , wait_handle ) ;
2006-03-21 14:47:24 +03:00
return NT_STATUS_OK ;
}
2006-05-23 07:52:57 +04:00
req - > async_states - > state | = NTVFS_ASYNC_STATE_ASYNC ;
2007-10-07 02:28:14 +04:00
pvfs_notify_send ( f - > notify_buffer , NT_STATUS_OK , false ) ;
2006-03-21 14:47:24 +03:00
return NT_STATUS_OK ;
}