forked from Minki/linux
Merge branch 'for-linus' of git://git.infradead.org/users/eparis/notify
* 'for-linus' of git://git.infradead.org/users/eparis/notify: (22 commits) Ensure FMODE_NONOTIFY is not set by userspace make fanotify_read() restartable across signals fsnotify: remove alignment padding from fsnotify_mark on 64 bit builds fs/notify/fanotify/fanotify_user.c: fix warnings fanotify: Fix FAN_CLOSE comments fanotify: do not recalculate the mask if the ignored mask changed fanotify: ignore events on directories unless specifically requested fsnotify: rename FS_IN_ISDIR to FS_ISDIR fanotify: do not send events for irregular files fanotify: limit number of listeners per user fanotify: allow userspace to override max marks fanotify: limit the number of marks in a single fanotify group fanotify: allow userspace to override max queue depth fsnotify: implement a default maximum queue depth fanotify: ignore fanotify ignore marks if open writers fanotify: allow userspace to flush all marks fsnotify: call fsnotify_parent in perm events fsnotify: correctly handle return codes from listeners fanotify: use __aligned_u64 in fanotify userspace metadata fanotify: implement fanotify listener ordering ...
This commit is contained in:
commit
1792f17b72
@ -3,4 +3,4 @@ config FSNOTIFY
|
||||
|
||||
source "fs/notify/dnotify/Kconfig"
|
||||
source "fs/notify/inotify/Kconfig"
|
||||
#source "fs/notify/fanotify/Kconfig"
|
||||
source "fs/notify/fanotify/Kconfig"
|
||||
|
@ -131,6 +131,7 @@ static int fanotify_handle_event(struct fsnotify_group *group,
|
||||
BUILD_BUG_ON(FAN_Q_OVERFLOW != FS_Q_OVERFLOW);
|
||||
BUILD_BUG_ON(FAN_OPEN_PERM != FS_OPEN_PERM);
|
||||
BUILD_BUG_ON(FAN_ACCESS_PERM != FS_ACCESS_PERM);
|
||||
BUILD_BUG_ON(FAN_ONDIR != FS_ISDIR);
|
||||
|
||||
pr_debug("%s: group=%p event=%p\n", __func__, group, event);
|
||||
|
||||
@ -160,20 +161,21 @@ static bool fanotify_should_send_event(struct fsnotify_group *group,
|
||||
__u32 event_mask, void *data, int data_type)
|
||||
{
|
||||
__u32 marks_mask, marks_ignored_mask;
|
||||
struct path *path = data;
|
||||
|
||||
pr_debug("%s: group=%p to_tell=%p inode_mark=%p vfsmnt_mark=%p "
|
||||
"mask=%x data=%p data_type=%d\n", __func__, group, to_tell,
|
||||
inode_mark, vfsmnt_mark, event_mask, data, data_type);
|
||||
|
||||
/* sorry, fanotify only gives a damn about files and dirs */
|
||||
if (!S_ISREG(to_tell->i_mode) &&
|
||||
!S_ISDIR(to_tell->i_mode))
|
||||
return false;
|
||||
|
||||
/* if we don't have enough info to send an event to userspace say no */
|
||||
if (data_type != FSNOTIFY_EVENT_PATH)
|
||||
return false;
|
||||
|
||||
/* sorry, fanotify only gives a damn about files and dirs */
|
||||
if (!S_ISREG(path->dentry->d_inode->i_mode) &&
|
||||
!S_ISDIR(path->dentry->d_inode->i_mode))
|
||||
return false;
|
||||
|
||||
if (inode_mark && vfsmnt_mark) {
|
||||
marks_mask = (vfsmnt_mark->mask | inode_mark->mask);
|
||||
marks_ignored_mask = (vfsmnt_mark->ignored_mask | inode_mark->ignored_mask);
|
||||
@ -194,16 +196,29 @@ static bool fanotify_should_send_event(struct fsnotify_group *group,
|
||||
BUG();
|
||||
}
|
||||
|
||||
if (S_ISDIR(path->dentry->d_inode->i_mode) &&
|
||||
(marks_ignored_mask & FS_ISDIR))
|
||||
return false;
|
||||
|
||||
if (event_mask & marks_mask & ~marks_ignored_mask)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void fanotify_free_group_priv(struct fsnotify_group *group)
|
||||
{
|
||||
struct user_struct *user;
|
||||
|
||||
user = group->fanotify_data.user;
|
||||
atomic_dec(&user->fanotify_listeners);
|
||||
free_uid(user);
|
||||
}
|
||||
|
||||
const struct fsnotify_ops fanotify_fsnotify_ops = {
|
||||
.handle_event = fanotify_handle_event,
|
||||
.should_send_event = fanotify_should_send_event,
|
||||
.free_group_priv = NULL,
|
||||
.free_group_priv = fanotify_free_group_priv,
|
||||
.free_event_priv = NULL,
|
||||
.freeing_mark = NULL,
|
||||
};
|
||||
|
@ -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 {
|
||||
oldmask = fsn_mark->ignored_mask;
|
||||
fsnotify_set_mark_ignored_mask_locked(fsn_mark, (oldmask | mask));
|
||||
__u32 tmask = 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)) {
|
||||
|
@ -84,16 +84,17 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode)
|
||||
}
|
||||
|
||||
/* Notify this dentry's parent about a child's events. */
|
||||
void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
|
||||
int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
|
||||
{
|
||||
struct dentry *parent;
|
||||
struct inode *p_inode;
|
||||
int ret = 0;
|
||||
|
||||
if (!dentry)
|
||||
dentry = path->dentry;
|
||||
|
||||
if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED))
|
||||
return;
|
||||
return 0;
|
||||
|
||||
parent = dget_parent(dentry);
|
||||
p_inode = parent->d_inode;
|
||||
@ -106,14 +107,16 @@ void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
|
||||
mask |= FS_EVENT_ON_CHILD;
|
||||
|
||||
if (path)
|
||||
fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
|
||||
dentry->d_name.name, 0);
|
||||
ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
|
||||
dentry->d_name.name, 0);
|
||||
else
|
||||
fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
|
||||
dentry->d_name.name, 0);
|
||||
ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
|
||||
dentry->d_name.name, 0);
|
||||
}
|
||||
|
||||
dput(parent);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__fsnotify_parent);
|
||||
|
||||
@ -252,20 +255,23 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
|
||||
|
||||
if (inode_group > vfsmount_group) {
|
||||
/* handle inode */
|
||||
send_to_group(to_tell, NULL, inode_mark, NULL, mask, data,
|
||||
data_is, cookie, file_name, &event);
|
||||
ret = send_to_group(to_tell, NULL, inode_mark, NULL, mask, data,
|
||||
data_is, cookie, file_name, &event);
|
||||
/* we didn't use the vfsmount_mark */
|
||||
vfsmount_group = NULL;
|
||||
} else if (vfsmount_group > inode_group) {
|
||||
send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data,
|
||||
data_is, cookie, file_name, &event);
|
||||
ret = send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data,
|
||||
data_is, cookie, file_name, &event);
|
||||
inode_group = NULL;
|
||||
} else {
|
||||
send_to_group(to_tell, mnt, inode_mark, vfsmount_mark,
|
||||
mask, data, data_is, cookie, file_name,
|
||||
&event);
|
||||
ret = send_to_group(to_tell, mnt, inode_mark, vfsmount_mark,
|
||||
mask, data, data_is, cookie, file_name,
|
||||
&event);
|
||||
}
|
||||
|
||||
if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS))
|
||||
goto out;
|
||||
|
||||
if (inode_group)
|
||||
inode_node = srcu_dereference(inode_node->next,
|
||||
&fsnotify_mark_srcu);
|
||||
@ -273,7 +279,8 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
|
||||
vfsmount_node = srcu_dereference(vfsmount_node->next,
|
||||
&fsnotify_mark_srcu);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
out:
|
||||
srcu_read_unlock(&fsnotify_mark_srcu, idx);
|
||||
/*
|
||||
* fsnotify_create_event() took a reference so the event can't be cleaned
|
||||
|
@ -177,7 +177,8 @@ void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *mark,
|
||||
* Attach an initialized mark to a given inode.
|
||||
* These marks may be used for the fsnotify backend to determine which
|
||||
* event types should be delivered to which group and for which inodes. These
|
||||
* marks are ordered according to the group's location in memory.
|
||||
* marks are ordered according to priority, highest number first, and then by
|
||||
* the group's location in memory.
|
||||
*/
|
||||
int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
|
||||
struct fsnotify_group *group, struct inode *inode,
|
||||
@ -211,7 +212,11 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mark->group < lmark->group)
|
||||
if (mark->group->priority < lmark->group->priority)
|
||||
continue;
|
||||
|
||||
if ((mark->group->priority == lmark->group->priority) &&
|
||||
(mark->group < lmark->group))
|
||||
continue;
|
||||
|
||||
hlist_add_before_rcu(&mark->i.i_list, &lmark->i.i_list);
|
||||
|
@ -862,7 +862,7 @@ static int __init inotify_user_setup(void)
|
||||
BUILD_BUG_ON(IN_Q_OVERFLOW != FS_Q_OVERFLOW);
|
||||
BUILD_BUG_ON(IN_IGNORED != FS_IN_IGNORED);
|
||||
BUILD_BUG_ON(IN_EXCL_UNLINK != FS_EXCL_UNLINK);
|
||||
BUILD_BUG_ON(IN_ISDIR != FS_IN_ISDIR);
|
||||
BUILD_BUG_ON(IN_ISDIR != FS_ISDIR);
|
||||
BUILD_BUG_ON(IN_ONESHOT != FS_IN_ONESHOT);
|
||||
|
||||
BUG_ON(hweight32(ALL_INOTIFY_BITS) != 21);
|
||||
|
@ -169,7 +169,11 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (mark->group < lmark->group)
|
||||
if (mark->group->priority < lmark->group->priority)
|
||||
continue;
|
||||
|
||||
if ((mark->group->priority == lmark->group->priority) &&
|
||||
(mark->group < lmark->group))
|
||||
continue;
|
||||
|
||||
hlist_add_before_rcu(&mark->m.m_list, &lmark->m.m_list);
|
||||
|
@ -118,6 +118,7 @@ header-y += eventpoll.h
|
||||
header-y += ext2_fs.h
|
||||
header-y += fadvise.h
|
||||
header-y += falloc.h
|
||||
header-y += fanotify.h
|
||||
header-y += fb.h
|
||||
header-y += fcntl.h
|
||||
header-y += fd.h
|
||||
|
@ -6,18 +6,19 @@
|
||||
/* the following events that user-space can register for */
|
||||
#define FAN_ACCESS 0x00000001 /* File was accessed */
|
||||
#define FAN_MODIFY 0x00000002 /* File was modified */
|
||||
#define FAN_CLOSE_WRITE 0x00000008 /* Unwrittable file closed */
|
||||
#define FAN_CLOSE_NOWRITE 0x00000010 /* Writtable file closed */
|
||||
#define FAN_CLOSE_WRITE 0x00000008 /* Writtable file closed */
|
||||
#define FAN_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed */
|
||||
#define FAN_OPEN 0x00000020 /* File was opened */
|
||||
|
||||
#define FAN_EVENT_ON_CHILD 0x08000000 /* interested in child events */
|
||||
|
||||
/* FIXME currently Q's have no limit.... */
|
||||
#define FAN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */
|
||||
|
||||
#define FAN_OPEN_PERM 0x00010000 /* File open in perm check */
|
||||
#define FAN_ACCESS_PERM 0x00020000 /* File accessed in perm check */
|
||||
|
||||
#define FAN_ONDIR 0x40000000 /* event occurred against dir */
|
||||
|
||||
#define FAN_EVENT_ON_CHILD 0x08000000 /* interested in child events */
|
||||
|
||||
/* helper events */
|
||||
#define FAN_CLOSE (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */
|
||||
|
||||
@ -25,7 +26,19 @@
|
||||
#define FAN_CLOEXEC 0x00000001
|
||||
#define FAN_NONBLOCK 0x00000002
|
||||
|
||||
#define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK)
|
||||
/* These are NOT bitwise flags. Both bits are used togther. */
|
||||
#define FAN_CLASS_NOTIF 0x00000000
|
||||
#define FAN_CLASS_CONTENT 0x00000004
|
||||
#define FAN_CLASS_PRE_CONTENT 0x00000008
|
||||
#define FAN_ALL_CLASS_BITS (FAN_CLASS_NOTIF | FAN_CLASS_CONTENT | \
|
||||
FAN_CLASS_PRE_CONTENT)
|
||||
|
||||
#define FAN_UNLIMITED_QUEUE 0x00000010
|
||||
#define FAN_UNLIMITED_MARKS 0x00000020
|
||||
|
||||
#define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK | \
|
||||
FAN_ALL_CLASS_BITS | FAN_UNLIMITED_QUEUE |\
|
||||
FAN_UNLIMITED_MARKS)
|
||||
|
||||
/* flags used for fanotify_modify_mark() */
|
||||
#define FAN_MARK_ADD 0x00000001
|
||||
@ -36,6 +49,10 @@
|
||||
#define FAN_MARK_IGNORED_MASK 0x00000020
|
||||
#define FAN_MARK_IGNORED_SURV_MODIFY 0x00000040
|
||||
#define FAN_MARK_FLUSH 0x00000080
|
||||
#ifdef __KERNEL__
|
||||
/* not valid from userspace, only kernel internal */
|
||||
#define FAN_MARK_ONDIR 0x00000100
|
||||
#endif
|
||||
|
||||
#define FAN_ALL_MARK_FLAGS (FAN_MARK_ADD |\
|
||||
FAN_MARK_REMOVE |\
|
||||
@ -43,7 +60,8 @@
|
||||
FAN_MARK_ONLYDIR |\
|
||||
FAN_MARK_MOUNT |\
|
||||
FAN_MARK_IGNORED_MASK |\
|
||||
FAN_MARK_IGNORED_SURV_MODIFY)
|
||||
FAN_MARK_IGNORED_SURV_MODIFY |\
|
||||
FAN_MARK_FLUSH)
|
||||
|
||||
/*
|
||||
* All of the events - we build the list by hand so that we can add flags in
|
||||
@ -70,10 +88,10 @@
|
||||
struct fanotify_event_metadata {
|
||||
__u32 event_len;
|
||||
__u32 vers;
|
||||
__u64 mask;
|
||||
__aligned_u64 mask;
|
||||
__s32 fd;
|
||||
__s32 pid;
|
||||
} __attribute__ ((packed));
|
||||
};
|
||||
|
||||
struct fanotify_response {
|
||||
__s32 fd;
|
||||
|
@ -26,12 +26,12 @@ static inline void fsnotify_d_instantiate(struct dentry *dentry,
|
||||
}
|
||||
|
||||
/* Notify this dentry's parent about a child's events. */
|
||||
static inline void fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
|
||||
static inline int fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
|
||||
{
|
||||
if (!dentry)
|
||||
dentry = path->dentry;
|
||||
|
||||
__fsnotify_parent(path, dentry, mask);
|
||||
return __fsnotify_parent(path, dentry, mask);
|
||||
}
|
||||
|
||||
/* simple call site for access decisions */
|
||||
@ -40,6 +40,7 @@ static inline int fsnotify_perm(struct file *file, int mask)
|
||||
struct path *path = &file->f_path;
|
||||
struct inode *inode = path->dentry->d_inode;
|
||||
__u32 fsnotify_mask = 0;
|
||||
int ret;
|
||||
|
||||
if (file->f_mode & FMODE_NONOTIFY)
|
||||
return 0;
|
||||
@ -52,6 +53,10 @@ static inline int fsnotify_perm(struct file *file, int mask)
|
||||
else
|
||||
BUG();
|
||||
|
||||
ret = fsnotify_parent(path, NULL, fsnotify_mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return fsnotify(inode, fsnotify_mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
|
||||
}
|
||||
|
||||
@ -93,8 +98,8 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
|
||||
old_dir_mask |= FS_DN_RENAME;
|
||||
|
||||
if (isdir) {
|
||||
old_dir_mask |= FS_IN_ISDIR;
|
||||
new_dir_mask |= FS_IN_ISDIR;
|
||||
old_dir_mask |= FS_ISDIR;
|
||||
new_dir_mask |= FS_ISDIR;
|
||||
}
|
||||
|
||||
fsnotify(old_dir, old_dir_mask, old_dir, FSNOTIFY_EVENT_INODE, old_name, fs_cookie);
|
||||
@ -132,7 +137,7 @@ static inline void fsnotify_nameremove(struct dentry *dentry, int isdir)
|
||||
__u32 mask = FS_DELETE;
|
||||
|
||||
if (isdir)
|
||||
mask |= FS_IN_ISDIR;
|
||||
mask |= FS_ISDIR;
|
||||
|
||||
fsnotify_parent(NULL, dentry, mask);
|
||||
}
|
||||
@ -174,7 +179,7 @@ static inline void fsnotify_link(struct inode *dir, struct inode *inode, struct
|
||||
*/
|
||||
static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry)
|
||||
{
|
||||
__u32 mask = (FS_CREATE | FS_IN_ISDIR);
|
||||
__u32 mask = (FS_CREATE | FS_ISDIR);
|
||||
struct inode *d_inode = dentry->d_inode;
|
||||
|
||||
audit_inode_child(dentry, inode);
|
||||
@ -192,7 +197,7 @@ static inline void fsnotify_access(struct file *file)
|
||||
__u32 mask = FS_ACCESS;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
mask |= FS_IN_ISDIR;
|
||||
mask |= FS_ISDIR;
|
||||
|
||||
if (!(file->f_mode & FMODE_NONOTIFY)) {
|
||||
fsnotify_parent(path, NULL, mask);
|
||||
@ -210,7 +215,7 @@ static inline void fsnotify_modify(struct file *file)
|
||||
__u32 mask = FS_MODIFY;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
mask |= FS_IN_ISDIR;
|
||||
mask |= FS_ISDIR;
|
||||
|
||||
if (!(file->f_mode & FMODE_NONOTIFY)) {
|
||||
fsnotify_parent(path, NULL, mask);
|
||||
@ -228,12 +233,13 @@ static inline void fsnotify_open(struct file *file)
|
||||
__u32 mask = FS_OPEN;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
mask |= FS_IN_ISDIR;
|
||||
mask |= FS_ISDIR;
|
||||
|
||||
if (!(file->f_mode & FMODE_NONOTIFY)) {
|
||||
fsnotify_parent(path, NULL, mask);
|
||||
fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
|
||||
}
|
||||
/* FMODE_NONOTIFY must never be set from user */
|
||||
file->f_mode &= ~FMODE_NONOTIFY;
|
||||
|
||||
fsnotify_parent(path, NULL, mask);
|
||||
fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -247,7 +253,7 @@ static inline void fsnotify_close(struct file *file)
|
||||
__u32 mask = (mode & FMODE_WRITE) ? FS_CLOSE_WRITE : FS_CLOSE_NOWRITE;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
mask |= FS_IN_ISDIR;
|
||||
mask |= FS_ISDIR;
|
||||
|
||||
if (!(file->f_mode & FMODE_NONOTIFY)) {
|
||||
fsnotify_parent(path, NULL, mask);
|
||||
@ -264,7 +270,7 @@ static inline void fsnotify_xattr(struct dentry *dentry)
|
||||
__u32 mask = FS_ATTRIB;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
mask |= FS_IN_ISDIR;
|
||||
mask |= FS_ISDIR;
|
||||
|
||||
fsnotify_parent(NULL, dentry, mask);
|
||||
fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0);
|
||||
@ -299,7 +305,7 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid)
|
||||
|
||||
if (mask) {
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
mask |= FS_IN_ISDIR;
|
||||
mask |= FS_ISDIR;
|
||||
|
||||
fsnotify_parent(NULL, dentry, mask);
|
||||
fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0);
|
||||
|
@ -45,7 +45,7 @@
|
||||
#define FS_ACCESS_PERM 0x00020000 /* access event in a permissions hook */
|
||||
|
||||
#define FS_EXCL_UNLINK 0x04000000 /* do not send events if object is unlinked */
|
||||
#define FS_IN_ISDIR 0x40000000 /* event occurred against dir */
|
||||
#define FS_ISDIR 0x40000000 /* event occurred against dir */
|
||||
#define FS_IN_ONESHOT 0x80000000 /* only send event once */
|
||||
|
||||
#define FS_DN_RENAME 0x10000000 /* file renamed */
|
||||
@ -64,13 +64,15 @@
|
||||
|
||||
#define FS_MOVE (FS_MOVED_FROM | FS_MOVED_TO)
|
||||
|
||||
#define ALL_FSNOTIFY_PERM_EVENTS (FS_OPEN_PERM | FS_ACCESS_PERM)
|
||||
|
||||
#define ALL_FSNOTIFY_EVENTS (FS_ACCESS | FS_MODIFY | FS_ATTRIB | \
|
||||
FS_CLOSE_WRITE | FS_CLOSE_NOWRITE | FS_OPEN | \
|
||||
FS_MOVED_FROM | FS_MOVED_TO | FS_CREATE | \
|
||||
FS_DELETE | FS_DELETE_SELF | FS_MOVE_SELF | \
|
||||
FS_UNMOUNT | FS_Q_OVERFLOW | FS_IN_IGNORED | \
|
||||
FS_OPEN_PERM | FS_ACCESS_PERM | FS_EXCL_UNLINK | \
|
||||
FS_IN_ISDIR | FS_IN_ONESHOT | FS_DN_RENAME | \
|
||||
FS_ISDIR | FS_IN_ONESHOT | FS_DN_RENAME | \
|
||||
FS_DN_MULTISHOT | FS_EVENT_ON_CHILD)
|
||||
|
||||
struct fsnotify_group;
|
||||
@ -129,6 +131,14 @@ struct fsnotify_group {
|
||||
wait_queue_head_t notification_waitq; /* read() on the notification file blocks on this waitq */
|
||||
unsigned int q_len; /* events on the queue */
|
||||
unsigned int max_events; /* maximum events allowed on the list */
|
||||
/*
|
||||
* Valid fsnotify group priorities. Events are send in order from highest
|
||||
* priority to lowest priority. We default to the lowest priority.
|
||||
*/
|
||||
#define FS_PRIO_0 0 /* normal notifiers, no permissions */
|
||||
#define FS_PRIO_1 1 /* fanotify content based access control */
|
||||
#define FS_PRIO_2 2 /* fanotify pre-content access */
|
||||
unsigned int priority;
|
||||
|
||||
/* stores all fastpath marks assoc with this group so they can be cleaned on unregister */
|
||||
spinlock_t mark_lock; /* protect marks_list */
|
||||
@ -159,6 +169,8 @@ struct fsnotify_group {
|
||||
bool bypass_perm; /* protected by access_mutex */
|
||||
#endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */
|
||||
int f_flags;
|
||||
unsigned int max_marks;
|
||||
struct user_struct *user;
|
||||
} fanotify_data;
|
||||
#endif /* CONFIG_FANOTIFY */
|
||||
};
|
||||
@ -275,8 +287,8 @@ struct fsnotify_mark {
|
||||
struct fsnotify_inode_mark i;
|
||||
struct fsnotify_vfsmount_mark m;
|
||||
};
|
||||
__u32 ignored_mask; /* events types to ignore */
|
||||
struct list_head free_g_list; /* tmp list used when freeing this mark */
|
||||
__u32 ignored_mask; /* events types to ignore */
|
||||
#define FSNOTIFY_MARK_FLAG_INODE 0x01
|
||||
#define FSNOTIFY_MARK_FLAG_VFSMOUNT 0x02
|
||||
#define FSNOTIFY_MARK_FLAG_OBJECT_PINNED 0x04
|
||||
@ -294,7 +306,7 @@ struct fsnotify_mark {
|
||||
/* main fsnotify call to send events */
|
||||
extern int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
|
||||
const unsigned char *name, u32 cookie);
|
||||
extern void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask);
|
||||
extern int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask);
|
||||
extern void __fsnotify_inode_delete(struct inode *inode);
|
||||
extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt);
|
||||
extern u32 fsnotify_get_cookie(void);
|
||||
@ -423,8 +435,10 @@ static inline int fsnotify(struct inode *to_tell, __u32 mask, void *data, int da
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
|
||||
{}
|
||||
static inline int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void __fsnotify_inode_delete(struct inode *inode)
|
||||
{}
|
||||
|
@ -672,6 +672,9 @@ struct user_struct {
|
||||
atomic_t inotify_watches; /* How many inotify watches does this user have? */
|
||||
atomic_t inotify_devs; /* How many inotify devs does this user have opened? */
|
||||
#endif
|
||||
#ifdef CONFIG_FANOTIFY
|
||||
atomic_t fanotify_listeners;
|
||||
#endif
|
||||
#ifdef CONFIG_EPOLL
|
||||
atomic_t epoll_watches; /* The number of file descriptors currently watched */
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user