@ -16,6 +16,10 @@
# include <asm/ioctls.h>
# define FANOTIFY_DEFAULT_MAX_EVENTS 16384
# define FANOTIFY_DEFAULT_MAX_MARKS 8192
# define FANOTIFY_DEFAULT_MAX_LISTENERS 128
extern const struct fsnotify_ops fanotify_fsnotify_ops ;
static struct kmem_cache * fanotify_mark_cache __read_mostly ;
@ -326,7 +330,7 @@ static ssize_t fanotify_read(struct file *file, char __user *buf,
ret = - EAGAIN ;
if ( file - > f_flags & O_NONBLOCK )
break ;
ret = - EINTR ;
ret = - ERESTARTSYS ;
if ( signal_pending ( current ) )
break ;
@ -372,11 +376,10 @@ static ssize_t fanotify_write(struct file *file, const char __user *buf, size_t
static int fanotify_release ( struct inode * ignored , struct file * file )
{
struct fsnotify_group * group = file - > private_data ;
struct fanotify_response_event * re , * lre ;
pr_debug ( " %s: file=%p group=%p \n " , __func__ , file , group ) ;
# ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
struct fanotify_response_event * re , * lre ;
mutex_lock ( & group - > fanotify_data . access_mutex ) ;
group - > fanotify_data . bypass_perm = true ;
@ -554,18 +557,24 @@ static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
__u32 mask ,
unsigned int flags )
{
__u32 oldmask ;
__u32 oldmask = - 1 ;
spin_lock ( & fsn_mark - > lock ) ;
if ( ! ( flags & FAN_MARK_IGNORED_MASK ) ) {
oldmask = fsn_mark - > mask ;
fsnotify_set_mark_mask_locked ( fsn_mark , ( oldmask | mask ) ) ;
} else {
old mask = fsn_mark - > ignored_mask ;
fsnotify_set_mark_ignored_mask_locked ( fsn_mark , ( oldmask | mask ) ) ;
__u32 t mask = fsn_mark - > ignored_mask | mask ;
fsnotify_set_mark_ignored_mask_locked ( fsn_mark , tmask ) ;
if ( flags & FAN_MARK_IGNORED_SURV_MODIFY )
fsn_mark - > flags | = FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY ;
}
if ( ! ( flags & FAN_MARK_ONDIR ) ) {
__u32 tmask = fsn_mark - > ignored_mask | FAN_ONDIR ;
fsnotify_set_mark_ignored_mask_locked ( fsn_mark , tmask ) ;
}
spin_unlock ( & fsn_mark - > lock ) ;
return mask & ~ oldmask ;
@ -582,6 +591,9 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
if ( ! fsn_mark ) {
int ret ;
if ( atomic_read ( & group - > num_marks ) > group - > fanotify_data . max_marks )
return - ENOSPC ;
fsn_mark = kmem_cache_alloc ( fanotify_mark_cache , GFP_KERNEL ) ;
if ( ! fsn_mark )
return - ENOMEM ;
@ -610,10 +622,23 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group,
pr_debug ( " %s: group=%p inode=%p \n " , __func__ , group , inode ) ;
/*
* If some other task has this inode open for write we should not add
* an ignored mark , unless that ignored mark is supposed to survive
* modification changes anyway .
*/
if ( ( flags & FAN_MARK_IGNORED_MASK ) & &
! ( flags & FAN_MARK_IGNORED_SURV_MODIFY ) & &
( atomic_read ( & inode - > i_writecount ) > 0 ) )
return 0 ;
fsn_mark = fsnotify_find_inode_mark ( group , inode ) ;
if ( ! fsn_mark ) {
int ret ;
if ( atomic_read ( & group - > num_marks ) > group - > fanotify_data . max_marks )
return - ENOSPC ;
fsn_mark = kmem_cache_alloc ( fanotify_mark_cache , GFP_KERNEL ) ;
if ( ! fsn_mark )
return - ENOMEM ;
@ -637,6 +662,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
{
struct fsnotify_group * group ;
int f_flags , fd ;
struct user_struct * user ;
pr_debug ( " %s: flags=%d event_f_flags=%d \n " ,
__func__ , flags , event_f_flags ) ;
@ -647,6 +673,12 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
if ( flags & ~ FAN_ALL_INIT_FLAGS )
return - EINVAL ;
user = get_current_user ( ) ;
if ( atomic_read ( & user - > fanotify_listeners ) > FANOTIFY_DEFAULT_MAX_LISTENERS ) {
free_uid ( user ) ;
return - EMFILE ;
}
f_flags = O_RDWR | FMODE_NONOTIFY ;
if ( flags & FAN_CLOEXEC )
f_flags | = O_CLOEXEC ;
@ -658,12 +690,47 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
if ( IS_ERR ( group ) )
return PTR_ERR ( group ) ;
group - > fanotify_data . user = user ;
atomic_inc ( & user - > fanotify_listeners ) ;
group - > fanotify_data . f_flags = event_f_flags ;
# ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
mutex_init ( & group - > fanotify_data . access_mutex ) ;
init_waitqueue_head ( & group - > fanotify_data . access_waitq ) ;
INIT_LIST_HEAD ( & group - > fanotify_data . access_list ) ;
# endif
switch ( flags & FAN_ALL_CLASS_BITS ) {
case FAN_CLASS_NOTIF :
group - > priority = FS_PRIO_0 ;
break ;
case FAN_CLASS_CONTENT :
group - > priority = FS_PRIO_1 ;
break ;
case FAN_CLASS_PRE_CONTENT :
group - > priority = FS_PRIO_2 ;
break ;
default :
fd = - EINVAL ;
goto out_put_group ;
}
if ( flags & FAN_UNLIMITED_QUEUE ) {
fd = - EPERM ;
if ( ! capable ( CAP_SYS_ADMIN ) )
goto out_put_group ;
group - > max_events = UINT_MAX ;
} else {
group - > max_events = FANOTIFY_DEFAULT_MAX_EVENTS ;
}
if ( flags & FAN_UNLIMITED_MARKS ) {
fd = - EPERM ;
if ( ! capable ( CAP_SYS_ADMIN ) )
goto out_put_group ;
group - > fanotify_data . max_marks = UINT_MAX ;
} else {
group - > fanotify_data . max_marks = FANOTIFY_DEFAULT_MAX_MARKS ;
}
fd = anon_inode_getfd ( " [fanotify] " , & fanotify_fops , group , f_flags ) ;
if ( fd < 0 )
@ -704,6 +771,12 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags,
default :
return - EINVAL ;
}
if ( mask & FAN_ONDIR ) {
flags | = FAN_MARK_ONDIR ;
mask & = ~ FAN_ONDIR ;
}
# ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
if ( mask & ~ ( FAN_ALL_EVENTS | FAN_ALL_PERM_EVENTS | FAN_EVENT_ON_CHILD ) )
# else
@ -719,6 +792,16 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags,
ret = - EINVAL ;
if ( unlikely ( filp - > f_op ! = & fanotify_fops ) )
goto fput_and_out ;
group = filp - > private_data ;
/*
* group - > priority = = FS_PRIO_0 = = FAN_CLASS_NOTIF . These are not
* allowed to set permissions events .
*/
ret = - EINVAL ;
if ( mask & FAN_ALL_PERM_EVENTS & &
group - > priority = = FS_PRIO_0 )
goto fput_and_out ;
ret = fanotify_find_path ( dfd , pathname , & path , flags ) ;
if ( ret )
@ -729,7 +812,6 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags,
inode = path . dentry - > d_inode ;
else
mnt = path . mnt ;
group = filp - > private_data ;
/* create/update an inode mark */
switch ( flags & ( FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_FLUSH ) ) {