2006-04-03 06:46:55 +00:00
/*
Unix SMB / CIFS implementation .
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 02:07:03 +00:00
the Free Software Foundation ; either version 3 of the License , or
2006-04-03 06:46:55 +00: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 02:07:03 +00:00
along with this program . If not , see < http : //www.gnu.org/licenses/>.
2006-04-03 06:46:55 +00:00
*/
/*
notify implementation using inotify
*/
# include "includes.h"
# include "system/filesys.h"
2009-01-03 12:43:18 +01:00
# include <tevent.h>
2006-04-03 06:46:55 +00:00
# include "ntvfs/sysdep/sys_notify.h"
2008-10-11 21:31:42 +02:00
# include "../lib/util/dlinklist.h"
2006-08-30 11:29:34 +00:00
# include "libcli/raw/smb.h"
2007-09-08 12:42:09 +00:00
# include "param/param.h"
2006-04-03 06:46:55 +00:00
2008-10-30 15:53:46 +11:00
# include <sys/inotify.h>
2006-04-03 06:46:55 +00:00
2012-04-09 21:16:12 +03:00
/* glibc < 2.5 headers don't have these defines */
2006-04-03 06:46:55 +00:00
# ifndef IN_ONLYDIR
# define IN_ONLYDIR 0x01000000
# endif
# ifndef IN_MASK_ADD
# define IN_MASK_ADD 0x20000000
# endif
struct inotify_private {
struct sys_notify_context * ctx ;
int fd ;
2007-02-01 10:59:35 +00:00
struct inotify_watch_context * watches ;
2006-04-03 06:46:55 +00:00
} ;
2007-02-01 10:59:35 +00:00
struct inotify_watch_context {
struct inotify_watch_context * next , * prev ;
2006-04-03 06:46:55 +00:00
struct inotify_private * in ;
int wd ;
sys_notify_callback_t callback ;
2007-01-28 17:03:10 +00:00
void * private_data ;
2006-04-06 10:07:13 +00:00
uint32_t mask ; /* the inotify mask */
uint32_t filter ; /* the windows completion filter */
const char * path ;
2006-04-03 06:46:55 +00:00
} ;
2006-04-06 10:07:13 +00:00
/*
see if a particular event from inotify really does match a requested
notify event in SMB
*/
2007-10-06 22:28:14 +00:00
static bool filter_match ( struct inotify_watch_context * w ,
2007-02-01 10:59:35 +00:00
struct inotify_event * e )
2006-04-06 10:07:13 +00:00
{
if ( ( e - > mask & w - > mask ) = = 0 ) {
/* this happens because inotify_add_watch() coalesces watches on the same
path , oring their masks together */
2007-10-06 22:28:14 +00:00
return false ;
2006-04-06 10:07:13 +00:00
}
/* SMB separates the filters for files and directories */
if ( e - > mask & IN_ISDIR ) {
if ( ( w - > filter & FILE_NOTIFY_CHANGE_DIR_NAME ) = = 0 ) {
2007-10-06 22:28:14 +00:00
return false ;
2006-04-06 10:07:13 +00:00
}
} else {
if ( ( e - > mask & IN_ATTRIB ) & &
( w - > filter & ( FILE_NOTIFY_CHANGE_ATTRIBUTES |
FILE_NOTIFY_CHANGE_LAST_WRITE |
FILE_NOTIFY_CHANGE_LAST_ACCESS |
FILE_NOTIFY_CHANGE_EA |
FILE_NOTIFY_CHANGE_SECURITY ) ) ) {
2007-10-06 22:28:14 +00:00
return true ;
2006-04-06 10:07:13 +00:00
}
2006-04-06 11:09:24 +00:00
if ( ( e - > mask & IN_MODIFY ) & &
( w - > filter & FILE_NOTIFY_CHANGE_ATTRIBUTES ) ) {
2007-10-06 22:28:14 +00:00
return true ;
2006-04-06 11:09:24 +00:00
}
2006-04-06 10:07:13 +00:00
if ( ( w - > filter & FILE_NOTIFY_CHANGE_FILE_NAME ) = = 0 ) {
2007-10-06 22:28:14 +00:00
return false ;
2006-04-06 10:07:13 +00:00
}
}
2007-10-06 22:28:14 +00:00
return true ;
2006-04-06 10:07:13 +00:00
}
2006-04-03 06:46:55 +00:00
/*
dispatch one inotify event
2006-04-05 08:52:55 +00:00
the cookies are used to correctly handle renames
2006-04-03 06:46:55 +00:00
*/
2006-04-05 08:52:55 +00:00
static void inotify_dispatch ( struct inotify_private * in ,
struct inotify_event * e ,
uint32_t prev_cookie ,
2006-04-06 10:07:13 +00:00
struct inotify_event * e2 )
2006-04-03 06:46:55 +00:00
{
2007-02-01 10:59:35 +00:00
struct inotify_watch_context * w , * next ;
2006-04-03 06:46:55 +00:00
struct notify_event ne ;
/* ignore extraneous events, such as unmount and IN_IGNORED events */
2006-04-06 10:07:13 +00:00
if ( ( e - > mask & ( IN_ATTRIB | IN_MODIFY | IN_CREATE | IN_DELETE |
IN_MOVED_FROM | IN_MOVED_TO ) ) = = 0 ) {
2006-04-03 06:46:55 +00:00
return ;
}
2006-04-05 08:52:55 +00:00
/* map the inotify mask to a action. This gets complicated for
renames */
2006-04-03 06:46:55 +00:00
if ( e - > mask & IN_CREATE ) {
ne . action = NOTIFY_ACTION_ADDED ;
} else if ( e - > mask & IN_DELETE ) {
ne . action = NOTIFY_ACTION_REMOVED ;
2006-04-05 08:52:55 +00:00
} else if ( e - > mask & IN_MOVED_FROM ) {
2006-04-06 10:07:13 +00:00
if ( e2 ! = NULL & & e2 - > cookie = = e - > cookie ) {
2006-04-05 08:52:55 +00:00
ne . action = NOTIFY_ACTION_OLD_NAME ;
} else {
ne . action = NOTIFY_ACTION_REMOVED ;
}
} else if ( e - > mask & IN_MOVED_TO ) {
if ( e - > cookie = = prev_cookie ) {
ne . action = NOTIFY_ACTION_NEW_NAME ;
} else {
ne . action = NOTIFY_ACTION_ADDED ;
}
2006-04-03 06:46:55 +00:00
} else {
ne . action = NOTIFY_ACTION_MODIFIED ;
}
ne . path = e - > name ;
/* find any watches that have this watch descriptor */
2006-04-06 10:07:13 +00:00
for ( w = in - > watches ; w ; w = next ) {
next = w - > next ;
if ( w - > wd = = e - > wd & & filter_match ( w , e ) ) {
2014-11-05 11:44:42 +00:00
ne . dir = w - > path ;
2007-01-28 17:03:10 +00:00
w - > callback ( in - > ctx , w - > private_data , & ne ) ;
2006-04-06 10:07:13 +00:00
}
}
/* SMB expects a file rename to generate three events, two for
the rename and the other for a modify of the
destination . Strange ! */
if ( ne . action ! = NOTIFY_ACTION_NEW_NAME | |
( e - > mask & IN_ISDIR ) ! = 0 ) {
return ;
}
ne . action = NOTIFY_ACTION_MODIFIED ;
e - > mask = IN_ATTRIB ;
for ( w = in - > watches ; w ; w = next ) {
next = w - > next ;
if ( w - > wd = = e - > wd & & filter_match ( w , e ) & &
! ( w - > filter & FILE_NOTIFY_CHANGE_CREATION ) ) {
2014-11-05 11:44:42 +00:00
ne . dir = w - > path ;
2007-01-28 17:03:10 +00:00
w - > callback ( in - > ctx , w - > private_data , & ne ) ;
2006-04-03 06:46:55 +00:00
}
}
}
/*
called when the kernel has some events for us
*/
2008-12-29 20:24:57 +01:00
static void inotify_handler ( struct tevent_context * ev , struct tevent_fd * fde ,
2007-01-28 17:03:10 +00:00
uint16_t flags , void * private_data )
2006-04-03 06:46:55 +00:00
{
2007-01-28 17:03:10 +00:00
struct inotify_private * in = talloc_get_type ( private_data ,
struct inotify_private ) ;
2006-04-03 06:46:55 +00:00
int bufsize = 0 ;
struct inotify_event * e0 , * e ;
2006-04-05 08:52:55 +00:00
uint32_t prev_cookie = 0 ;
2006-04-03 06:46:55 +00:00
/*
we must use FIONREAD as we cannot predict the length of the
filenames , and thus can ' t know how much to allocate
otherwise
*/
if ( ioctl ( in - > fd , FIONREAD , & bufsize ) ! = 0 | |
bufsize = = 0 ) {
DEBUG ( 0 , ( " No data on inotify fd?! \n " ) ) ;
return ;
}
e0 = e = talloc_size ( in , bufsize ) ;
if ( e = = NULL ) return ;
if ( read ( in - > fd , e0 , bufsize ) ! = bufsize ) {
DEBUG ( 0 , ( " Failed to read all inotify data \n " ) ) ;
talloc_free ( e0 ) ;
return ;
}
/* we can get more than one event in the buffer */
while ( bufsize > = sizeof ( * e ) ) {
2006-04-05 08:52:55 +00:00
struct inotify_event * e2 = NULL ;
2006-04-03 06:46:55 +00:00
bufsize - = e - > len + sizeof ( * e ) ;
2006-04-05 08:52:55 +00:00
if ( bufsize > = sizeof ( * e ) ) {
e2 = ( struct inotify_event * ) ( e - > len + sizeof ( * e ) + ( char * ) e ) ;
}
2006-04-06 10:07:13 +00:00
inotify_dispatch ( in , e , prev_cookie , e2 ) ;
2006-04-05 08:52:55 +00:00
prev_cookie = e - > cookie ;
e = e2 ;
2006-04-03 06:46:55 +00:00
}
talloc_free ( e0 ) ;
}
/*
setup the inotify handle - called the first time a watch is added on
this context
*/
static NTSTATUS inotify_setup ( struct sys_notify_context * ctx )
{
struct inotify_private * in ;
2009-01-03 12:43:18 +01:00
struct tevent_fd * fde ;
2006-04-03 06:46:55 +00:00
in = talloc ( ctx , struct inotify_private ) ;
NT_STATUS_HAVE_NO_MEMORY ( in ) ;
2009-01-03 12:43:18 +01:00
2006-04-03 06:46:55 +00:00
in - > fd = inotify_init ( ) ;
if ( in - > fd = = - 1 ) {
DEBUG ( 0 , ( " Failed to init inotify - %s \n " , strerror ( errno ) ) ) ;
talloc_free ( in ) ;
2011-06-20 14:55:32 +10:00
return map_nt_error_from_unix_common ( errno ) ;
2006-04-03 06:46:55 +00:00
}
in - > ctx = ctx ;
in - > watches = NULL ;
2007-01-28 17:03:10 +00:00
ctx - > private_data = in ;
2006-04-03 06:46:55 +00:00
/* add a event waiting for the inotify fd to be readable */
2009-01-03 12:43:18 +01:00
fde = tevent_add_fd ( ctx - > ev , in , in - > fd ,
TEVENT_FD_READ , inotify_handler , in ) ;
if ( ! fde ) {
if ( errno = = 0 ) {
errno = ENOMEM ;
}
DEBUG ( 0 , ( " Failed to tevent_add_fd() - %s \n " , strerror ( errno ) ) ) ;
talloc_free ( in ) ;
2011-06-20 14:55:32 +10:00
return map_nt_error_from_unix_common ( errno ) ;
2009-01-03 12:43:18 +01:00
}
tevent_fd_set_auto_close ( fde ) ;
2006-04-03 06:46:55 +00:00
return NT_STATUS_OK ;
}
/*
2006-04-05 05:54:10 +00:00
map from a change notify mask to a inotify mask . Remove any bits
which we can handle
2006-04-03 06:46:55 +00:00
*/
static const struct {
uint32_t notify_mask ;
uint32_t inotify_mask ;
} inotify_mapping [ ] = {
2006-04-06 10:07:13 +00:00
{ FILE_NOTIFY_CHANGE_FILE_NAME , IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO } ,
{ FILE_NOTIFY_CHANGE_DIR_NAME , IN_CREATE | IN_DELETE | IN_MOVED_FROM | IN_MOVED_TO } ,
2006-04-06 11:09:24 +00:00
{ FILE_NOTIFY_CHANGE_ATTRIBUTES , IN_ATTRIB | IN_MOVED_TO | IN_MOVED_FROM | IN_MODIFY } ,
2006-04-06 10:07:13 +00:00
{ FILE_NOTIFY_CHANGE_LAST_WRITE , IN_ATTRIB } ,
{ FILE_NOTIFY_CHANGE_LAST_ACCESS , IN_ATTRIB } ,
{ FILE_NOTIFY_CHANGE_EA , IN_ATTRIB } ,
{ FILE_NOTIFY_CHANGE_SECURITY , IN_ATTRIB }
2006-04-03 06:46:55 +00:00
} ;
2006-04-05 05:54:10 +00:00
static uint32_t inotify_map ( struct notify_entry * e )
2006-04-03 06:46:55 +00:00
{
int i ;
uint32_t out = 0 ;
for ( i = 0 ; i < ARRAY_SIZE ( inotify_mapping ) ; i + + ) {
2006-04-05 05:54:10 +00:00
if ( inotify_mapping [ i ] . notify_mask & e - > filter ) {
2006-04-03 06:46:55 +00:00
out | = inotify_mapping [ i ] . inotify_mask ;
2006-04-05 05:54:10 +00:00
e - > filter & = ~ inotify_mapping [ i ] . notify_mask ;
2006-04-03 06:46:55 +00:00
}
}
return out ;
}
/*
destroy a watch
*/
2007-02-01 10:59:35 +00:00
static int watch_destructor ( struct inotify_watch_context * w )
2006-04-03 06:46:55 +00:00
{
struct inotify_private * in = w - > in ;
int wd = w - > wd ;
DLIST_REMOVE ( w - > in - > watches , w ) ;
/* only rm the watch if its the last one with this wd */
for ( w = in - > watches ; w ; w = w - > next ) {
if ( w - > wd = = wd ) break ;
}
if ( w = = NULL ) {
inotify_rm_watch ( in - > fd , wd ) ;
}
return 0 ;
}
/*
add a watch . The watch is removed when the caller calls
2006-04-05 04:50:08 +00:00
talloc_free ( ) on * handle
2006-04-03 06:46:55 +00:00
*/
2007-01-28 17:03:10 +00:00
static NTSTATUS inotify_watch ( struct sys_notify_context * ctx ,
struct notify_entry * e ,
sys_notify_callback_t callback ,
void * private_data ,
void * handle_p )
2006-04-03 06:46:55 +00:00
{
struct inotify_private * in ;
int wd ;
uint32_t mask ;
2007-02-01 10:59:35 +00:00
struct inotify_watch_context * w ;
2006-04-05 05:54:10 +00:00
uint32_t filter = e - > filter ;
2007-01-28 17:03:10 +00:00
void * * handle = ( void * * ) handle_p ;
2006-04-03 06:46:55 +00:00
/* maybe setup the inotify fd */
2007-01-28 17:03:10 +00:00
if ( ctx - > private_data = = NULL ) {
2006-04-03 06:46:55 +00:00
NTSTATUS status ;
status = inotify_setup ( ctx ) ;
NT_STATUS_NOT_OK_RETURN ( status ) ;
}
2007-01-28 17:03:10 +00:00
in = talloc_get_type ( ctx - > private_data , struct inotify_private ) ;
2006-04-03 06:46:55 +00:00
2006-04-05 05:54:10 +00:00
mask = inotify_map ( e ) ;
2006-04-03 06:46:55 +00:00
if ( mask = = 0 ) {
/* this filter can't be handled by inotify */
return NT_STATUS_INVALID_PARAMETER ;
}
/* using IN_MASK_ADD allows us to cope with inotify() returning the same
2023-08-03 14:40:03 +02:00
watch descriptor for multiple watches on the same path */
2006-04-03 06:46:55 +00:00
mask | = ( IN_MASK_ADD | IN_ONLYDIR ) ;
/* get a new watch descriptor for this path */
2006-04-05 05:54:10 +00:00
wd = inotify_add_watch ( in - > fd , e - > path , mask ) ;
2006-04-03 06:46:55 +00:00
if ( wd = = - 1 ) {
2006-04-05 05:54:10 +00:00
e - > filter = filter ;
2011-06-20 14:55:32 +10:00
return map_nt_error_from_unix_common ( errno ) ;
2006-04-03 06:46:55 +00:00
}
2007-02-01 10:59:35 +00:00
w = talloc ( in , struct inotify_watch_context ) ;
2006-04-03 06:46:55 +00:00
if ( w = = NULL ) {
inotify_rm_watch ( in - > fd , wd ) ;
2006-04-05 05:54:10 +00:00
e - > filter = filter ;
2006-04-03 06:46:55 +00:00
return NT_STATUS_NO_MEMORY ;
}
w - > in = in ;
w - > wd = wd ;
w - > callback = callback ;
2007-01-28 17:03:10 +00:00
w - > private_data = private_data ;
2006-04-03 06:46:55 +00:00
w - > mask = mask ;
2006-04-06 10:07:13 +00:00
w - > filter = filter ;
w - > path = talloc_strdup ( w , e - > path ) ;
if ( w - > path = = NULL ) {
inotify_rm_watch ( in - > fd , wd ) ;
e - > filter = filter ;
return NT_STATUS_NO_MEMORY ;
}
2006-04-03 06:46:55 +00:00
( * handle ) = w ;
DLIST_ADD ( in - > watches , w ) ;
/* the caller frees the handle to stop watching */
talloc_set_destructor ( w , watch_destructor ) ;
return NT_STATUS_OK ;
}
static struct sys_notify_backend inotify = {
. name = " inotify " ,
. notify_watch = inotify_watch
} ;
/*
2023-08-03 14:40:03 +02:00
initialise the inotify module
2006-04-03 06:46:55 +00:00
*/
2017-04-20 12:24:43 -07:00
NTSTATUS sys_notify_inotify_init ( TALLOC_CTX * ) ;
NTSTATUS sys_notify_inotify_init ( TALLOC_CTX * ctx )
2006-04-03 06:46:55 +00:00
{
/* register ourselves as a system inotify module */
2017-05-11 15:50:08 -07:00
return sys_notify_register ( ctx , & inotify ) ;
2006-04-03 06:46:55 +00:00
}