Commit 55b27bf6 authored by Stephen Rothwell's avatar Stephen Rothwell

Merge commit 'fsnotify/for-next'

parents e5c86131 cf71f3cf
...@@ -426,6 +426,13 @@ When: 2.6.33 ...@@ -426,6 +426,13 @@ When: 2.6.33
Why: Should be implemented in userspace, policy daemon. Why: Should be implemented in userspace, policy daemon.
Who: Johannes Berg <johannes@sipsolutions.net> Who: Johannes Berg <johannes@sipsolutions.net>
---------------------------
What: CONFIG_INOTIFY
When: 2.6.33
Why: No known users, fsnotify more generic and more easily maintained.
Who: Eric Paris <eparis@redhat.com>
---------------------------- ----------------------------
What: CONFIG_X86_OLD_MCE What: CONFIG_X86_OLD_MCE
......
...@@ -1177,11 +1177,10 @@ out: ...@@ -1177,11 +1177,10 @@ out:
if (iov != iovstack) if (iov != iovstack)
kfree(iov); kfree(iov);
if ((ret + (type == READ)) > 0) { if ((ret + (type == READ)) > 0) {
struct dentry *dentry = file->f_path.dentry;
if (type == READ) if (type == READ)
fsnotify_access(dentry); fsnotify_access(file);
else else
fsnotify_modify(dentry); fsnotify_modify(file);
} }
return ret; return ret;
} }
......
...@@ -128,7 +128,7 @@ SYSCALL_DEFINE1(uselib, const char __user *, library) ...@@ -128,7 +128,7 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) if (file->f_path.mnt->mnt_flags & MNT_NOEXEC)
goto exit; goto exit;
fsnotify_open(file->f_path.dentry); fsnotify_open(file);
error = -ENOEXEC; error = -ENOEXEC;
if(file->f_op) { if(file->f_op) {
...@@ -663,7 +663,7 @@ struct file *open_exec(const char *name) ...@@ -663,7 +663,7 @@ struct file *open_exec(const char *name)
if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) if (file->f_path.mnt->mnt_flags & MNT_NOEXEC)
goto exit; goto exit;
fsnotify_open(file->f_path.dentry); fsnotify_open(file);
err = deny_write_access(file); err = deny_write_access(file);
if (err) if (err)
......
...@@ -951,7 +951,7 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, ...@@ -951,7 +951,7 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
nfsdstats.io_read += host_err; nfsdstats.io_read += host_err;
*count = host_err; *count = host_err;
err = 0; err = 0;
fsnotify_access(file->f_path.dentry); fsnotify_access(file);
} else } else
err = nfserrno(host_err); err = nfserrno(host_err);
out: out:
...@@ -1062,7 +1062,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, ...@@ -1062,7 +1062,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file,
goto out_nfserr; goto out_nfserr;
*cnt = host_err; *cnt = host_err;
nfsdstats.io_write += host_err; nfsdstats.io_write += host_err;
fsnotify_modify(file->f_path.dentry); fsnotify_modify(file);
/* clear setuid/setgid flag after write */ /* clear setuid/setgid flag after write */
if (inode->i_mode & (S_ISUID | S_ISGID)) if (inode->i_mode & (S_ISUID | S_ISGID))
......
...@@ -132,7 +132,8 @@ static int dnotify_handle_event(struct fsnotify_group *group, ...@@ -132,7 +132,8 @@ static int dnotify_handle_event(struct fsnotify_group *group,
* userspace notification for that pair. * userspace notification for that pair.
*/ */
static bool dnotify_should_send_event(struct fsnotify_group *group, static bool dnotify_should_send_event(struct fsnotify_group *group,
struct inode *inode, __u32 mask) struct inode *inode, __u32 mask,
void *data, int data_type)
{ {
struct fsnotify_mark_entry *entry; struct fsnotify_mark_entry *entry;
bool send; bool send;
...@@ -361,7 +362,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) ...@@ -361,7 +362,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
dnentry = container_of(entry, struct dnotify_mark_entry, fsn_entry); dnentry = container_of(entry, struct dnotify_mark_entry, fsn_entry);
spin_lock(&entry->lock); spin_lock(&entry->lock);
} else { } else {
fsnotify_add_mark(new_entry, dnotify_group, inode); fsnotify_add_mark(new_entry, dnotify_group, inode, 0);
spin_lock(&new_entry->lock); spin_lock(&new_entry->lock);
entry = new_entry; entry = new_entry;
dnentry = new_dnentry; dnentry = new_dnentry;
...@@ -431,8 +432,7 @@ static int __init dnotify_init(void) ...@@ -431,8 +432,7 @@ static int __init dnotify_init(void)
dnotify_struct_cache = KMEM_CACHE(dnotify_struct, SLAB_PANIC); dnotify_struct_cache = KMEM_CACHE(dnotify_struct, SLAB_PANIC);
dnotify_mark_entry_cache = KMEM_CACHE(dnotify_mark_entry, SLAB_PANIC); dnotify_mark_entry_cache = KMEM_CACHE(dnotify_mark_entry, SLAB_PANIC);
dnotify_group = fsnotify_obtain_group(DNOTIFY_GROUP_NUM, dnotify_group = fsnotify_obtain_group(0, &dnotify_fsnotify_ops);
0, &dnotify_fsnotify_ops);
if (IS_ERR(dnotify_group)) if (IS_ERR(dnotify_group))
panic("unable to allocate fsnotify group for dnotify\n"); panic("unable to allocate fsnotify group for dnotify\n");
return 0; return 0;
......
...@@ -77,13 +77,16 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode) ...@@ -77,13 +77,16 @@ 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 dentry *dentry, __u32 mask) void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask)
{ {
struct dentry *parent; struct dentry *parent;
struct inode *p_inode; struct inode *p_inode;
bool send = false; bool send = false;
bool should_update_children = false; bool should_update_children = false;
if (file)
dentry = file->f_path.dentry;
if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED))
return; return;
...@@ -114,8 +117,12 @@ void __fsnotify_parent(struct dentry *dentry, __u32 mask) ...@@ -114,8 +117,12 @@ void __fsnotify_parent(struct dentry *dentry, __u32 mask)
* specifies these are events which came from a child. */ * specifies these are events which came from a child. */
mask |= FS_EVENT_ON_CHILD; mask |= FS_EVENT_ON_CHILD;
fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, if (file)
dentry->d_name.name, 0); fsnotify(p_inode, mask, file, FSNOTIFY_EVENT_FILE,
dentry->d_name.name, 0);
else
fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE,
dentry->d_name.name, 0);
dput(parent); dput(parent);
} }
...@@ -156,7 +163,8 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const ...@@ -156,7 +163,8 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const
idx = srcu_read_lock(&fsnotify_grp_srcu); idx = srcu_read_lock(&fsnotify_grp_srcu);
list_for_each_entry_rcu(group, &fsnotify_groups, group_list) { list_for_each_entry_rcu(group, &fsnotify_groups, group_list) {
if (test_mask & group->mask) { if (test_mask & group->mask) {
if (!group->ops->should_send_event(group, to_tell, mask)) if (!group->ops->should_send_event(group, to_tell, mask,
data, data_is))
continue; continue;
if (!event) { if (!event) {
event = fsnotify_create_event(to_tell, mask, data, event = fsnotify_create_event(to_tell, mask, data,
......
...@@ -77,15 +77,6 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) ...@@ -77,15 +77,6 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group)
fsnotify_recalc_global_mask(); fsnotify_recalc_global_mask();
} }
/*
* Take a reference to a group so things found under the fsnotify_grp_mutex
* can't get freed under us
*/
static void fsnotify_get_group(struct fsnotify_group *group)
{
atomic_inc(&group->refcnt);
}
/* /*
* Final freeing of a group * Final freeing of a group
*/ */
...@@ -170,51 +161,24 @@ void fsnotify_put_group(struct fsnotify_group *group) ...@@ -170,51 +161,24 @@ void fsnotify_put_group(struct fsnotify_group *group)
fsnotify_destroy_group(group); fsnotify_destroy_group(group);
} }
/*
* Simply run the fsnotify_groups list and find a group which matches
* the given parameters. If a group is found we take a reference to that
* group.
*/
static struct fsnotify_group *fsnotify_find_group(unsigned int group_num, __u32 mask,
const struct fsnotify_ops *ops)
{
struct fsnotify_group *group_iter;
struct fsnotify_group *group = NULL;
BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex));
list_for_each_entry_rcu(group_iter, &fsnotify_groups, group_list) {
if (group_iter->group_num == group_num) {
if ((group_iter->mask == mask) &&
(group_iter->ops == ops)) {
fsnotify_get_group(group_iter);
group = group_iter;
} else
group = ERR_PTR(-EEXIST);
}
}
return group;
}
/* /*
* Either finds an existing group which matches the group_num, mask, and ops or * Either finds an existing group which matches the group_num, mask, and ops or
* creates a new group and adds it to the global group list. In either case we * creates a new group and adds it to the global group list. In either case we
* take a reference for the group returned. * take a reference for the group returned.
*/ */
struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, __u32 mask, struct fsnotify_group *fsnotify_obtain_group(__u32 mask,
const struct fsnotify_ops *ops) const struct fsnotify_ops *ops)
{ {
struct fsnotify_group *group, *tgroup; struct fsnotify_group *group;
/* very low use, simpler locking if we just always alloc */ /* very low use, simpler locking if we just always alloc */
group = kmalloc(sizeof(struct fsnotify_group), GFP_KERNEL); group = kzalloc(sizeof(struct fsnotify_group), GFP_KERNEL);
if (!group) if (!group)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
atomic_set(&group->refcnt, 1); atomic_set(&group->refcnt, 1);
group->on_group_list = 0; group->on_group_list = 0;
group->group_num = group_num;
group->mask = mask; group->mask = mask;
mutex_init(&group->notification_mutex); mutex_init(&group->notification_mutex);
...@@ -230,14 +194,6 @@ struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, __u32 mask, ...@@ -230,14 +194,6 @@ struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, __u32 mask,
group->ops = ops; group->ops = ops;
mutex_lock(&fsnotify_grp_mutex); mutex_lock(&fsnotify_grp_mutex);
tgroup = fsnotify_find_group(group_num, mask, ops);
if (tgroup) {
/* group already exists */
mutex_unlock(&fsnotify_grp_mutex);
/* destroy the new one we made */
fsnotify_put_group(group);
return tgroup;
}
/* group not found, add a new one */ /* group not found, add a new one */
list_add_rcu(&group->group_list, &fsnotify_groups); list_add_rcu(&group->group_list, &fsnotify_groups);
......
...@@ -283,12 +283,20 @@ struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *grou ...@@ -283,12 +283,20 @@ struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *grou
return NULL; return NULL;
} }
void fsnotify_duplicate_mark(struct fsnotify_mark_entry *new, struct fsnotify_mark_entry *old)
{
assert_spin_locked(&old->lock);
new->inode = old->inode;
new->group = old->group;
new->mask = old->mask;
new->free_mark = old->free_mark;
}
/* /*
* Nothing fancy, just initialize lists and locks and counters. * Nothing fancy, just initialize lists and locks and counters.
*/ */
void fsnotify_init_mark(struct fsnotify_mark_entry *entry, void fsnotify_init_mark(struct fsnotify_mark_entry *entry,
void (*free_mark)(struct fsnotify_mark_entry *entry)) void (*free_mark)(struct fsnotify_mark_entry *entry))
{ {
spin_lock_init(&entry->lock); spin_lock_init(&entry->lock);
atomic_set(&entry->refcnt, 1); atomic_set(&entry->refcnt, 1);
...@@ -305,9 +313,10 @@ void fsnotify_init_mark(struct fsnotify_mark_entry *entry, ...@@ -305,9 +313,10 @@ void fsnotify_init_mark(struct fsnotify_mark_entry *entry,
* event types should be delivered to which group and for which inodes. * event types should be delivered to which group and for which inodes.
*/ */
int fsnotify_add_mark(struct fsnotify_mark_entry *entry, int fsnotify_add_mark(struct fsnotify_mark_entry *entry,
struct fsnotify_group *group, struct inode *inode) struct fsnotify_group *group, struct inode *inode,
int allow_dups)
{ {
struct fsnotify_mark_entry *lentry; struct fsnotify_mark_entry *lentry = NULL;
int ret = 0; int ret = 0;
inode = igrab(inode); inode = igrab(inode);
...@@ -324,11 +333,12 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, ...@@ -324,11 +333,12 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry,
spin_lock(&group->mark_lock); spin_lock(&group->mark_lock);
spin_lock(&inode->i_lock); spin_lock(&inode->i_lock);
entry->group = group; if (!allow_dups)
entry->inode = inode; lentry = fsnotify_find_mark_entry(group, inode);
lentry = fsnotify_find_mark_entry(group, inode);
if (!lentry) { if (!lentry) {
entry->group = group;
entry->inode = inode;
hlist_add_head(&entry->i_list, &inode->i_fsnotify_mark_entries); hlist_add_head(&entry->i_list, &inode->i_fsnotify_mark_entries);
list_add(&entry->g_list, &group->mark_entries); list_add(&entry->g_list, &group->mark_entries);
......
...@@ -31,6 +31,60 @@ ...@@ -31,6 +31,60 @@
#include "inotify.h" #include "inotify.h"
/*
* Check if 2 events contain the same information. We do not compare private data
* but at this moment that isn't a problem for any know fsnotify listeners.
*/
static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new)
{
if ((old->mask == new->mask) &&
(old->to_tell == new->to_tell) &&
(old->data_type == new->data_type) &&
(old->name_len == new->name_len)) {
switch (old->data_type) {
case (FSNOTIFY_EVENT_INODE):
/* remember, after old was put on the wait_q we aren't
* allowed to look at the inode any more, only thing
* left to check was if the file_name is the same */
if (old->name_len &&
!strcmp(old->file_name, new->file_name))
return true;
break;
case (FSNOTIFY_EVENT_PATH):
if ((old->path.mnt == new->path.mnt) &&
(old->path.dentry == new->path.dentry))
return true;
break;
case (FSNOTIFY_EVENT_NONE):
if (old->mask & FS_Q_OVERFLOW)
return true;
else if (old->mask & FS_IN_IGNORED)
return false;
return true;
};
}
return false;
}
static int inotify_merge(struct list_head *list, struct fsnotify_event *event)
{
struct fsnotify_event_holder *last_holder;
struct fsnotify_event *last_event;
int ret = 0;
/* and the list better be locked by something too */
spin_lock(&event->lock);
last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list);
last_event = last_holder->event;
if (event_compare(last_event, event))
ret = -EEXIST;
spin_unlock(&event->lock);
return ret;
}
static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event)
{ {
struct fsnotify_mark_entry *entry; struct fsnotify_mark_entry *entry;
...@@ -61,7 +115,7 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev ...@@ -61,7 +115,7 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev
fsn_event_priv->group = group; fsn_event_priv->group = group;
event_priv->wd = wd; event_priv->wd = wd;
ret = fsnotify_add_notify_event(group, event, fsn_event_priv); ret = fsnotify_add_notify_event(group, event, fsn_event_priv, inotify_merge);
if (ret) { if (ret) {
inotify_free_event_priv(fsn_event_priv); inotify_free_event_priv(fsn_event_priv);
/* EEXIST says we tail matched, EOVERFLOW isn't something /* EEXIST says we tail matched, EOVERFLOW isn't something
...@@ -85,7 +139,8 @@ static void inotify_freeing_mark(struct fsnotify_mark_entry *entry, struct fsnot ...@@ -85,7 +139,8 @@ static void inotify_freeing_mark(struct fsnotify_mark_entry *entry, struct fsnot
inotify_ignored_and_remove_idr(entry, group); inotify_ignored_and_remove_idr(entry, group);
} }
static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, __u32 mask) static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode,
__u32 mask, void *data, int data_type)
{ {
struct fsnotify_mark_entry *entry; struct fsnotify_mark_entry *entry;
bool send; bool send;
......
This diff is collapsed.
...@@ -56,7 +56,7 @@ static struct kmem_cache *fsnotify_event_holder_cachep; ...@@ -56,7 +56,7 @@ static struct kmem_cache *fsnotify_event_holder_cachep;
* it is needed. It's refcnt is set 1 at kernel init time and will never * it is needed. It's refcnt is set 1 at kernel init time and will never
* get set to 0 so it will never get 'freed' * get set to 0 so it will never get 'freed'
*/ */
static struct fsnotify_event q_overflow_event; static struct fsnotify_event *q_overflow_event;
static atomic_t fsnotify_sync_cookie = ATOMIC_INIT(0); static atomic_t fsnotify_sync_cookie = ATOMIC_INIT(0);
/** /**
...@@ -104,7 +104,8 @@ struct fsnotify_event_holder *fsnotify_alloc_event_holder(void) ...@@ -104,7 +104,8 @@ struct fsnotify_event_holder *fsnotify_alloc_event_holder(void)
void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder) void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder)
{ {
kmem_cache_free(fsnotify_event_holder_cachep, holder); if (holder)
kmem_cache_free(fsnotify_event_holder_cachep, holder);
} }
/* /*
...@@ -128,54 +129,18 @@ struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnot ...@@ -128,54 +129,18 @@ struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnot
return priv; return priv;
} }
/*
* Check if 2 events contain the same information. We do not compare private data
* but at this moment that isn't a problem for any know fsnotify listeners.
*/
static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new)
{
if ((old->mask == new->mask) &&
(old->to_tell == new->to_tell) &&
(old->data_type == new->data_type) &&
(old->name_len == new->name_len)) {
switch (old->data_type) {
case (FSNOTIFY_EVENT_INODE):
/* remember, after old was put on the wait_q we aren't
* allowed to look at the inode any more, only thing
* left to check was if the file_name is the same */
if (old->name_len &&
!strcmp(old->file_name, new->file_name))
return true;
break;
case (FSNOTIFY_EVENT_PATH):
if ((old->path.mnt == new->path.mnt) &&
(old->path.dentry == new->path.dentry))
return true;
break;
case (FSNOTIFY_EVENT_NONE):
if (old->mask & FS_Q_OVERFLOW)
return true;
else if (old->mask & FS_IN_IGNORED)
return false;
return false;
};
}
return false;
}
/* /*
* Add an event to the group notification queue. The group can later pull this * Add an event to the group notification queue. The group can later pull this
* event off the queue to deal with. If the event is successfully added to the * event off the queue to deal with. If the event is successfully added to the
* group's notification queue, a reference is taken on event. * group's notification queue, a reference is taken on event.
*/ */
int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event,
struct fsnotify_event_private_data *priv) struct fsnotify_event_private_data *priv,
int (*merge)(struct list_head *, struct fsnotify_event *))
{ {
struct fsnotify_event_holder *holder = NULL; struct fsnotify_event_holder *holder = NULL;
struct list_head *list = &group->notification_list; struct list_head *list = &group->notification_list;
struct fsnotify_event_holder *last_holder; int rc = 0;
struct fsnotify_event *last_event;
int ret = 0;
/* /*
* There is one fsnotify_event_holder embedded inside each fsnotify_event. * There is one fsnotify_event_holder embedded inside each fsnotify_event.
...@@ -195,12 +160,24 @@ alloc_holder: ...@@ -195,12 +160,24 @@ alloc_holder:
mutex_lock(&group->notification_mutex); mutex_lock(&group->notification_mutex);
if (group->q_len >= group->max_events) { if (group->q_len >= group->max_events) {
event = &q_overflow_event; event = q_overflow_event;
ret = -EOVERFLOW; rc = -EOVERFLOW;
/* sorry, no private data on the overflow event */ /* sorry, no private data on the overflow event */
priv = NULL; priv = NULL;
} }
if (!list_empty(list) && merge) {
int ret;
ret = merge(list, event);
if (ret) {
mutex_unlock(&group->notification_mutex);
if (holder != &event->holder)
fsnotify_destroy_event_holder(holder);
return ret;
}
}
spin_lock(&event->lock); spin_lock(&event->lock);
if (list_empty(&event->holder.event_list)) { if (list_empty(&event->holder.event_list)) {
...@@ -215,18 +192,6 @@ alloc_holder: ...@@ -215,18 +192,6 @@ alloc_holder:
goto alloc_holder; goto alloc_holder;
} }
if (!list_empty(list)) {
last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list);
last_event = last_holder->event;
if (event_compare(last_event, event)) {
spin_unlock(&event->lock);
mutex_unlock(&group->notification_mutex);
if (holder != &event->holder)
fsnotify_destroy_event_holder(holder);
return -EEXIST;
}
}
group->q_len++; group->q_len++;
holder->event = event; holder->event = event;
...@@ -238,7 +203,7 @@ alloc_holder: ...@@ -238,7 +203,7 @@ alloc_holder:
mutex_unlock(&group->notification_mutex); mutex_unlock(&group->notification_mutex);
wake_up(&group->notification_waitq); wake_up(&group->notification_waitq);
return ret; return rc;
} }
/* /*
...@@ -314,25 +279,77 @@ void fsnotify_flush_notify(struct fsnotify_group *group) ...@@ -314,25 +279,77 @@ void fsnotify_flush_notify(struct fsnotify_group *group)
static void initialize_event(struct fsnotify_event *event) static void initialize_event(struct fsnotify_event *event)
{ {
event->holder.event = NULL;
INIT_LIST_HEAD(&event->holder.event_list); INIT_LIST_HEAD(&event->holder.event_list);
atomic_set(&event->refcnt, 1); atomic_set(&event->refcnt, 1);
spin_lock_init(&event->lock); spin_lock_init(&event->lock);
event->path.dentry = NULL;
event->path.mnt = NULL;
event->inode = NULL;
event->data_type = FSNOTIFY_EVENT_NONE;
INIT_LIST_HEAD(&event->private_data_list); INIT_LIST_HEAD(&event->private_data_list);
}
/*
* Caller damn well better be holding whatever mutex is protecting the
* old_holder->event_list and the new_event must be a clean event which
* cannot be found anywhere else in the kernel.
*/
int fsnotify_replace_event(struct fsnotify_event_holder *old_holder,
struct fsnotify_event *new_event)
{
struct fsnotify_event *old_event = old_holder->event;
struct fsnotify_event_holder *new_holder = &new_event->holder;
event->to_tell = NULL; enum event_spinlock_class {
SPINLOCK_OLD,
SPINLOCK_NEW,
};
event->file_name = NULL; /*
event->name_len = 0; * if the new_event's embedded holder is in use someone
* screwed up and didn't give us a clean new event.
*/
BUG_ON(!list_empty(&new_holder->event_list));
spin_lock_nested(&old_event->lock, SPINLOCK_OLD);
spin_lock_nested(&new_event->lock, SPINLOCK_NEW);
new_holder->event = new_event;
list_replace_init(&old_holder->event_list, &new_holder->event_list);
spin_unlock(&new_event->lock);
spin_unlock(&old_event->lock);
/* event == holder means we are referenced through the in event holder */
if (old_holder != &old_event->holder)
fsnotify_destroy_event_holder(old_holder);
fsnotify_get_event(new_event); /* on the list take reference */
fsnotify_put_event(old_event); /* off the list, drop reference */
return 0;
}
struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event)
{
struct fsnotify_event *event;
event->sync_cookie = 0; event = kmem_cache_alloc(fsnotify_event_cachep, GFP_KERNEL);
if (!event)
return NULL;
memcpy(event, old_event, sizeof(*event));
initialize_event(event);
if (event->name_len) {
event->file_name = kstrdup(old_event->file_name, GFP_KERNEL);
if (!event->file_name) {
kmem_cache_free(fsnotify_event_cachep, event);
return NULL;
}
}
if (event->data_type == FSNOTIFY_EVENT_PATH)
path_get(&event->path);
return event;
} }
/* /*
...@@ -353,7 +370,7 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, ...@@ -353,7 +370,7 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask,
{ {
struct fsnotify_event *event; struct fsnotify_event *event;
event = kmem_cache_alloc(fsnotify_event_cachep, gfp); event = kmem_cache_zalloc(fsnotify_event_cachep, gfp);
if (!event) if (!event)
return NULL; return NULL;
...@@ -370,6 +387,7 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, ...@@ -370,6 +387,7 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask,
event->sync_cookie = cookie; event->sync_cookie = cookie;
event->to_tell = to_tell; event->to_tell = to_tell;
event->data_type = data_type;
switch (data_type) { switch (data_type) {
case FSNOTIFY_EVENT_FILE: { case FSNOTIFY_EVENT_FILE: {
...@@ -386,12 +404,10 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, ...@@ -386,12 +404,10 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask,
event->path.dentry = path->dentry; event->path.dentry = path->dentry;
event->path.mnt = path->mnt; event->path.mnt = path->mnt;
path_get(&event->path); path_get(&event->path);
event->data_type = FSNOTIFY_EVENT_PATH;
break; break;
} }
case FSNOTIFY_EVENT_INODE: case FSNOTIFY_EVENT_INODE:
event->inode = data; event->inode = data;
event->data_type = FSNOTIFY_EVENT_INODE;
break; break;
case FSNOTIFY_EVENT_NONE: case FSNOTIFY_EVENT_NONE:
event->inode = NULL; event->inode = NULL;
...@@ -412,8 +428,11 @@ __init int fsnotify_notification_init(void) ...@@ -412,8 +428,11 @@ __init int fsnotify_notification_init(void)
fsnotify_event_cachep = KMEM_CACHE(fsnotify_event, SLAB_PANIC); fsnotify_event_cachep = KMEM_CACHE(fsnotify_event, SLAB_PANIC);
fsnotify_event_holder_cachep = KMEM_CACHE(fsnotify_event_holder, SLAB_PANIC); fsnotify_event_holder_cachep = KMEM_CACHE(fsnotify_event_holder, SLAB_PANIC);
initialize_event(&q_overflow_event); q_overflow_event = fsnotify_create_event(NULL, FS_Q_OVERFLOW, NULL,
q_overflow_event.mask = FS_Q_OVERFLOW; FSNOTIFY_EVENT_NONE, NULL, 0,
GFP_KERNEL);
if (!q_overflow_event)
panic("unable to allocate fsnotify q_overflow_event\n");
return 0; return 0;
} }
......
...@@ -1042,7 +1042,7 @@ long do_sys_open(int dfd, const char __user *filename, int flags, int mode) ...@@ -1042,7 +1042,7 @@ long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
put_unused_fd(fd); put_unused_fd(fd);
fd = PTR_ERR(f); fd = PTR_ERR(f);
} else { } else {
fsnotify_open(f->f_path.dentry); fsnotify_open(f);
fd_install(fd, f); fd_install(fd, f);
} }
} }
......
...@@ -293,7 +293,7 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) ...@@ -293,7 +293,7 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
else else
ret = do_sync_read(file, buf, count, pos); ret = do_sync_read(file, buf, count, pos);
if (ret > 0) { if (ret > 0) {
fsnotify_access(file->f_path.dentry); fsnotify_access(file);
add_rchar(current, ret); add_rchar(current, ret);
} }
inc_syscr(current); inc_syscr(current);
...@@ -348,7 +348,7 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_ ...@@ -348,7 +348,7 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_
else else
ret = do_sync_write(file, buf, count, pos); ret = do_sync_write(file, buf, count, pos);
if (ret > 0) { if (ret > 0) {
fsnotify_modify(file->f_path.dentry); fsnotify_modify(file);
add_wchar(current, ret); add_wchar(current, ret);
} }
inc_syscw(current); inc_syscw(current);
...@@ -656,9 +656,9 @@ out: ...@@ -656,9 +656,9 @@ out:
kfree(iov); kfree(iov);
if ((ret + (type == READ)) > 0) { if ((ret + (type == READ)) > 0) {
if (type == READ) if (type == READ)
fsnotify_access(file->f_path.dentry); fsnotify_access(file);
else else
fsnotify_modify(file->f_path.dentry); fsnotify_modify(file);
} }
return ret; return ret;
} }
......
...@@ -29,9 +29,14 @@ static inline void fsnotify_d_instantiate(struct dentry *entry, ...@@ -29,9 +29,14 @@ static inline void fsnotify_d_instantiate(struct dentry *entry,
} }
/* 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 dentry *dentry, __u32 mask) static inline void fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask)
{ {
__fsnotify_parent(dentry, mask); BUG_ON(file && dentry);
if (file)
dentry = file->f_path.dentry;
__fsnotify_parent(file, dentry, mask);
inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name); inotify_dentry_parent_queue_event(dentry, mask, 0, dentry->d_name.name);
} }
...@@ -124,7 +129,7 @@ static inline void fsnotify_nameremove(struct dentry *dentry, int isdir) ...@@ -124,7 +129,7 @@ static inline void fsnotify_nameremove(struct dentry *dentry, int isdir)
if (isdir) if (isdir)
mask |= FS_IN_ISDIR; mask |= FS_IN_ISDIR;
fsnotify_parent(dentry, mask); fsnotify_parent(NULL, dentry, mask);
} }
/* /*
...@@ -183,9 +188,9 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) ...@@ -183,9 +188,9 @@ static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry)
/* /*
* fsnotify_access - file was read * fsnotify_access - file was read
*/ */
static inline void fsnotify_access(struct dentry *dentry) static inline void fsnotify_access(struct file *file)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = file->f_path.dentry->d_inode;
__u32 mask = FS_ACCESS; __u32 mask = FS_ACCESS;
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
...@@ -193,16 +198,16 @@ static inline void fsnotify_access(struct dentry *dentry) ...@@ -193,16 +198,16 @@ static inline void fsnotify_access(struct dentry *dentry)
inotify_inode_queue_event(inode, mask, 0, NULL, NULL); inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
fsnotify_parent(dentry, mask); fsnotify_parent(file, NULL, mask);
fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0);
} }
/* /*
* fsnotify_modify - file was modified * fsnotify_modify - file was modified
*/ */
static inline void fsnotify_modify(struct dentry *dentry) static inline void fsnotify_modify(struct file *file)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = file->f_path.dentry->d_inode;
__u32 mask = FS_MODIFY; __u32 mask = FS_MODIFY;
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
...@@ -210,16 +215,16 @@ static inline void fsnotify_modify(struct dentry *dentry) ...@@ -210,16 +215,16 @@ static inline void fsnotify_modify(struct dentry *dentry)
inotify_inode_queue_event(inode, mask, 0, NULL, NULL); inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
fsnotify_parent(dentry, mask); fsnotify_parent(file, NULL, mask);
fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0);
} }
/* /*
* fsnotify_open - file was opened * fsnotify_open - file was opened
*/ */
static inline void fsnotify_open(struct dentry *dentry) static inline void fsnotify_open(struct file *file)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = file->f_path.dentry->d_inode;
__u32 mask = FS_OPEN; __u32 mask = FS_OPEN;
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
...@@ -227,8 +232,8 @@ static inline void fsnotify_open(struct dentry *dentry) ...@@ -227,8 +232,8 @@ static inline void fsnotify_open(struct dentry *dentry)
inotify_inode_queue_event(inode, mask, 0, NULL, NULL); inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
fsnotify_parent(dentry, mask); fsnotify_parent(file, NULL, mask);
fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0);
} }
/* /*
...@@ -236,8 +241,7 @@ static inline void fsnotify_open(struct dentry *dentry) ...@@ -236,8 +241,7 @@ static inline void fsnotify_open(struct dentry *dentry)
*/ */
static inline void fsnotify_close(struct file *file) static inline void fsnotify_close(struct file *file)
{ {
struct dentry *dentry = file->f_path.dentry; struct inode *inode = file->f_path.dentry->d_inode;
struct inode *inode = dentry->d_inode;
fmode_t mode = file->f_mode; fmode_t mode = file->f_mode;
__u32 mask = (mode & FMODE_WRITE) ? FS_CLOSE_WRITE : FS_CLOSE_NOWRITE; __u32 mask = (mode & FMODE_WRITE) ? FS_CLOSE_WRITE : FS_CLOSE_NOWRITE;
...@@ -246,7 +250,7 @@ static inline void fsnotify_close(struct file *file) ...@@ -246,7 +250,7 @@ static inline void fsnotify_close(struct file *file)
inotify_inode_queue_event(inode, mask, 0, NULL, NULL); inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
fsnotify_parent(dentry, mask); fsnotify_parent(file, NULL, mask);
fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0); fsnotify(inode, mask, file, FSNOTIFY_EVENT_FILE, NULL, 0);
} }
...@@ -263,7 +267,7 @@ static inline void fsnotify_xattr(struct dentry *dentry) ...@@ -263,7 +267,7 @@ static inline void fsnotify_xattr(struct dentry *dentry)
inotify_inode_queue_event(inode, mask, 0, NULL, NULL); inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
fsnotify_parent(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 +303,7 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid) ...@@ -299,7 +303,7 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid)
mask |= FS_IN_ISDIR; mask |= FS_IN_ISDIR;
inotify_inode_queue_event(inode, mask, 0, NULL, NULL); inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
fsnotify_parent(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);
} }
} }
......
...@@ -58,9 +58,7 @@ ...@@ -58,9 +58,7 @@
FS_MOVED_FROM | FS_MOVED_TO | FS_CREATE |\ FS_MOVED_FROM | FS_MOVED_TO | FS_CREATE |\
FS_DELETE) FS_DELETE)
/* listeners that hard code group numbers near the top */ #define FS_MOVE (FS_MOVED_FROM | FS_MOVED_TO)
#define DNOTIFY_GROUP_NUM UINT_MAX
#define INOTIFY_GROUP_NUM (DNOTIFY_GROUP_NUM-1)
struct fsnotify_group; struct fsnotify_group;
struct fsnotify_event; struct fsnotify_event;
...@@ -80,7 +78,8 @@ struct fsnotify_event_private_data; ...@@ -80,7 +78,8 @@ struct fsnotify_event_private_data;
* valid group and inode to use to clean up. * valid group and inode to use to clean up.
*/ */
struct fsnotify_ops { struct fsnotify_ops {
bool (*should_send_event)(struct fsnotify_group *group, struct inode *inode, __u32 mask); bool (*should_send_event)(struct fsnotify_group *group, struct inode *inode,
__u32 mask, void *data, int data_type);
int (*handle_event)(struct fsnotify_group *group, struct fsnotify_event *event); int (*handle_event)(struct fsnotify_group *group, struct fsnotify_event *event);
void (*free_group_priv)(struct fsnotify_group *group); void (*free_group_priv)(struct fsnotify_group *group);
void (*freeing_mark)(struct fsnotify_mark_entry *entry, struct fsnotify_group *group); void (*freeing_mark)(struct fsnotify_mark_entry *entry, struct fsnotify_group *group);
...@@ -119,7 +118,6 @@ struct fsnotify_group { ...@@ -119,7 +118,6 @@ struct fsnotify_group {
* closed. * closed.
*/ */
atomic_t refcnt; /* things with interest in this group */ atomic_t refcnt; /* things with interest in this group */
unsigned int group_num; /* simply prevents accidental group collision */
const struct fsnotify_ops *ops; /* how this group handles things */ const struct fsnotify_ops *ops; /* how this group handles things */
...@@ -254,7 +252,7 @@ struct fsnotify_mark_entry { ...@@ -254,7 +252,7 @@ struct fsnotify_mark_entry {
/* main fsnotify call to send events */ /* main fsnotify call to send events */
extern void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, extern void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
const char *name, u32 cookie); const char *name, u32 cookie);
extern void __fsnotify_parent(struct dentry *dentry, __u32 mask); extern void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask);
extern void __fsnotify_inode_delete(struct inode *inode); extern void __fsnotify_inode_delete(struct inode *inode);
extern u32 fsnotify_get_cookie(void); extern u32 fsnotify_get_cookie(void);
...@@ -307,8 +305,7 @@ static inline void __fsnotify_d_instantiate(struct dentry *dentry, struct inode ...@@ -307,8 +305,7 @@ static inline void __fsnotify_d_instantiate(struct dentry *dentry, struct inode
/* must call when a group changes its ->mask */ /* must call when a group changes its ->mask */
extern void fsnotify_recalc_global_mask(void); extern void fsnotify_recalc_global_mask(void);
/* get a reference to an existing or create a new group */ /* get a reference to an existing or create a new group */
extern struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, extern struct fsnotify_group *fsnotify_obtain_group(__u32 mask,
__u32 mask,
const struct fsnotify_ops *ops); const struct fsnotify_ops *ops);
/* run all marks associated with this group and update group->mask */ /* run all marks associated with this group and update group->mask */
extern void fsnotify_recalc_group_mask(struct fsnotify_group *group); extern void fsnotify_recalc_group_mask(struct fsnotify_group *group);
...@@ -323,8 +320,10 @@ extern struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struc ...@@ -323,8 +320,10 @@ extern struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struc
struct fsnotify_event *event); struct fsnotify_event *event);
/* attach the event to the group notification queue */ /* attach the event to the group notification queue */
extern int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, extern int fsnotify_add_notify_event(struct fsnotify_group *group,
struct fsnotify_event_private_data *priv); struct fsnotify_event *event,
struct fsnotify_event_private_data *priv,
int (*merge)(struct list_head *, struct fsnotify_event *));
/* true if the group notification queue is empty */ /* true if the group notification queue is empty */
extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group); extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group);
/* return, but do not dequeue the first event on the notification queue */ /* return, but do not dequeue the first event on the notification queue */
...@@ -339,8 +338,10 @@ extern void fsnotify_recalc_inode_mask(struct inode *inode); ...@@ -339,8 +338,10 @@ extern void fsnotify_recalc_inode_mask(struct inode *inode);
extern void fsnotify_init_mark(struct fsnotify_mark_entry *entry, void (*free_mark)(struct fsnotify_mark_entry *entry)); extern void fsnotify_init_mark(struct fsnotify_mark_entry *entry, void (*free_mark)(struct fsnotify_mark_entry *entry));
/* find (and take a reference) to a mark associated with group and inode */ /* find (and take a reference) to a mark associated with group and inode */
extern struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *group, struct inode *inode); extern struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *group, struct inode *inode);
/* copy the values from old into new */
extern void fsnotify_duplicate_mark(struct fsnotify_mark_entry *new, struct fsnotify_mark_entry *old);
/* attach the mark to both the group and the inode */ /* attach the mark to both the group and the inode */
extern int fsnotify_add_mark(struct fsnotify_mark_entry *entry, struct fsnotify_group *group, struct inode *inode); extern int fsnotify_add_mark(struct fsnotify_mark_entry *entry, struct fsnotify_group *group, struct inode *inode, int allow_dups);
/* given a mark, flag it to be freed when all references are dropped */ /* given a mark, flag it to be freed when all references are dropped */
extern void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry); extern void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry);
/* run all the marks in a group, and flag them to be freed */ /* run all the marks in a group, and flag them to be freed */
...@@ -354,13 +355,18 @@ extern struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 ...@@ -354,13 +355,18 @@ extern struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32
void *data, int data_is, const char *name, void *data, int data_is, const char *name,
u32 cookie, gfp_t gfp); u32 cookie, gfp_t gfp);
/* fanotify likes to change events after they are on lists... */
extern struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event);
extern int fsnotify_replace_event(struct fsnotify_event_holder *old_holder,
struct fsnotify_event *new_event);
#else #else
static inline void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, static inline void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
const char *name, u32 cookie) const char *name, u32 cookie)
{} {}
static inline void __fsnotify_parent(struct dentry *dentry, __u32 mask) static inline void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask)
{} {}
static inline void __fsnotify_inode_delete(struct inode *inode) static inline void __fsnotify_inode_delete(struct inode *inode)
......
...@@ -302,13 +302,17 @@ config AUDITSYSCALL ...@@ -302,13 +302,17 @@ config AUDITSYSCALL
help help
Enable low-overhead system-call auditing infrastructure that Enable low-overhead system-call auditing infrastructure that
can be used independently or with another kernel subsystem, can be used independently or with another kernel subsystem,
such as SELinux. To use audit's filesystem watch feature, please such as SELinux.
ensure that INOTIFY is configured.
config AUDIT_WATCH
def_bool y
depends on AUDITSYSCALL
select FSNOTIFY
config AUDIT_TREE config AUDIT_TREE
def_bool y def_bool y
depends on AUDITSYSCALL depends on AUDITSYSCALL
select INOTIFY select FSNOTIFY
menu "RCU Subsystem" menu "RCU Subsystem"
......
...@@ -69,10 +69,11 @@ obj-$(CONFIG_IKCONFIG) += configs.o ...@@ -69,10 +69,11 @@ obj-$(CONFIG_IKCONFIG) += configs.o
obj-$(CONFIG_RESOURCE_COUNTERS) += res_counter.o obj-$(CONFIG_RESOURCE_COUNTERS) += res_counter.o
obj-$(CONFIG_STOP_MACHINE) += stop_machine.o obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
obj-$(CONFIG_AUDIT) += audit.o auditfilter.o audit_watch.o obj-$(CONFIG_AUDIT) += audit.o auditfilter.o
obj-$(CONFIG_AUDITSYSCALL) += auditsc.o obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
obj-$(CONFIG_GCOV_KERNEL) += gcov/ obj-$(CONFIG_AUDIT_WATCH) += audit_watch.o
obj-$(CONFIG_AUDIT_TREE) += audit_tree.o obj-$(CONFIG_AUDIT_TREE) += audit_tree.o
obj-$(CONFIG_GCOV_KERNEL) += gcov/
obj-$(CONFIG_KPROBES) += kprobes.o obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_KGDB) += kgdb.o obj-$(CONFIG_KGDB) += kgdb.o
obj-$(CONFIG_DETECT_SOFTLOCKUP) += softlockup.o obj-$(CONFIG_DETECT_SOFTLOCKUP) += softlockup.o
......
...@@ -55,7 +55,6 @@ ...@@ -55,7 +55,6 @@
#include <net/netlink.h> #include <net/netlink.h>
#include <linux/skbuff.h> #include <linux/skbuff.h>
#include <linux/netlink.h> #include <linux/netlink.h>
#include <linux/inotify.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/tty.h> #include <linux/tty.h>
......
...@@ -103,21 +103,27 @@ extern struct mutex audit_filter_mutex; ...@@ -103,21 +103,27 @@ extern struct mutex audit_filter_mutex;
extern void audit_free_rule_rcu(struct rcu_head *); extern void audit_free_rule_rcu(struct rcu_head *);
extern struct list_head audit_filter_list[]; extern struct list_head audit_filter_list[];
extern struct audit_entry *audit_dupe_rule(struct audit_krule *old);
/* audit watch functions */ /* audit watch functions */
extern unsigned long audit_watch_inode(struct audit_watch *watch); #ifdef CONFIG_AUDIT_WATCH
extern dev_t audit_watch_dev(struct audit_watch *watch);
extern void audit_put_watch(struct audit_watch *watch); extern void audit_put_watch(struct audit_watch *watch);
extern void audit_get_watch(struct audit_watch *watch); extern void audit_get_watch(struct audit_watch *watch);
extern int audit_to_watch(struct audit_krule *krule, char *path, int len, u32 op); extern int audit_to_watch(struct audit_krule *krule, char *path, int len, u32 op);
extern int audit_add_watch(struct audit_krule *krule); extern int audit_add_watch(struct audit_krule *krule, struct list_head **list);
extern void audit_remove_watch(struct audit_watch *watch); extern void audit_remove_watch_rule(struct audit_krule *krule);
extern void audit_remove_watch_rule(struct audit_krule *krule, struct list_head *list);
extern void audit_inotify_unregister(struct list_head *in_list);
extern char *audit_watch_path(struct audit_watch *watch); extern char *audit_watch_path(struct audit_watch *watch);
extern struct list_head *audit_watch_rules(struct audit_watch *watch); extern int audit_watch_compare(struct audit_watch *watch, unsigned long ino, dev_t dev);
#else
extern struct audit_entry *audit_dupe_rule(struct audit_krule *old, #define audit_put_watch(w) {}
struct audit_watch *watch); #define audit_get_watch(w) {}
#define audit_to_watch(k, p, l, o) (-EINVAL)
#define audit_add_watch(k, l) (-EINVAL)
#define audit_remove_watch_rule(k) BUG()
#define audit_watch_path(w) ""
#define audit_watch_compare(w, i, d) 0
#endif /* CONFIG_AUDIT_WATCH */
#ifdef CONFIG_AUDIT_TREE #ifdef CONFIG_AUDIT_TREE
extern struct audit_chunk *audit_tree_lookup(const struct inode *); extern struct audit_chunk *audit_tree_lookup(const struct inode *);
......
This diff is collapsed.
This diff is collapsed.
...@@ -70,6 +70,7 @@ static inline void audit_free_rule(struct audit_entry *e) ...@@ -70,6 +70,7 @@ static inline void audit_free_rule(struct audit_entry *e)
{ {
int i; int i;
struct audit_krule *erule = &e->rule; struct audit_krule *erule = &e->rule;
/* some rules don't have associated watches */ /* some rules don't have associated watches */
if (erule->watch) if (erule->watch)
audit_put_watch(erule->watch); audit_put_watch(erule->watch);
...@@ -745,8 +746,7 @@ static inline int audit_dupe_lsm_field(struct audit_field *df, ...@@ -745,8 +746,7 @@ static inline int audit_dupe_lsm_field(struct audit_field *df,
* rule with the new rule in the filterlist, then free the old rule. * rule with the new rule in the filterlist, then free the old rule.
* The rlist element is undefined; list manipulations are handled apart from * The rlist element is undefined; list manipulations are handled apart from
* the initial copy. */ * the initial copy. */
struct audit_entry *audit_dupe_rule(struct audit_krule *old, struct audit_entry *audit_dupe_rule(struct audit_krule *old)
struct audit_watch *watch)
{ {
u32 fcount = old->field_count; u32 fcount = old->field_count;
struct audit_entry *entry; struct audit_entry *entry;
...@@ -768,8 +768,8 @@ struct audit_entry *audit_dupe_rule(struct audit_krule *old, ...@@ -768,8 +768,8 @@ struct audit_entry *audit_dupe_rule(struct audit_krule *old,
new->prio = old->prio; new->prio = old->prio;
new->buflen = old->buflen; new->buflen = old->buflen;
new->inode_f = old->inode_f; new->inode_f = old->inode_f;
new->watch = NULL;
new->field_count = old->field_count; new->field_count = old->field_count;
/* /*
* note that we are OK with not refcounting here; audit_match_tree() * note that we are OK with not refcounting here; audit_match_tree()
* never dereferences tree and we can't get false positives there * never dereferences tree and we can't get false positives there
...@@ -810,9 +810,9 @@ struct audit_entry *audit_dupe_rule(struct audit_krule *old, ...@@ -810,9 +810,9 @@ struct audit_entry *audit_dupe_rule(struct audit_krule *old,
} }
} }
if (watch) { if (old->watch) {
audit_get_watch(watch); audit_get_watch(old->watch);
new->watch = watch; new->watch = old->watch;
} }
return entry; return entry;
...@@ -865,7 +865,7 @@ static inline int audit_add_rule(struct audit_entry *entry) ...@@ -865,7 +865,7 @@ static inline int audit_add_rule(struct audit_entry *entry)
struct audit_watch *watch = entry->rule.watch; struct audit_watch *watch = entry->rule.watch;
struct audit_tree *tree = entry->rule.tree; struct audit_tree *tree = entry->rule.tree;
struct list_head *list; struct list_head *list;
int h, err; int err;
#ifdef CONFIG_AUDITSYSCALL #ifdef CONFIG_AUDITSYSCALL
int dont_count = 0; int dont_count = 0;
...@@ -888,15 +888,11 @@ static inline int audit_add_rule(struct audit_entry *entry) ...@@ -888,15 +888,11 @@ static inline int audit_add_rule(struct audit_entry *entry)
if (watch) { if (watch) {
/* audit_filter_mutex is dropped and re-taken during this call */ /* audit_filter_mutex is dropped and re-taken during this call */
err = audit_add_watch(&entry->rule); err = audit_add_watch(&entry->rule, &list);
if (err) { if (err) {
mutex_unlock(&audit_filter_mutex); mutex_unlock(&audit_filter_mutex);
goto error; goto error;
} }
/* entry->rule.watch may have changed during audit_add_watch() */
watch = entry->rule.watch;
h = audit_hash_ino((u32)audit_watch_inode(watch));
list = &audit_inode_hash[h];
} }
if (tree) { if (tree) {
err = audit_add_tree_rule(&entry->rule); err = audit_add_tree_rule(&entry->rule);
...@@ -948,7 +944,6 @@ static inline int audit_del_rule(struct audit_entry *entry) ...@@ -948,7 +944,6 @@ static inline int audit_del_rule(struct audit_entry *entry)
struct audit_watch *watch = entry->rule.watch; struct audit_watch *watch = entry->rule.watch;
struct audit_tree *tree = entry->rule.tree; struct audit_tree *tree = entry->rule.tree;
struct list_head *list; struct list_head *list;
LIST_HEAD(inotify_list);
int ret = 0; int ret = 0;
#ifdef CONFIG_AUDITSYSCALL #ifdef CONFIG_AUDITSYSCALL
int dont_count = 0; int dont_count = 0;
...@@ -968,7 +963,7 @@ static inline int audit_del_rule(struct audit_entry *entry) ...@@ -968,7 +963,7 @@ static inline int audit_del_rule(struct audit_entry *entry)
} }
if (e->rule.watch) if (e->rule.watch)
audit_remove_watch_rule(&e->rule, &inotify_list); audit_remove_watch_rule(&e->rule);
if (e->rule.tree) if (e->rule.tree)
audit_remove_tree_rule(&e->rule); audit_remove_tree_rule(&e->rule);
...@@ -986,9 +981,6 @@ static inline int audit_del_rule(struct audit_entry *entry) ...@@ -986,9 +981,6 @@ static inline int audit_del_rule(struct audit_entry *entry)
#endif #endif
mutex_unlock(&audit_filter_mutex); mutex_unlock(&audit_filter_mutex);
if (!list_empty(&inotify_list))
audit_inotify_unregister(&inotify_list);
out: out:
if (watch) if (watch)
audit_put_watch(watch); /* match initial get */ audit_put_watch(watch); /* match initial get */
...@@ -1322,30 +1314,23 @@ static int update_lsm_rule(struct audit_krule *r) ...@@ -1322,30 +1314,23 @@ static int update_lsm_rule(struct audit_krule *r)
{ {
struct audit_entry *entry = container_of(r, struct audit_entry, rule); struct audit_entry *entry = container_of(r, struct audit_entry, rule);
struct audit_entry *nentry; struct audit_entry *nentry;
struct audit_watch *watch;
struct audit_tree *tree;
int err = 0; int err = 0;
if (!security_audit_rule_known(r)) if (!security_audit_rule_known(r))
return 0; return 0;
watch = r->watch; nentry = audit_dupe_rule(r);
tree = r->tree;
nentry = audit_dupe_rule(r, watch);
if (IS_ERR(nentry)) { if (IS_ERR(nentry)) {
/* save the first error encountered for the /* save the first error encountered for the
* return value */ * return value */
err = PTR_ERR(nentry); err = PTR_ERR(nentry);
audit_panic("error updating LSM filters"); audit_panic("error updating LSM filters");
if (watch) if (r->watch)
list_del(&r->rlist); list_del(&r->rlist);
list_del_rcu(&entry->list); list_del_rcu(&entry->list);
list_del(&r->list); list_del(&r->list);
} else { } else {
if (watch) { if (r->watch || r->tree)
list_add(&nentry->rule.rlist, audit_watch_rules(watch));
list_del(&r->rlist);
} else if (tree)
list_replace_init(&r->rlist, &nentry->rule.rlist); list_replace_init(&r->rlist, &nentry->rule.rlist);
list_replace_rcu(&entry->list, &nentry->list); list_replace_rcu(&entry->list, &nentry->list);
list_replace(&r->list, &nentry->rule.list); list_replace(&r->list, &nentry->rule.list);
......
...@@ -168,12 +168,12 @@ struct audit_context { ...@@ -168,12 +168,12 @@ struct audit_context {
int in_syscall; /* 1 if task is in a syscall */ int in_syscall; /* 1 if task is in a syscall */
enum audit_state state, current_state; enum audit_state state, current_state;
unsigned int serial; /* serial number for record */ unsigned int serial; /* serial number for record */
struct timespec ctime; /* time of syscall entry */
int major; /* syscall number */ int major; /* syscall number */
struct timespec ctime; /* time of syscall entry */
unsigned long argv[4]; /* syscall arguments */ unsigned long argv[4]; /* syscall arguments */
int return_valid; /* return code is valid */
long return_code;/* syscall return code */ long return_code;/* syscall return code */
u64 prio; u64 prio;
int return_valid; /* return code is valid */
int name_count; int name_count;
struct audit_names names[AUDIT_NAMES]; struct audit_names names[AUDIT_NAMES];
char * filterkey; /* key for rule that triggered record */ char * filterkey; /* key for rule that triggered record */
...@@ -198,8 +198,8 @@ struct audit_context { ...@@ -198,8 +198,8 @@ struct audit_context {
char target_comm[TASK_COMM_LEN]; char target_comm[TASK_COMM_LEN];
struct audit_tree_refs *trees, *first_trees; struct audit_tree_refs *trees, *first_trees;
int tree_count;
struct list_head killed_trees; struct list_head killed_trees;
int tree_count;
int type; int type;
union { union {
...@@ -549,9 +549,8 @@ static int audit_filter_rules(struct task_struct *tsk, ...@@ -549,9 +549,8 @@ static int audit_filter_rules(struct task_struct *tsk,
} }
break; break;
case AUDIT_WATCH: case AUDIT_WATCH:
if (name && audit_watch_inode(rule->watch) != (unsigned long)-1) if (name)
result = (name->dev == audit_watch_dev(rule->watch) && result = audit_watch_compare(rule->watch, name->ino, name->dev);
name->ino == audit_watch_inode(rule->watch));
break; break;
case AUDIT_DIR: case AUDIT_DIR:
if (ctx) if (ctx)
...@@ -1726,7 +1725,7 @@ static inline void handle_one(const struct inode *inode) ...@@ -1726,7 +1725,7 @@ static inline void handle_one(const struct inode *inode)
struct audit_tree_refs *p; struct audit_tree_refs *p;
struct audit_chunk *chunk; struct audit_chunk *chunk;
int count; int count;
if (likely(list_empty(&inode->inotify_watches))) if (likely(hlist_empty(&inode->i_fsnotify_mark_entries)))
return; return;
context = current->audit_context; context = current->audit_context;
p = context->trees; p = context->trees;
...@@ -1769,7 +1768,7 @@ retry: ...@@ -1769,7 +1768,7 @@ retry:
seq = read_seqbegin(&rename_lock); seq = read_seqbegin(&rename_lock);
for(;;) { for(;;) {
struct inode *inode = d->d_inode; struct inode *inode = d->d_inode;
if (inode && unlikely(!list_empty(&inode->inotify_watches))) { if (inode && unlikely(!hlist_empty(&inode->i_fsnotify_mark_entries))) {
struct audit_chunk *chunk; struct audit_chunk *chunk;
chunk = audit_tree_lookup(inode); chunk = audit_tree_lookup(inode);
if (chunk) { if (chunk) {
......
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