2006-01-19 03:30:16 +03:00
/*
* FAM file notification support .
*
* Copyright ( c ) James Peach 2005
2007-01-21 14:49:00 +03:00
* Copyright ( c ) Volker Lendecke 2007
2006-01-19 03:30:16 +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
2006-01-19 03:30:16 +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 07:42:26 +04:00
* along with this program ; if not , see < http : //www.gnu.org/licenses/>.
2006-01-19 03:30:16 +03:00
*/
# include "includes.h"
2011-03-23 00:34:22 +03:00
# include "smbd/smbd.h"
2010-05-06 12:25:47 +04:00
# include "librpc/gen_ndr/notify.h"
2006-01-19 03:30:16 +03:00
# include <fam.h>
2006-03-10 01:31:05 +03:00
# if !defined(HAVE_FAM_H_FAMCODES_TYPEDEF)
/* Gamin provides this typedef which means we can't use 'enum FAMCodes' as per
* every other FAM implementation . Phooey .
*/
typedef enum FAMCodes FAMCodes ;
# endif
2006-01-19 03:30:16 +03:00
/* NOTE: There are multiple versions of FAM floating around the net, each with
* slight differences from the original SGI FAM implementation . In this file ,
* we rely only on the SGI features and do not assume any extensions . For
* example , we do not look at FAMErrno , because it is not set by the original
* implementation .
*
* Random FAM links :
* http : //oss.sgi.com/projects/fam/
* http : //savannah.nongnu.org/projects/fam/
* http : //sourceforge.net/projects/bsdfam/
*/
2007-01-21 14:49:00 +03:00
/* ------------------------------------------------------------------------- */
2006-02-13 07:07:15 +03:00
2007-02-01 15:36:21 +03:00
struct fam_watch_context {
struct fam_watch_context * prev , * next ;
2007-01-21 14:49:00 +03:00
FAMConnection * fam_connection ;
struct FAMRequest fr ;
2007-02-01 15:36:21 +03:00
struct sys_notify_context * sys_ctx ;
void ( * callback ) ( struct sys_notify_context * ctx ,
void * private_data ,
struct notify_event * ev ) ;
void * private_data ;
uint32_t mask ; /* the inotify mask */
uint32_t filter ; /* the windows completion filter */
const char * path ;
2007-01-21 14:49:00 +03:00
} ;
2006-02-13 07:07:15 +03:00
2007-02-01 15:36:21 +03:00
/*
* We want one FAM connection per smbd , not one per tcon .
*/
2007-01-21 14:49:00 +03:00
static FAMConnection fam_connection ;
2007-10-19 04:40:25 +04:00
static bool fam_connection_initialized = False ;
2007-02-01 15:36:21 +03:00
static struct fam_watch_context * fam_notify_list ;
2007-01-21 14:49:00 +03:00
static void fam_handler ( struct event_context * event_ctx ,
struct fd_event * fd_event ,
uint16 flags ,
void * private_data ) ;
2006-02-13 07:07:15 +03:00
2007-01-21 14:49:00 +03:00
static NTSTATUS fam_open_connection ( FAMConnection * fam_conn ,
struct event_context * event_ctx )
2006-02-13 07:07:15 +03:00
{
2007-01-21 14:49:00 +03:00
int res ;
char * name ;
2006-02-13 07:07:15 +03:00
2007-01-21 14:49:00 +03:00
ZERO_STRUCTP ( fam_conn ) ;
FAMCONNECTION_GETFD ( fam_conn ) = - 1 ;
2006-01-19 03:30:16 +03:00
2010-04-18 17:02:23 +04:00
# ifdef HAVE_FAMNOEXISTS
/* We should honor outside setting of the GAM_CLIENT_ID. */
setenv ( " GAM_CLIENT_ID " , " SAMBA " , 0 ) ;
# endif
2007-01-21 14:49:00 +03:00
if ( asprintf ( & name , " smbd (%lu) " , ( unsigned long ) sys_getpid ( ) ) = = - 1 ) {
DEBUG ( 0 , ( " No memory \n " ) ) ;
return NT_STATUS_NO_MEMORY ;
}
2006-01-19 03:30:16 +03:00
2007-01-21 14:49:00 +03:00
res = FAMOpen2 ( fam_conn , name ) ;
2010-04-18 17:02:23 +04:00
# ifdef HAVE_FAMNOEXISTS
/*
* This reduces the chatter between GAMIN and samba making the pair
* much more reliable .
*/
FAMNoExists ( fam_conn ) ;
# endif
2007-01-21 14:49:00 +03:00
SAFE_FREE ( name ) ;
2006-01-19 03:30:16 +03:00
2007-01-21 14:49:00 +03:00
if ( res < 0 ) {
2007-02-01 15:36:21 +03:00
DEBUG ( 10 , ( " FAM file change notifications not available \n " ) ) ;
2007-01-21 14:49:00 +03:00
/*
* No idea how to get NT_STATUS from a FAM result
*/
FAMCONNECTION_GETFD ( fam_conn ) = - 1 ;
return NT_STATUS_UNEXPECTED_IO_ERROR ;
2006-01-19 03:30:16 +03:00
}
2007-01-21 14:49:00 +03:00
if ( event_add_fd ( event_ctx , event_ctx ,
FAMCONNECTION_GETFD ( fam_conn ) ,
EVENT_FD_READ , fam_handler ,
( void * ) fam_conn ) = = NULL ) {
DEBUG ( 0 , ( " event_add_fd failed \n " ) ) ;
FAMClose ( fam_conn ) ;
FAMCONNECTION_GETFD ( fam_conn ) = - 1 ;
return NT_STATUS_NO_MEMORY ;
}
2006-01-19 03:30:16 +03:00
2007-01-21 14:49:00 +03:00
return NT_STATUS_OK ;
2006-01-19 03:30:16 +03:00
}
2007-01-21 14:49:00 +03:00
static void fam_reopen ( FAMConnection * fam_conn ,
struct event_context * event_ctx ,
2007-02-01 15:36:21 +03:00
struct fam_watch_context * notify_list )
2006-01-19 03:30:16 +03:00
{
2007-02-01 15:36:21 +03:00
struct fam_watch_context * ctx ;
2006-01-19 03:30:16 +03:00
2007-01-21 14:49:00 +03:00
DEBUG ( 5 , ( " Re-opening FAM connection \n " ) ) ;
2006-01-19 03:30:16 +03:00
2007-01-21 14:49:00 +03:00
FAMClose ( fam_conn ) ;
2006-01-19 03:30:16 +03:00
2007-01-21 14:49:00 +03:00
if ( ! NT_STATUS_IS_OK ( fam_open_connection ( fam_conn , event_ctx ) ) ) {
DEBUG ( 5 , ( " Re-opening fam connection failed \n " ) ) ;
return ;
2006-01-19 03:30:16 +03:00
}
2007-01-21 14:49:00 +03:00
for ( ctx = notify_list ; ctx ; ctx = ctx - > next ) {
FAMMonitorDirectory ( fam_conn , ctx - > path , & ctx - > fr , NULL ) ;
2006-01-19 03:30:16 +03:00
}
2007-01-21 14:49:00 +03:00
}
2006-01-19 03:30:16 +03:00
2007-01-21 14:49:00 +03:00
static void fam_handler ( struct event_context * event_ctx ,
struct fd_event * fd_event ,
uint16 flags ,
void * private_data )
{
FAMConnection * fam_conn = ( FAMConnection * ) private_data ;
FAMEvent fam_event ;
2007-02-01 15:36:21 +03:00
struct fam_watch_context * ctx ;
struct notify_event ne ;
2007-01-21 14:49:00 +03:00
if ( FAMPending ( fam_conn ) = = 0 ) {
DEBUG ( 10 , ( " fam_handler called but nothing pending \n " ) ) ;
return ;
}
2006-01-19 03:30:16 +03:00
2007-01-21 14:49:00 +03:00
if ( FAMNextEvent ( fam_conn , & fam_event ) ! = 1 ) {
2007-02-01 15:36:21 +03:00
DEBUG ( 5 , ( " FAMNextEvent returned an error \n " ) ) ;
2007-01-21 14:49:00 +03:00
TALLOC_FREE ( fd_event ) ;
fam_reopen ( fam_conn , event_ctx , fam_notify_list ) ;
return ;
}
2006-01-19 03:30:16 +03:00
2007-02-01 15:36:21 +03:00
DEBUG ( 10 , ( " Got FAMCode %d for %s \n " , fam_event . code ,
fam_event . filename ) ) ;
switch ( fam_event . code ) {
2008-03-06 09:19:46 +03:00
case FAMChanged :
ne . action = NOTIFY_ACTION_MODIFIED ;
break ;
2007-02-01 15:36:21 +03:00
case FAMCreated :
ne . action = NOTIFY_ACTION_ADDED ;
break ;
case FAMDeleted :
ne . action = NOTIFY_ACTION_REMOVED ;
break ;
default :
2007-01-21 14:49:00 +03:00
DEBUG ( 10 , ( " Ignoring code FAMCode %d for file %s \n " ,
( int ) fam_event . code , fam_event . filename ) ) ;
return ;
}
2006-01-19 03:30:16 +03:00
2007-01-21 14:49:00 +03:00
for ( ctx = fam_notify_list ; ctx ; ctx = ctx - > next ) {
if ( memcmp ( & fam_event . fr , & ctx - > fr , sizeof ( FAMRequest ) ) = = 0 ) {
break ;
2006-01-19 03:30:16 +03:00
}
2007-01-21 14:49:00 +03:00
}
2006-01-19 03:30:16 +03:00
2007-01-21 14:49:00 +03:00
if ( ctx = = NULL ) {
DEBUG ( 5 , ( " Discarding event for file %s \n " ,
fam_event . filename ) ) ;
return ;
}
2006-01-19 03:30:16 +03:00
2007-02-01 15:36:21 +03:00
if ( ( ne . path = strrchr_m ( fam_event . filename , ' \\ ' ) ) = = NULL ) {
ne . path = fam_event . filename ;
2006-01-19 03:30:16 +03:00
}
2007-02-01 15:36:21 +03:00
ctx - > callback ( ctx - > sys_ctx , ctx - > private_data , & ne ) ;
2006-01-19 03:30:16 +03:00
}
2007-02-01 15:36:21 +03:00
static int fam_watch_context_destructor ( struct fam_watch_context * ctx )
2006-01-19 03:30:16 +03:00
{
2007-01-21 14:49:00 +03:00
if ( FAMCONNECTION_GETFD ( ctx - > fam_connection ) ! = - 1 ) {
FAMCancelMonitor ( & fam_connection , & ctx - > fr ) ;
}
DLIST_REMOVE ( fam_notify_list , ctx ) ;
return 0 ;
}
2006-01-19 03:30:16 +03:00
2007-02-01 15:36:21 +03:00
/*
add a watch . The watch is removed when the caller calls
talloc_free ( ) on * handle
*/
2007-02-01 17:31:10 +03:00
static NTSTATUS fam_watch ( vfs_handle_struct * vfs_handle ,
struct sys_notify_context * ctx ,
struct notify_entry * e ,
void ( * callback ) ( struct sys_notify_context * ctx ,
void * private_data ,
struct notify_event * ev ) ,
void * private_data ,
void * handle_p )
2007-01-21 14:49:00 +03:00
{
2007-02-01 15:36:21 +03:00
const uint32 fam_mask = ( FILE_NOTIFY_CHANGE_FILE_NAME |
FILE_NOTIFY_CHANGE_DIR_NAME ) ;
struct fam_watch_context * watch ;
void * * handle = ( void * * ) handle_p ;
if ( ( e - > filter & fam_mask ) = = 0 ) {
DEBUG ( 10 , ( " filter = %u, ignoring in FAM \n " , e - > filter ) ) ;
return NT_STATUS_OK ;
2007-01-21 14:49:00 +03:00
}
2006-01-19 03:30:16 +03:00
2007-02-01 15:36:21 +03:00
if ( ! fam_connection_initialized ) {
if ( ! NT_STATUS_IS_OK ( fam_open_connection ( & fam_connection ,
ctx - > ev ) ) ) {
/*
* Just let smbd do all the work itself
*/
return NT_STATUS_OK ;
}
fam_connection_initialized = True ;
2007-01-21 14:49:00 +03:00
}
2006-01-19 03:30:16 +03:00
2011-06-07 05:38:41 +04:00
if ( ! ( watch = talloc ( ctx , struct fam_watch_context ) ) ) {
2007-02-01 15:36:21 +03:00
return NT_STATUS_NO_MEMORY ;
}
2006-01-19 03:30:16 +03:00
2007-02-01 15:36:21 +03:00
watch - > fam_connection = & fam_connection ;
2006-01-19 03:30:16 +03:00
2007-02-01 15:36:21 +03:00
watch - > callback = callback ;
watch - > private_data = private_data ;
watch - > sys_ctx = ctx ;
2006-01-19 03:30:16 +03:00
2007-02-01 15:36:21 +03:00
if ( ! ( watch - > path = talloc_strdup ( watch , e - > path ) ) ) {
2007-01-31 16:09:07 +03:00
DEBUG ( 0 , ( " talloc_asprintf failed \n " ) ) ;
2007-02-01 15:36:21 +03:00
TALLOC_FREE ( watch ) ;
return NT_STATUS_NO_MEMORY ;
2007-01-21 14:49:00 +03:00
}
2006-01-19 03:30:16 +03:00
2007-01-21 14:49:00 +03:00
/*
2007-02-01 15:36:21 +03:00
* The FAM module in this early state will only take care of
* FAMCreated and FAMDeleted events , Leave the rest to
* notify_internal . c
2007-01-21 14:49:00 +03:00
*/
2006-01-19 03:30:16 +03:00
2007-02-01 15:36:21 +03:00
watch - > filter = fam_mask ;
e - > filter & = ~ fam_mask ;
2006-01-19 03:30:16 +03:00
2007-02-01 15:36:21 +03:00
DLIST_ADD ( fam_notify_list , watch ) ;
talloc_set_destructor ( watch , fam_watch_context_destructor ) ;
2007-01-21 14:49:00 +03:00
/*
* Only directories monitored so far
2006-01-19 03:30:16 +03:00
*/
2007-02-01 15:36:21 +03:00
if ( FAMCONNECTION_GETFD ( watch - > fam_connection ) ! = - 1 ) {
FAMMonitorDirectory ( watch - > fam_connection , watch - > path ,
& watch - > fr , NULL ) ;
2007-01-21 14:49:00 +03:00
}
else {
/*
* If the re - open is successful , this will establish the
* FAMMonitor from the list
*/
2007-02-01 15:36:21 +03:00
fam_reopen ( watch - > fam_connection , ctx - > ev , fam_notify_list ) ;
2007-01-21 14:49:00 +03:00
}
2007-02-01 15:36:21 +03:00
* handle = ( void * ) watch ;
2007-01-21 14:49:00 +03:00
2007-02-01 15:36:21 +03:00
return NT_STATUS_OK ;
2006-01-19 03:30:16 +03:00
}
2007-02-01 17:31:10 +03:00
/* VFS operations structure */
2009-07-24 04:28:58 +04:00
static struct vfs_fn_pointers notify_fam_fns = {
. notify_watch = fam_watch ,
2007-02-01 17:31:10 +03:00
} ;
NTSTATUS vfs_notify_fam_init ( void ) ;
NTSTATUS vfs_notify_fam_init ( void )
{
return smb_register_vfs ( SMB_VFS_INTERFACE_VERSION , " notify_fam " ,
2009-07-24 04:28:58 +04:00
& notify_fam_fns ) ;
2007-02-01 17:31:10 +03:00
}