Commit 1792f17b authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents f02a38d8 6bff7ecc
...@@ -3,4 +3,4 @@ config FSNOTIFY ...@@ -3,4 +3,4 @@ config FSNOTIFY
source "fs/notify/dnotify/Kconfig" source "fs/notify/dnotify/Kconfig"
source "fs/notify/inotify/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, ...@@ -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_Q_OVERFLOW != FS_Q_OVERFLOW);
BUILD_BUG_ON(FAN_OPEN_PERM != FS_OPEN_PERM); BUILD_BUG_ON(FAN_OPEN_PERM != FS_OPEN_PERM);
BUILD_BUG_ON(FAN_ACCESS_PERM != FS_ACCESS_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); 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, ...@@ -160,20 +161,21 @@ static bool fanotify_should_send_event(struct fsnotify_group *group,
__u32 event_mask, void *data, int data_type) __u32 event_mask, void *data, int data_type)
{ {
__u32 marks_mask, marks_ignored_mask; __u32 marks_mask, marks_ignored_mask;
struct path *path = data;
pr_debug("%s: group=%p to_tell=%p inode_mark=%p vfsmnt_mark=%p " 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, "mask=%x data=%p data_type=%d\n", __func__, group, to_tell,
inode_mark, vfsmnt_mark, event_mask, data, data_type); 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 we don't have enough info to send an event to userspace say no */
if (data_type != FSNOTIFY_EVENT_PATH) if (data_type != FSNOTIFY_EVENT_PATH)
return false; 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) { if (inode_mark && vfsmnt_mark) {
marks_mask = (vfsmnt_mark->mask | inode_mark->mask); marks_mask = (vfsmnt_mark->mask | inode_mark->mask);
marks_ignored_mask = (vfsmnt_mark->ignored_mask | inode_mark->ignored_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, ...@@ -194,16 +196,29 @@ static bool fanotify_should_send_event(struct fsnotify_group *group,
BUG(); 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) if (event_mask & marks_mask & ~marks_ignored_mask)
return true; return true;
return false; 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 = { const struct fsnotify_ops fanotify_fsnotify_ops = {
.handle_event = fanotify_handle_event, .handle_event = fanotify_handle_event,
.should_send_event = fanotify_should_send_event, .should_send_event = fanotify_should_send_event,
.free_group_priv = NULL, .free_group_priv = fanotify_free_group_priv,
.free_event_priv = NULL, .free_event_priv = NULL,
.freeing_mark = NULL, .freeing_mark = NULL,
}; };
...@@ -16,6 +16,10 @@ ...@@ -16,6 +16,10 @@
#include <asm/ioctls.h> #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; extern const struct fsnotify_ops fanotify_fsnotify_ops;
static struct kmem_cache *fanotify_mark_cache __read_mostly; static struct kmem_cache *fanotify_mark_cache __read_mostly;
...@@ -326,7 +330,7 @@ static ssize_t fanotify_read(struct file *file, char __user *buf, ...@@ -326,7 +330,7 @@ static ssize_t fanotify_read(struct file *file, char __user *buf,
ret = -EAGAIN; ret = -EAGAIN;
if (file->f_flags & O_NONBLOCK) if (file->f_flags & O_NONBLOCK)
break; break;
ret = -EINTR; ret = -ERESTARTSYS;
if (signal_pending(current)) if (signal_pending(current))
break; break;
...@@ -372,11 +376,10 @@ static ssize_t fanotify_write(struct file *file, const char __user *buf, size_t ...@@ -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) static int fanotify_release(struct inode *ignored, struct file *file)
{ {
struct fsnotify_group *group = file->private_data; 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 #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
struct fanotify_response_event *re, *lre;
mutex_lock(&group->fanotify_data.access_mutex); mutex_lock(&group->fanotify_data.access_mutex);
group->fanotify_data.bypass_perm = true; group->fanotify_data.bypass_perm = true;
...@@ -554,18 +557,24 @@ static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, ...@@ -554,18 +557,24 @@ static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
__u32 mask, __u32 mask,
unsigned int flags) unsigned int flags)
{ {
__u32 oldmask; __u32 oldmask = -1;
spin_lock(&fsn_mark->lock); spin_lock(&fsn_mark->lock);
if (!(flags & FAN_MARK_IGNORED_MASK)) { if (!(flags & FAN_MARK_IGNORED_MASK)) {
oldmask = fsn_mark->mask; oldmask = fsn_mark->mask;
fsnotify_set_mark_mask_locked(fsn_mark, (oldmask | mask)); fsnotify_set_mark_mask_locked(fsn_mark, (oldmask | mask));
} else { } else {
oldmask = fsn_mark->ignored_mask; __u32 tmask = fsn_mark->ignored_mask | mask;
fsnotify_set_mark_ignored_mask_locked(fsn_mark, (oldmask | mask)); fsnotify_set_mark_ignored_mask_locked(fsn_mark, tmask);
if (flags & FAN_MARK_IGNORED_SURV_MODIFY) if (flags & FAN_MARK_IGNORED_SURV_MODIFY)
fsn_mark->flags |= FSNOTIFY_MARK_FLAG_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); spin_unlock(&fsn_mark->lock);
return mask & ~oldmask; return mask & ~oldmask;
...@@ -582,6 +591,9 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, ...@@ -582,6 +591,9 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
if (!fsn_mark) { if (!fsn_mark) {
int ret; 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); fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
if (!fsn_mark) if (!fsn_mark)
return -ENOMEM; return -ENOMEM;
...@@ -610,10 +622,23 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, ...@@ -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); 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); fsn_mark = fsnotify_find_inode_mark(group, inode);
if (!fsn_mark) { if (!fsn_mark) {
int ret; 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); fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
if (!fsn_mark) if (!fsn_mark)
return -ENOMEM; return -ENOMEM;
...@@ -637,6 +662,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) ...@@ -637,6 +662,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
{ {
struct fsnotify_group *group; struct fsnotify_group *group;
int f_flags, fd; int f_flags, fd;
struct user_struct *user;
pr_debug("%s: flags=%d event_f_flags=%d\n", pr_debug("%s: flags=%d event_f_flags=%d\n",
__func__, flags, event_f_flags); __func__, flags, event_f_flags);
...@@ -647,6 +673,12 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, 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) if (flags & ~FAN_ALL_INIT_FLAGS)
return -EINVAL; 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; f_flags = O_RDWR | FMODE_NONOTIFY;
if (flags & FAN_CLOEXEC) if (flags & FAN_CLOEXEC)
f_flags |= O_CLOEXEC; f_flags |= O_CLOEXEC;
...@@ -658,12 +690,47 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) ...@@ -658,12 +690,47 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
if (IS_ERR(group)) if (IS_ERR(group))
return PTR_ERR(group); return PTR_ERR(group);
group->fanotify_data.user = user;
atomic_inc(&user->fanotify_listeners);
group->fanotify_data.f_flags = event_f_flags; group->fanotify_data.f_flags = event_f_flags;
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
mutex_init(&group->fanotify_data.access_mutex); mutex_init(&group->fanotify_data.access_mutex);
init_waitqueue_head(&group->fanotify_data.access_waitq); init_waitqueue_head(&group->fanotify_data.access_waitq);
INIT_LIST_HEAD(&group->fanotify_data.access_list); INIT_LIST_HEAD(&group->fanotify_data.access_list);
#endif #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); fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags);
if (fd < 0) if (fd < 0)
...@@ -704,6 +771,12 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, ...@@ -704,6 +771,12 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags,
default: default:
return -EINVAL; return -EINVAL;
} }
if (mask & FAN_ONDIR) {
flags |= FAN_MARK_ONDIR;
mask &= ~FAN_ONDIR;
}
#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
if (mask & ~(FAN_ALL_EVENTS | FAN_ALL_PERM_EVENTS | FAN_EVENT_ON_CHILD)) if (mask & ~(FAN_ALL_EVENTS | FAN_ALL_PERM_EVENTS | FAN_EVENT_ON_CHILD))
#else #else
...@@ -719,6 +792,16 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, ...@@ -719,6 +792,16 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags,
ret = -EINVAL; ret = -EINVAL;
if (unlikely(filp->f_op != &fanotify_fops)) if (unlikely(filp->f_op != &fanotify_fops))
goto fput_and_out; 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); ret = fanotify_find_path(dfd, pathname, &path, flags);
if (ret) if (ret)
...@@ -729,7 +812,6 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, ...@@ -729,7 +812,6 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags,
inode = path.dentry->d_inode; inode = path.dentry->d_inode;
else else
mnt = path.mnt; mnt = path.mnt;
group = filp->private_data;
/* create/update an inode mark */ /* create/update an inode mark */
switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_FLUSH)) { switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_FLUSH)) {
......
...@@ -84,16 +84,17 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode) ...@@ -84,16 +84,17 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode)
} }
/* Notify this dentry's parent about a child's events. */ /* 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 dentry *parent;
struct inode *p_inode; struct inode *p_inode;
int ret = 0;
if (!dentry) if (!dentry)
dentry = path->dentry; dentry = path->dentry;
if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED))
return; return 0;
parent = dget_parent(dentry); parent = dget_parent(dentry);
p_inode = parent->d_inode; p_inode = parent->d_inode;
...@@ -106,14 +107,16 @@ void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) ...@@ -106,14 +107,16 @@ void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
mask |= FS_EVENT_ON_CHILD; mask |= FS_EVENT_ON_CHILD;
if (path) if (path)
fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH, ret = fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH,
dentry->d_name.name, 0); dentry->d_name.name, 0);
else else
fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, ret = fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
dentry->d_name.name, 0); dentry->d_name.name, 0);
} }
dput(parent); dput(parent);
return ret;
} }
EXPORT_SYMBOL_GPL(__fsnotify_parent); EXPORT_SYMBOL_GPL(__fsnotify_parent);
...@@ -252,20 +255,23 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, ...@@ -252,20 +255,23 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
if (inode_group > vfsmount_group) { if (inode_group > vfsmount_group) {
/* handle inode */ /* handle inode */
send_to_group(to_tell, NULL, inode_mark, NULL, mask, data, ret = send_to_group(to_tell, NULL, inode_mark, NULL, mask, data,
data_is, cookie, file_name, &event); data_is, cookie, file_name, &event);
/* we didn't use the vfsmount_mark */ /* we didn't use the vfsmount_mark */
vfsmount_group = NULL; vfsmount_group = NULL;
} else if (vfsmount_group > inode_group) { } else if (vfsmount_group > inode_group) {
send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data, ret = send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data,
data_is, cookie, file_name, &event); data_is, cookie, file_name, &event);
inode_group = NULL; inode_group = NULL;
} else { } else {
send_to_group(to_tell, mnt, inode_mark, vfsmount_mark, ret = send_to_group(to_tell, mnt, inode_mark, vfsmount_mark,
mask, data, data_is, cookie, file_name, mask, data, data_is, cookie, file_name,
&event); &event);
} }
if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS))
goto out;
if (inode_group) if (inode_group)
inode_node = srcu_dereference(inode_node->next, inode_node = srcu_dereference(inode_node->next,
&fsnotify_mark_srcu); &fsnotify_mark_srcu);
...@@ -273,7 +279,8 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, ...@@ -273,7 +279,8 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
vfsmount_node = srcu_dereference(vfsmount_node->next, vfsmount_node = srcu_dereference(vfsmount_node->next,
&fsnotify_mark_srcu); &fsnotify_mark_srcu);
} }
ret = 0;
out:
srcu_read_unlock(&fsnotify_mark_srcu, idx); srcu_read_unlock(&fsnotify_mark_srcu, idx);
/* /*
* fsnotify_create_event() took a reference so the event can't be cleaned * 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, ...@@ -177,7 +177,8 @@ void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *mark,
* Attach an initialized mark to a given inode. * Attach an initialized mark to a given inode.
* These marks may be used for the fsnotify backend to determine which * 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 * 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, int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
struct fsnotify_group *group, struct inode *inode, struct fsnotify_group *group, struct inode *inode,
...@@ -211,7 +212,11 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, ...@@ -211,7 +212,11 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
goto out; 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; continue;
hlist_add_before_rcu(&mark->i.i_list, &lmark->i.i_list); hlist_add_before_rcu(&mark->i.i_list, &lmark->i.i_list);
......
...@@ -862,7 +862,7 @@ static int __init inotify_user_setup(void) ...@@ -862,7 +862,7 @@ static int __init inotify_user_setup(void)
BUILD_BUG_ON(IN_Q_OVERFLOW != FS_Q_OVERFLOW); BUILD_BUG_ON(IN_Q_OVERFLOW != FS_Q_OVERFLOW);
BUILD_BUG_ON(IN_IGNORED != FS_IN_IGNORED); BUILD_BUG_ON(IN_IGNORED != FS_IN_IGNORED);
BUILD_BUG_ON(IN_EXCL_UNLINK != FS_EXCL_UNLINK); 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); BUILD_BUG_ON(IN_ONESHOT != FS_IN_ONESHOT);
BUG_ON(hweight32(ALL_INOTIFY_BITS) != 21); BUG_ON(hweight32(ALL_INOTIFY_BITS) != 21);
......
...@@ -169,7 +169,11 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, ...@@ -169,7 +169,11 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
goto out; 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; continue;
hlist_add_before_rcu(&mark->m.m_list, &lmark->m.m_list); hlist_add_before_rcu(&mark->m.m_list, &lmark->m.m_list);
......
...@@ -118,6 +118,7 @@ header-y += eventpoll.h ...@@ -118,6 +118,7 @@ header-y += eventpoll.h
header-y += ext2_fs.h header-y += ext2_fs.h
header-y += fadvise.h header-y += fadvise.h
header-y += falloc.h header-y += falloc.h
header-y += fanotify.h
header-y += fb.h header-y += fb.h
header-y += fcntl.h header-y += fcntl.h
header-y += fd.h header-y += fd.h
......
...@@ -6,18 +6,19 @@ ...@@ -6,18 +6,19 @@
/* the following events that user-space can register for */ /* the following events that user-space can register for */
#define FAN_ACCESS 0x00000001 /* File was accessed */ #define FAN_ACCESS 0x00000001 /* File was accessed */
#define FAN_MODIFY 0x00000002 /* File was modified */ #define FAN_MODIFY 0x00000002 /* File was modified */
#define FAN_CLOSE_WRITE 0x00000008 /* Unwrittable file closed */ #define FAN_CLOSE_WRITE 0x00000008 /* Writtable file closed */
#define FAN_CLOSE_NOWRITE 0x00000010 /* Writtable file closed */ #define FAN_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed */
#define FAN_OPEN 0x00000020 /* File was opened */ #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_Q_OVERFLOW 0x00004000 /* Event queued overflowed */
#define FAN_OPEN_PERM 0x00010000 /* File open in perm check */ #define FAN_OPEN_PERM 0x00010000 /* File open in perm check */
#define FAN_ACCESS_PERM 0x00020000 /* File accessed 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 */ /* helper events */
#define FAN_CLOSE (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */ #define FAN_CLOSE (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */
...@@ -25,7 +26,19 @@ ...@@ -25,7 +26,19 @@
#define FAN_CLOEXEC 0x00000001 #define FAN_CLOEXEC 0x00000001
#define FAN_NONBLOCK 0x00000002 #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() */ /* flags used for fanotify_modify_mark() */
#define FAN_MARK_ADD 0x00000001 #define FAN_MARK_ADD 0x00000001
...@@ -36,6 +49,10 @@ ...@@ -36,6 +49,10 @@
#define FAN_MARK_IGNORED_MASK 0x00000020 #define FAN_MARK_IGNORED_MASK 0x00000020
#define FAN_MARK_IGNORED_SURV_MODIFY 0x00000040 #define FAN_MARK_IGNORED_SURV_MODIFY 0x00000040
#define FAN_MARK_FLUSH 0x00000080 #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 |\ #define FAN_ALL_MARK_FLAGS (FAN_MARK_ADD |\
FAN_MARK_REMOVE |\ FAN_MARK_REMOVE |\
...@@ -43,7 +60,8 @@ ...@@ -43,7 +60,8 @@
FAN_MARK_ONLYDIR |\ FAN_MARK_ONLYDIR |\
FAN_MARK_MOUNT |\ FAN_MARK_MOUNT |\
FAN_MARK_IGNORED_MASK |\ 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 * All of the events - we build the list by hand so that we can add flags in
...@@ -70,10 +88,10 @@ ...@@ -70,10 +88,10 @@
struct fanotify_event_metadata { struct fanotify_event_metadata {
__u32 event_len; __u32 event_len;
__u32 vers; __u32 vers;
__u64 mask; __aligned_u64 mask;
__s32 fd; __s32 fd;
__s32 pid; __s32 pid;
} __attribute__ ((packed)); };
struct fanotify_response { struct fanotify_response {
__s32 fd; __s32 fd;
......
...@@ -26,12 +26,12 @@ static inline void fsnotify_d_instantiate(struct dentry *dentry, ...@@ -26,12 +26,12 @@ static inline void fsnotify_d_instantiate(struct dentry *dentry,
} }
/* Notify this dentry's parent about a child's events. */ /* 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) if (!dentry)
dentry = path->dentry; dentry = path->dentry;
__fsnotify_parent(path, dentry, mask); return __fsnotify_parent(path, dentry, mask);
} }
/* simple call site for access decisions */ /* simple call site for access decisions */
...@@ -40,6 +40,7 @@ static inline int fsnotify_perm(struct file *file, int mask) ...@@ -40,6 +40,7 @@ static inline int fsnotify_perm(struct file *file, int mask)
struct path *path = &file->f_path; struct path *path = &file->f_path;
struct inode *inode = path->dentry->d_inode; struct inode *inode = path->dentry->d_inode;
__u32 fsnotify_mask = 0; __u32 fsnotify_mask = 0;
int ret;
if (file->f_mode & FMODE_NONOTIFY) if (file->f_mode & FMODE_NONOTIFY)
return 0; return 0;
...@@ -52,6 +53,10 @@ static inline int fsnotify_perm(struct file *file, int mask) ...@@ -52,6 +53,10 @@ static inline int fsnotify_perm(struct file *file, int mask)
else else
BUG(); BUG();
ret = fsnotify_parent(path, NULL, fsnotify_mask);
if (ret)
return ret;
return fsnotify(inode, fsnotify_mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); 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, ...@@ -93,8 +98,8 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
old_dir_mask |= FS_DN_RENAME; old_dir_mask |= FS_DN_RENAME;
if (isdir) { if (isdir) {
old_dir_mask |= FS_IN_ISDIR; old_dir_mask |= FS_ISDIR;
new_dir_mask |= FS_IN_ISDIR; new_dir_mask |= FS_ISDIR;
} }
fsnotify(old_dir, old_dir_mask, old_dir, FSNOTIFY_EVENT_INODE, old_name, fs_cookie); 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) ...@@ -132,7 +137,7 @@ static inline void fsnotify_nameremove(struct dentry *dentry, int isdir)
__u32 mask = FS_DELETE; __u32 mask = FS_DELETE;
if (isdir) if (isdir)
mask |= FS_IN_ISDIR; mask |= FS_ISDIR;
fsnotify_parent(NULL, dentry, mask); fsnotify_parent(NULL, dentry, mask);
} }
...@@ -174,7 +179,7 @@ static inline void fsnotify_link(struct inode *dir, struct inode *inode, struct ...@@ -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) 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; struct inode *d_inode = dentry->d_inode;
audit_inode_child(dentry, inode); audit_inode_child(dentry, inode);
...@@ -192,7 +197,7 @@ static inline void fsnotify_access(struct file *file) ...@@ -192,7 +197,7 @@ static inline void fsnotify_access(struct file *file)
__u32 mask = FS_ACCESS; __u32 mask = FS_ACCESS;
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
mask |= FS_IN_ISDIR; mask |= FS_ISDIR;
if (!(file->f_mode & FMODE_NONOTIFY)) { if (!(file->f_mode & FMODE_NONOTIFY)) {
fsnotify_parent(path, NULL, mask); fsnotify_parent(path, NULL, mask);
...@@ -210,7 +215,7 @@ static inline void fsnotify_modify(struct file *file) ...@@ -210,7 +215,7 @@ static inline void fsnotify_modify(struct file *file)
__u32 mask = FS_MODIFY; __u32 mask = FS_MODIFY;
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
mask |= FS_IN_ISDIR; mask |= FS_ISDIR;
if (!(file->f_mode & FMODE_NONOTIFY)) { if (!(file->f_mode & FMODE_NONOTIFY)) {
fsnotify_parent(path, NULL, mask); fsnotify_parent(path, NULL, mask);
...@@ -228,12 +233,13 @@ static inline void fsnotify_open(struct file *file) ...@@ -228,12 +233,13 @@ static inline void fsnotify_open(struct file *file)
__u32 mask = FS_OPEN; __u32 mask = FS_OPEN;
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
mask |= FS_IN_ISDIR; mask |= FS_ISDIR;
if (!(file->f_mode & FMODE_NONOTIFY)) { /* FMODE_NONOTIFY must never be set from user */
fsnotify_parent(path, NULL, mask); file->f_mode &= ~FMODE_NONOTIFY;
fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
} 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) ...@@ -247,7 +253,7 @@ static inline void fsnotify_close(struct file *file)
__u32 mask = (mode & FMODE_WRITE) ? FS_CLOSE_WRITE : FS_CLOSE_NOWRITE; __u32 mask = (mode & FMODE_WRITE) ? FS_CLOSE_WRITE : FS_CLOSE_NOWRITE;
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
mask |= FS_IN_ISDIR; mask |= FS_ISDIR;
if (!(file->f_mode & FMODE_NONOTIFY)) { if (!(file->f_mode & FMODE_NONOTIFY)) {
fsnotify_parent(path, NULL, mask); fsnotify_parent(path, NULL, mask);
...@@ -264,7 +270,7 @@ static inline void fsnotify_xattr(struct dentry *dentry) ...@@ -264,7 +270,7 @@ static inline void fsnotify_xattr(struct dentry *dentry)
__u32 mask = FS_ATTRIB; __u32 mask = FS_ATTRIB;
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
mask |= FS_IN_ISDIR; mask |= FS_ISDIR;
fsnotify_parent(NULL, dentry, mask); fsnotify_parent(NULL, dentry, mask);
fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); 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) ...@@ -299,7 +305,7 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid)
if (mask) { if (mask) {
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
mask |= FS_IN_ISDIR; mask |= FS_ISDIR;
fsnotify_parent(NULL, dentry, mask); fsnotify_parent(NULL, dentry, mask);
fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0);
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
#define FS_ACCESS_PERM 0x00020000 /* access event in a permissions hook */ #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_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_IN_ONESHOT 0x80000000 /* only send event once */
#define FS_DN_RENAME 0x10000000 /* file renamed */ #define FS_DN_RENAME 0x10000000 /* file renamed */
...@@ -64,13 +64,15 @@ ...@@ -64,13 +64,15 @@
#define FS_MOVE (FS_MOVED_FROM | FS_MOVED_TO) #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 | \ #define ALL_FSNOTIFY_EVENTS (FS_ACCESS | FS_MODIFY | FS_ATTRIB | \
FS_CLOSE_WRITE | FS_CLOSE_NOWRITE | FS_OPEN | \ FS_CLOSE_WRITE | FS_CLOSE_NOWRITE | FS_OPEN | \
FS_MOVED_FROM | FS_MOVED_TO | FS_CREATE | \ FS_MOVED_FROM | FS_MOVED_TO | FS_CREATE | \
FS_DELETE | FS_DELETE_SELF | FS_MOVE_SELF | \ FS_DELETE | FS_DELETE_SELF | FS_MOVE_SELF | \
FS_UNMOUNT | FS_Q_OVERFLOW | FS_IN_IGNORED | \ FS_UNMOUNT | FS_Q_OVERFLOW | FS_IN_IGNORED | \
FS_OPEN_PERM | FS_ACCESS_PERM | FS_EXCL_UNLINK | \ 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) FS_DN_MULTISHOT | FS_EVENT_ON_CHILD)
struct fsnotify_group; struct fsnotify_group;
...@@ -129,6 +131,14 @@ 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 */ 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 q_len; /* events on the queue */
unsigned int max_events; /* maximum events allowed on the list */ 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 */ /* stores all fastpath marks assoc with this group so they can be cleaned on unregister */
spinlock_t mark_lock; /* protect marks_list */ spinlock_t mark_lock; /* protect marks_list */
...@@ -159,6 +169,8 @@ struct fsnotify_group { ...@@ -159,6 +169,8 @@ struct fsnotify_group {
bool bypass_perm; /* protected by access_mutex */ bool bypass_perm; /* protected by access_mutex */
#endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */ #endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */
int f_flags; int f_flags;
unsigned int max_marks;
struct user_struct *user;
} fanotify_data; } fanotify_data;
#endif /* CONFIG_FANOTIFY */ #endif /* CONFIG_FANOTIFY */
}; };
...@@ -275,8 +287,8 @@ struct fsnotify_mark { ...@@ -275,8 +287,8 @@ struct fsnotify_mark {
struct fsnotify_inode_mark i; struct fsnotify_inode_mark i;
struct fsnotify_vfsmount_mark m; 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 */ 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_INODE 0x01
#define FSNOTIFY_MARK_FLAG_VFSMOUNT 0x02 #define FSNOTIFY_MARK_FLAG_VFSMOUNT 0x02
#define FSNOTIFY_MARK_FLAG_OBJECT_PINNED 0x04 #define FSNOTIFY_MARK_FLAG_OBJECT_PINNED 0x04
...@@ -294,7 +306,7 @@ struct fsnotify_mark { ...@@ -294,7 +306,7 @@ struct fsnotify_mark {
/* main fsnotify call to send events */ /* main fsnotify call to send events */
extern int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, extern int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
const unsigned char *name, u32 cookie); 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_inode_delete(struct inode *inode);
extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt); extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt);
extern u32 fsnotify_get_cookie(void); extern u32 fsnotify_get_cookie(void);
...@@ -423,8 +435,10 @@ static inline int fsnotify(struct inode *to_tell, __u32 mask, void *data, int da ...@@ -423,8 +435,10 @@ static inline int fsnotify(struct inode *to_tell, __u32 mask, void *data, int da
return 0; 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) static inline void __fsnotify_inode_delete(struct inode *inode)
{} {}
......
...@@ -672,6 +672,9 @@ struct user_struct { ...@@ -672,6 +672,9 @@ struct user_struct {
atomic_t inotify_watches; /* How many inotify watches does this user have? */ 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? */ atomic_t inotify_devs; /* How many inotify devs does this user have opened? */
#endif #endif
#ifdef CONFIG_FANOTIFY
atomic_t fanotify_listeners;
#endif
#ifdef CONFIG_EPOLL #ifdef CONFIG_EPOLL
atomic_t epoll_watches; /* The number of file descriptors currently watched */ atomic_t epoll_watches; /* The number of file descriptors currently watched */
#endif #endif
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment