};
struct audit_parent {
- struct list_head ilist; /* tmp list used to free parents */
struct list_head watches; /* anchor for audit_watch->wlist */
- struct fsnotify_mark_entry mark; /* fsnotify mark on the inode */
- unsigned flags; /* status flags */
+ struct fsnotify_mark mark; /* fsnotify mark on the inode */
};
/* fsnotify handle. */
struct fsnotify_group *audit_watch_group;
-/*
- * audit_parent status flags:
- *
- * AUDIT_PARENT_INVALID - set anytime rules/watches are auto-removed due to
- * a filesystem event to ensure we're adding audit watches to a valid parent.
- * Technically not needed for FS_DELETE_SELF or FS_UNMOUNT events, as we cannot
- * receive them while we have nameidata, but must be used for FS_MOVE_SELF which
- * we can receive while holding nameidata.
- */
-#define AUDIT_PARENT_INVALID 0x001
-
/* fsnotify events we care about. */
#define AUDIT_FS_WATCH (FS_MOVE | FS_CREATE | FS_DELETE | FS_DELETE_SELF |\
FS_MOVE_SELF | FS_EVENT_ON_CHILD)
kfree(parent);
}
-static void audit_watch_free_mark(struct fsnotify_mark_entry *entry)
+static void audit_watch_free_mark(struct fsnotify_mark *entry)
{
struct audit_parent *parent;
static inline struct audit_parent *audit_find_parent(struct inode *inode)
{
struct audit_parent *parent = NULL;
- struct fsnotify_mark_entry *entry;
-
- spin_lock(&inode->i_lock);
- entry = fsnotify_find_mark_entry(audit_watch_group, inode);
- spin_unlock(&inode->i_lock);
+ struct fsnotify_mark *entry;
+ entry = fsnotify_find_inode_mark(audit_watch_group, inode);
if (entry)
parent = container_of(entry, struct audit_parent, mark);
return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&parent->watches);
- parent->flags = 0;
fsnotify_init_mark(&parent->mark, audit_watch_free_mark);
parent->mark.mask = AUDIT_FS_WATCH;
- /* grab a ref so fsnotify mark hangs around until we take audit_filter_mutex */
- audit_get_parent(parent);
- ret = fsnotify_add_mark(&parent->mark, audit_watch_group, inode);
+ ret = fsnotify_add_mark(&parent->mark, audit_watch_group, inode, NULL, 0);
if (ret < 0) {
audit_free_parent(parent);
return ERR_PTR(ret);
struct audit_entry *e;
mutex_lock(&audit_filter_mutex);
- parent->flags |= AUDIT_PARENT_INVALID;
list_for_each_entry_safe(w, nextw, &parent->watches, wlist) {
list_for_each_entry_safe(r, nextr, &w->rules, rlist) {
e = container_of(r, struct audit_entry, rule);
}
mutex_unlock(&audit_filter_mutex);
- fsnotify_destroy_mark_by_entry(&parent->mark);
-}
-
-/* Unregister inotify watches for parents on in_list.
- * Generates an FS_IGNORED event. */
-void audit_watch_inotify_unregister(struct list_head *in_list)
-{
- struct audit_parent *p, *n;
-
- list_for_each_entry_safe(p, n, in_list, ilist) {
- list_del(&p->ilist);
- fsnotify_destroy_mark_by_entry(&p->mark);
- /* matches the get in audit_remove_watch_rule() */
- audit_put_parent(p);
- }
+ fsnotify_destroy_mark(&parent->mark);
}
/* Get path information necessary for adding watches. */
goto error;
}
+ mutex_lock(&audit_filter_mutex);
+
/* update watch filter fields */
if (ndw) {
watch->dev = ndw->path.dentry->d_inode->i_sb->s_dev;
watch->ino = ndw->path.dentry->d_inode->i_ino;
}
- /* The audit_filter_mutex must not be held during inotify calls because
- * we hold it during inotify event callback processing. If an existing
- * inotify watch is found, inotify_find_watch() grabs a reference before
- * returning.
- */
+ /* either find an old parent or attach a new one */
parent = audit_find_parent(ndp->path.dentry->d_inode);
if (!parent) {
parent = audit_init_parent(ndp);
if (IS_ERR(parent)) {
- /* caller expects mutex locked */
- mutex_lock(&audit_filter_mutex);
ret = PTR_ERR(parent);
goto error;
}
}
- mutex_lock(&audit_filter_mutex);
-
- /* parent was moved before we took audit_filter_mutex */
- if (parent->flags & AUDIT_PARENT_INVALID)
- ret = -ENOENT;
- else
- audit_add_to_parent(krule, parent);
+ audit_add_to_parent(krule, parent);
/* match get in audit_find_parent or audit_init_parent */
audit_put_parent(parent);
}
-void audit_remove_watch_rule(struct audit_krule *krule, struct list_head *list)
+void audit_remove_watch_rule(struct audit_krule *krule)
{
struct audit_watch *watch = krule->watch;
struct audit_parent *parent = watch->parent;
audit_remove_watch(watch);
if (list_empty(&parent->watches)) {
- /* Put parent on the un-registration list.
- * Grab a reference before releasing
- * audit_filter_mutex, to be released in
- * audit_watch_inotify_unregister().
- * If filesystem is going away, just leave
- * the sucker alone, eviction will take
- * care of it. */
audit_get_parent(parent);
- list_add(&parent->ilist, list);
+ fsnotify_destroy_mark(&parent->mark);
+ audit_put_parent(parent);
}
}
}
-static bool audit_watch_should_send_event(struct fsnotify_group *group, struct inode *inode, __u32 mask)
+static bool audit_watch_should_send_event(struct fsnotify_group *group, struct inode *inode,
+ struct fsnotify_mark *inode_mark,
+ struct fsnotify_mark *vfsmount_mark,
+ __u32 mask, void *data, int data_type)
{
- struct fsnotify_mark_entry *entry;
- bool send;
-
- spin_lock(&inode->i_lock);
- entry = fsnotify_find_mark_entry(group, inode);
- spin_unlock(&inode->i_lock);
- if (!entry)
- return false;
-
- mask = (mask & ~FS_EVENT_ON_CHILD);
- send = (entry->mask & mask);
-
- /* find took a reference */
- fsnotify_put_mark(entry);
-
- return send;
+ return true;
}
/* Update watch data in audit rules based on fsnotify events. */
-static int audit_watch_handle_event(struct fsnotify_group *group, struct fsnotify_event *event)
+static int audit_watch_handle_event(struct fsnotify_group *group,
+ struct fsnotify_mark *inode_mark,
+ struct fsnotify_mark *vfsmount_mark,
+ struct fsnotify_event *event)
{
struct inode *inode;
__u32 mask = event->mask;
const char *dname = event->file_name;
struct audit_parent *parent;
- BUG_ON(group != audit_watch_group);
+ parent = container_of(inode_mark, struct audit_parent, mark);
- parent = audit_find_parent(event->to_tell);
- if (unlikely(!parent))
- return 0;
+ BUG_ON(group != audit_watch_group);
switch (event->data_type) {
case (FSNOTIFY_EVENT_PATH):
audit_update_watch(parent, dname, (dev_t)-1, (unsigned long)-1, 1);
else if (mask & (FS_DELETE_SELF|FS_UNMOUNT|FS_MOVE_SELF))
audit_remove_parent_watches(parent);
- /* moved put_inotify_watch to freeing mark */
-
- /* matched the ref taken by audit_find_parent */
- audit_put_parent(parent);
return 0;
}
-static void audit_watch_freeing_mark(struct fsnotify_mark_entry *entry, struct fsnotify_group *group)
-{
- struct audit_parent *parent;
-
- parent = container_of(entry, struct audit_parent, mark);
- /* taken from audit_handle_ievent & FS_IGNORED please figure out what I match... */
- audit_put_parent(parent);
-}
-
static const struct fsnotify_ops audit_watch_fsnotify_ops = {
.should_send_event = audit_watch_should_send_event,
.handle_event = audit_watch_handle_event,
.free_group_priv = NULL,
- .freeing_mark = audit_watch_freeing_mark,
+ .freeing_mark = NULL,
.free_event_priv = NULL,
};
static int __init audit_watch_init(void)
{
- audit_watch_group = fsnotify_obtain_group(AUDIT_WATCH_GROUP_NUM, AUDIT_FS_WATCH,
- &audit_watch_fsnotify_ops);
+ audit_watch_group = fsnotify_alloc_group(&audit_watch_fsnotify_ops);
if (IS_ERR(audit_watch_group)) {
audit_watch_group = NULL;
audit_panic("cannot create audit fsnotify group");
}
return 0;
}
-subsys_initcall(audit_watch_init);
+device_initcall(audit_watch_init);