2009-12-03 05:53:20

by Eric Paris

[permalink] [raw]
Subject: [PATCH 1/5] fsnotify/vfsmount: add fsnotify fields to struct vfsmount

From: Andreas Gruenbacher <[email protected]>

This patch adds the list and mask fields needed to support vfsmount marks.
These are the same fields fsnotify needs on an inode. They are not used,
just declared and we note where the cleanup hook should be (the function is
not yet defined)

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Eric Paris <[email protected]>
---

fs/namespace.c | 4 ++++
fs/notify/fsnotify.c | 4 +---
include/linux/mount.h | 6 +++++-
3 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/fs/namespace.c b/fs/namespace.c
index 7d70d63..2bff3b1 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -29,6 +29,7 @@
#include <linux/log2.h>
#include <linux/idr.h>
#include <linux/fs_struct.h>
+#include <linux/fsnotify.h>
#include <asm/uaccess.h>
#include <asm/unistd.h>
#include "pnode.h"
@@ -150,6 +151,9 @@ struct vfsmount *alloc_vfsmnt(const char *name)
INIT_LIST_HEAD(&mnt->mnt_share);
INIT_LIST_HEAD(&mnt->mnt_slave_list);
INIT_LIST_HEAD(&mnt->mnt_slave);
+#ifdef CONFIG_FSNOTIFY
+ INIT_HLIST_HEAD(&mnt->mnt_fsnotify_marks);
+#endif
#ifdef CONFIG_SMP
mnt->mnt_writers = alloc_percpu(int);
if (!mnt->mnt_writers)
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index b076b06..8ed9fb5 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -162,9 +162,7 @@ static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt)
if (!mnt)
return false;

- /* hook in this when mnt->mnt_fsnotify_mask is defined */
- /* return (test_mask & path->mnt->mnt_fsnotify_mask); */
- return false;
+ return (test_mask & mnt->mnt_fsnotify_mask);
}
/*
* This is the main call to fsnotify. The VFS calls into hook specific functions
diff --git a/include/linux/mount.h b/include/linux/mount.h
index 5d52753..9d542fc 100644
--- a/include/linux/mount.h
+++ b/include/linux/mount.h
@@ -45,7 +45,11 @@ struct vfsmount {
struct list_head mnt_mounts; /* list of children, anchored here */
struct list_head mnt_child; /* and going through their mnt_child */
int mnt_flags;
- /* 4 bytes hole on 64bits arches */
+ /* 4 bytes hole on 64bits arches without fsnotify */
+#ifdef CONFIG_FSNOTIFY
+ __u32 mnt_fsnotify_mask;
+ struct hlist_head mnt_fsnotify_marks;
+#endif
const char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */
struct list_head mnt_list;
struct list_head mnt_expire; /* link in fs-specific expiry list */


2009-12-03 05:53:30

by Eric Paris

[permalink] [raw]
Subject: [PATCH 2/5] fsnotify: vfsmount marks generic functions

Much like inode-mark.c has all of the code dealing with marks on inodes
this patch adds a vfsmount-mark.c which has similar code but is intended
for marks on vfsmounts.

Signed-off-by: Eric Paris <[email protected]>
---

fs/notify/Makefile | 2
fs/notify/fsnotify.h | 6 +
fs/notify/mark.c | 20 ++--
fs/notify/vfsmount_mark.c | 171 ++++++++++++++++++++++++++++++++++++++
include/linux/fsnotify_backend.h | 2
5 files changed, 191 insertions(+), 10 deletions(-)
create mode 100644 fs/notify/vfsmount_mark.c

diff --git a/fs/notify/Makefile b/fs/notify/Makefile
index 8f7f3b0..ae5f33a 100644
--- a/fs/notify/Makefile
+++ b/fs/notify/Makefile
@@ -1,5 +1,5 @@
obj-$(CONFIG_FSNOTIFY) += fsnotify.o notification.o group.o inode_mark.o \
- mark.o
+ mark.o vfsmount_mark.o

obj-y += dnotify/
obj-y += inotify/
diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h
index 7c7a904..38f3fb5 100644
--- a/fs/notify/fsnotify.h
+++ b/fs/notify/fsnotify.h
@@ -24,6 +24,10 @@ extern void fsnotify_flush_notify(struct fsnotify_group *group);
extern int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
struct fsnotify_group *group, struct inode *inode,
int allow_dups);
+/* add a mark to a vfsmount */
+extern int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
+ struct fsnotify_group *group, struct vfsmount *mnt,
+ int allow_dups);

/* add a group to the inode group list */
extern void fsnotify_add_inode_group(struct fsnotify_group *group);
@@ -32,6 +36,8 @@ extern void fsnotify_add_vfsmount_group(struct fsnotify_group *group);
/* final kfree of a group */
extern void fsnotify_final_destroy_group(struct fsnotify_group *group);

+/* vfsmount specific destruction of a mark */
+extern void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark);
/* inode specific destruction of a mark */
extern void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark);
/* run the list of all marks associated with inode and flag them to be freed */
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index 57bb1d7..d296ec9 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -115,15 +115,11 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
void fsnotify_destroy_mark(struct fsnotify_mark *mark)
{
struct fsnotify_group *group;
- struct inode *inode;
+ struct inode *inode = NULL;

spin_lock(&mark->lock);

group = mark->group;
- inode = mark->i.inode;
-
- BUG_ON(group && !inode);
- BUG_ON(!group && inode);

/* if !group something else already marked this to die */
if (!group) {
@@ -136,8 +132,11 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark)

spin_lock(&group->mark_lock);

- if (mark->flags & FSNOTIFY_MARK_FLAG_INODE)
+ if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) {
fsnotify_destroy_inode_mark(mark);
+ inode = mark->i.inode;
+ } else if (mark->flags & FSNOTIFY_MARK_FLAG_VFSMOUNT)
+ fsnotify_destroy_vfsmount_mark(mark);
else
BUG();

@@ -169,8 +168,8 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark)
* is just a lazy update (and could be a perf win...)
*/

-
- iput(inode);
+ if (inode)
+ iput(inode);

/*
* it's possible that this group tried to destroy itself, but this
@@ -192,7 +191,6 @@ int fsnotify_add_mark(struct fsnotify_mark *mark,
{
int ret = 0;

- BUG_ON(mnt);
BUG_ON(inode && mnt);
BUG_ON(!inode && !mnt);

@@ -223,6 +221,10 @@ int fsnotify_add_mark(struct fsnotify_mark *mark,
ret = fsnotify_add_inode_mark(mark, group, inode, allow_dups);
if (ret)
goto err;
+ } else if (mnt) {
+ ret = fsnotify_add_vfsmount_mark(mark, group, mnt, allow_dups);
+ if (ret)
+ goto err;
} else {
BUG();
}
diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c
new file mode 100644
index 0000000..1b61d0a
--- /dev/null
+++ b/fs/notify/vfsmount_mark.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2008 Red Hat, Inc., Eric Paris <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mount.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/writeback.h> /* for inode_lock */
+
+#include <asm/atomic.h>
+
+#include <linux/fsnotify_backend.h>
+#include "fsnotify.h"
+
+void fsnotify_clear_marks_by_mount(struct vfsmount *mnt)
+{
+ struct fsnotify_mark *mark, *lmark;
+ struct hlist_node *pos, *n;
+ LIST_HEAD(free_list);
+
+ spin_lock(&mnt->mnt_root->d_lock);
+ hlist_for_each_entry_safe(mark, pos, n, &mnt->mnt_fsnotify_marks, m.m_list) {
+ list_add(&mark->m.free_m_list, &free_list);
+ hlist_del_init(&mark->m.m_list);
+ fsnotify_get_mark(mark);
+ }
+ spin_unlock(&mnt->mnt_root->d_lock);
+
+ list_for_each_entry_safe(mark, lmark, &free_list, m.free_m_list) {
+ fsnotify_destroy_mark(mark);
+ fsnotify_put_mark(mark);
+ }
+}
+
+/*
+ * Recalculate the mask of events relevant to a given vfsmount locked.
+ */
+static void fsnotify_recalc_vfsmount_mask_locked(struct vfsmount *mnt)
+{
+ struct fsnotify_mark *mark;
+ struct hlist_node *pos;
+ __u32 new_mask = 0;
+
+ assert_spin_locked(&mnt->mnt_root->d_lock);
+
+ hlist_for_each_entry(mark, pos, &mnt->mnt_fsnotify_marks, m.m_list)
+ new_mask |= mark->mask;
+ mnt->mnt_fsnotify_mask = new_mask;
+}
+
+/*
+ * Recalculate the mnt->mnt_fsnotify_mask, or the mask of all FS_* event types
+ * any notifier is interested in hearing for this mount point
+ */
+void fsnotify_recalc_vfsmount_mask(struct vfsmount *mnt)
+{
+ spin_lock(&mnt->mnt_root->d_lock);
+ fsnotify_recalc_vfsmount_mask_locked(mnt);
+ spin_unlock(&mnt->mnt_root->d_lock);
+}
+
+void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark)
+{
+ struct vfsmount *mnt = mark->m.mnt;
+
+ assert_spin_locked(&mark->lock);
+ assert_spin_locked(&mark->group->mark_lock);
+
+ spin_lock(&mnt->mnt_root->d_lock);
+
+ hlist_del_init(&mark->m.m_list);
+ mark->m.mnt = NULL;
+
+ fsnotify_recalc_vfsmount_mask_locked(mnt);
+
+ spin_unlock(&mnt->mnt_root->d_lock);
+}
+
+static struct fsnotify_mark *fsnotify_find_vfsmount_mark_locked(struct fsnotify_group *group,
+ struct vfsmount *mnt)
+{
+ struct fsnotify_mark *mark;
+ struct hlist_node *pos;
+
+ assert_spin_locked(&mnt->mnt_root->d_lock);
+
+ hlist_for_each_entry(mark, pos, &mnt->mnt_fsnotify_marks, m.m_list) {
+ if (mark->group == group) {
+ fsnotify_get_mark(mark);
+ return mark;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * given a group and vfsmount, find the mark associated with that combination.
+ * if found take a reference to that mark and return it, else return NULL
+ */
+struct fsnotify_mark *fsnotify_find_vfsmount_mark(struct fsnotify_group *group,
+ struct vfsmount *mnt)
+{
+ struct fsnotify_mark *mark;
+
+ spin_lock(&mnt->mnt_root->d_lock);
+ mark = fsnotify_find_vfsmount_mark_locked(group, mnt);
+ spin_unlock(&mnt->mnt_root->d_lock);
+
+ return mark;
+}
+
+/*
+ * Attach an initialized mark to a given group and vfsmount.
+ * These marks may be used for the fsnotify backend to determine which
+ * event types should be delivered to which groups.
+ */
+int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
+ struct fsnotify_group *group, struct vfsmount *mnt,
+ int allow_dups)
+{
+ struct fsnotify_mark *lmark = NULL;
+ int ret = 0;
+
+ mark->flags = FSNOTIFY_MARK_FLAG_VFSMOUNT;
+
+ /*
+ * LOCKING ORDER!!!!
+ * mark->lock
+ * group->mark_lock
+ * mnt->mnt_root->d_lock
+ */
+ assert_spin_locked(&mark->lock);
+ assert_spin_locked(&group->mark_lock);
+
+ spin_lock(&mnt->mnt_root->d_lock);
+
+ if (!allow_dups)
+ lmark = fsnotify_find_vfsmount_mark_locked(group, mnt);
+ if (!lmark) {
+ mark->m.mnt = mnt;
+
+ hlist_add_head(&mark->m.m_list, &mnt->mnt_fsnotify_marks);
+
+ fsnotify_recalc_vfsmount_mask_locked(mnt);
+ } else {
+ ret = -EEXIST;
+ }
+
+ spin_unlock(&mnt->mnt_root->d_lock);
+
+ return ret;
+}
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index 27cccbe..f21ff1b 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -360,6 +360,8 @@ extern struct fsnotify_event *fsnotify_remove_notify_event(struct fsnotify_group

/* functions used to manipulate the marks attached to inodes */

+/* run all marks associated with a vfsmount and update mnt->mnt_fsnotify_mask */
+extern void fsnotify_recalc_vfsmount_mask(struct vfsmount *mnt);
/* run all marks associated with an inode and update inode->i_fsnotify_mask */
extern void fsnotify_recalc_inode_mask(struct inode *inode);
extern void fsnotify_init_mark(struct fsnotify_mark *mark, void (*free_mark)(struct fsnotify_mark *mark));

2009-12-03 05:53:41

by Eric Paris

[permalink] [raw]
Subject: [PATCH 4/5] fanotify: should_send_event needs to handle vfsmounts

currently should_send_event in fanotify only cares about marks on inodes.
This patch extends that interface to indicate that it cares about events
that happened on vfsmounts.

Signed-off-by: Eric Paris <[email protected]>
---

fs/notify/fanotify/fanotify.c | 56 +++++++++++++++++++++++++++++++-------
include/linux/fsnotify_backend.h | 2 +
2 files changed, 47 insertions(+), 11 deletions(-)

diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index aa5e926..202be8a 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -2,6 +2,7 @@
#include <linux/fsnotify_backend.h>
#include <linux/init.h>
#include <linux/kernel.h> /* UINT_MAX */
+#include <linux/mount.h>
#include <linux/types.h>

#include "fanotify.h"
@@ -99,24 +100,35 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e
return ret;
}

-static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *inode,
- struct vfsmount *mnt, __u32 mask, void *data,
- int data_type)
+static bool should_send_vfsmount_event(struct fsnotify_group *group, struct vfsmount *mnt,
+ __u32 mask)
{
struct fsnotify_mark *fsn_mark;
bool send;

- pr_debug("%s: group=%p inode=%p mask=%x data=%p data_type=%d\n",
- __func__, group, inode, mask, data, data_type);
+ pr_debug("%s: group=%p vfsmount=%p mask=%x\n",
+ __func__, group, mnt, mask);

- /* sorry, fanotify only gives a damn about files and dirs */
- if (!S_ISREG(inode->i_mode) &&
- !S_ISDIR(inode->i_mode))
+ fsn_mark = fsnotify_find_vfsmount_mark(group, mnt);
+ if (!fsn_mark)
return false;

- /* if we don't have enough info to send an event to userspace say no */
- if (data_type != FSNOTIFY_EVENT_PATH)
- return false;
+ send = (mask & fsn_mark->mask);
+
+ /* find took a reference */
+ fsnotify_put_mark(fsn_mark);
+
+ return send;
+}
+
+static bool should_send_inode_event(struct fsnotify_group *group, struct inode *inode,
+ __u32 mask)
+{
+ struct fsnotify_mark *fsn_mark;
+ bool send;
+
+ pr_debug("%s: group=%p inode=%p mask=%x\n",
+ __func__, group, inode, mask);

fsn_mark = fsnotify_find_inode_mark(group, inode);
if (!fsn_mark)
@@ -142,6 +154,28 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, struct inod
return send;
}

+static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *to_tell,
+ struct vfsmount *mnt, __u32 mask, void *data,
+ int data_type)
+{
+ pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x data=%p data_type=%d\n",
+ __func__, group, to_tell, mnt, mask, data, data_type);
+
+ /* sorry, fanotify only gives a damn about files and dirs */
+ if (!S_ISREG(to_tell->i_mode) &&
+ !S_ISDIR(to_tell->i_mode))
+ return false;
+
+ /* if we don't have enough info to send an event to userspace say no */
+ if (data_type != FSNOTIFY_EVENT_PATH)
+ return false;
+
+ if (mnt)
+ return should_send_vfsmount_event(group, mnt, mask);
+ else
+ return should_send_inode_event(group, to_tell, mask);
+}
+
const struct fsnotify_ops fanotify_fsnotify_ops = {
.handle_event = fanotify_handle_event,
.should_send_event = fanotify_should_send_event,
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index 1af42cb..2d2f015 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -368,6 +368,8 @@ extern void fsnotify_recalc_inode_mask(struct inode *inode);
extern void fsnotify_init_mark(struct fsnotify_mark *mark, void (*free_mark)(struct fsnotify_mark *mark));
/* find (and take a reference) to a mark associated with group and inode */
extern struct fsnotify_mark *fsnotify_find_inode_mark(struct fsnotify_group *group, struct inode *inode);
+/* find (and take a reference) to a mark associated with group and vfsmount */
+extern struct fsnotify_mark *fsnotify_find_vfsmount_mark(struct fsnotify_group *group, struct vfsmount *mnt);
/* copy the values from old into new */
extern void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old);
/* attach the mark to both the group and the inode */

2009-12-03 05:53:46

by Eric Paris

[permalink] [raw]
Subject: [PATCH 5/5] fanotify: infrastructure to add an remove marks on vfsmounts

infrastructure work to add and remove marks on vfsmounts. This should get
every set up except wiring the functions to the syscalls.

Signed-off-by: Eric Paris <[email protected]>
---

fs/notify/fanotify/fanotify_user.c | 185 ++++++++++++++++++++++++++----------
1 files changed, 133 insertions(+), 52 deletions(-)

diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 79376ec..f5bf1db 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -300,31 +300,83 @@ out:
return ret;
}

-static int fanotify_remove_mark(struct fsnotify_group *group,
- struct inode *inode,
- __u32 mask)
+static void fanotify_update_object_mask(struct fsnotify_group *group,
+ struct inode *inode,
+ struct vfsmount *mnt,
+ struct fsnotify_mark *fsn_mark,
+ unsigned int flags,
+ __u32 mask)
{
- struct fsnotify_mark *fsn_mark;
- __u32 new_mask;
-
- pr_debug("%s: group=%p inode=%p mask=%x\n", __func__,
- group, inode, mask);
+ __u32 old_mask, new_mask;

- fsn_mark = fsnotify_find_inode_mark(group, inode);
- if (!fsn_mark)
- return -ENOENT;
+ pr_debug("%s: group=%p inode=%p mnt=%p fsn_mark=%p flags=%x mask=%x\n",
+ __func__, group, inode, mnt, fsn_mark, flags, mask);

spin_lock(&fsn_mark->lock);
- fsn_mark->mask &= ~mask;
+ old_mask = fsn_mark->mask;
+ if (flags & FAN_MARK_ADD)
+ fsn_mark->mask |= mask;
+ else if (flags & FAN_MARK_REMOVE)
+ fsn_mark->mask &= ~mask;
+ else
+ BUG();
new_mask = fsn_mark->mask;
spin_unlock(&fsn_mark->lock);

if (!new_mask)
fsnotify_destroy_mark(fsn_mark);
+
+ /* we made changes to a mask, update the group mask and the object mask
+ * so things happen quickly. */
+ if (old_mask != new_mask) {
+ __u32 dropped, do_object, do_group;
+
+ /* more bits in old than in new? */
+ dropped = (old_mask & ~new_mask);
+ /* more bits in this fsn_mark than the group? */
+ do_group = (new_mask & ~group->mask);
+
+ if (inode) {
+ /* more bits in this fsn_mark than the object's mask? */
+ do_object = (new_mask & ~inode->i_fsnotify_mask);
+ /* update the object with this new fsn_mark */
+ if (dropped || do_object)
+ fsnotify_recalc_inode_mask(inode);
+ } else if (mnt) {
+ /* more bits in this fsn_mark than the object's mask? */
+ do_object = (new_mask & ~mnt->mnt_fsnotify_mask);
+ /* update the object with this new fsn_mark */
+ if (dropped || do_object)
+ fsnotify_recalc_vfsmount_mask(mnt);
+ } else {
+ BUG();
+ }
+
+ /* update the group mask with the new mask */
+ if (dropped || do_group)
+ fsnotify_recalc_group_mask(group);
+ }
+}
+
+static int fanotify_remove_mark(struct fsnotify_group *group, struct inode *inode,
+ struct vfsmount *mnt, unsigned int flags, __u32 mask)
+{
+ struct fsnotify_mark *fsn_mark = NULL;
+
+ BUG_ON(inode && mnt);
+ BUG_ON(!inode && !mnt);
+
+ if (inode)
+ fsn_mark = fsnotify_find_inode_mark(group, inode);
+ else if (mnt)
+ fsn_mark = fsnotify_find_vfsmount_mark(group, mnt);
else
- fsnotify_recalc_inode_mask(inode);
+ BUG();

- fsnotify_recalc_group_mask(group);
+ if (!fsn_mark)
+ return -ENOENT;
+
+ fanotify_update_object_mask(group, inode, mnt, fsn_mark, flags, mask);

/* matches the fsnotify_find_inode_mark() */
fsnotify_put_mark(fsn_mark);
@@ -332,22 +384,48 @@ static int fanotify_remove_mark(struct fsnotify_group *group,
return 0;
}

-static int fanotify_add_mark(struct fsnotify_group *group,
- struct inode *inode,
- __u32 mask)
+static struct fsnotify_mark *fanotify_add_vfsmount_mark(struct fsnotify_group *group,
+ struct vfsmount *mnt)
{
struct fsnotify_mark *fsn_mark;
- __u32 old_mask, new_mask;
- int ret;

- pr_debug("%s: group=%p inode=%p mask=%x\n", __func__,
- group, inode, mask);
+ fsn_mark = fsnotify_find_vfsmount_mark(group, mnt);
+ if (!fsn_mark) {
+ struct fsnotify_mark *new_fsn_mark;
+ int ret;
+
+ fsn_mark = ERR_PTR(-ENOMEM);
+ new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
+ if (!new_fsn_mark)
+ goto out;
+
+ fsnotify_init_mark(new_fsn_mark, fanotify_free_mark);
+ ret = fsnotify_add_mark(new_fsn_mark, group, NULL, mnt, 0);
+ if (ret) {
+ fsn_mark = ERR_PTR(ret);
+ fanotify_free_mark(new_fsn_mark);
+ goto out;
+ }
+
+ fsn_mark = new_fsn_mark;
+ }
+out:
+ return fsn_mark;
+}
+
+static struct fsnotify_mark *fanotify_add_inode_mark(struct fsnotify_group *group,
+ struct inode *inode)
+{
+ struct fsnotify_mark *fsn_mark;
+
+ pr_debug("%s: group=%p inode=%p\n", __func__, group, inode);

fsn_mark = fsnotify_find_inode_mark(group, inode);
if (!fsn_mark) {
struct fsnotify_mark *new_fsn_mark;
+ int ret;

- ret = -ENOMEM;
+ fsn_mark = ERR_PTR(-ENOMEM);
new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL);
if (!new_fsn_mark)
goto out;
@@ -355,57 +433,60 @@ static int fanotify_add_mark(struct fsnotify_group *group,
fsnotify_init_mark(new_fsn_mark, fanotify_free_mark);
ret = fsnotify_add_mark(new_fsn_mark, group, inode, NULL, 0);
if (ret) {
+ fsn_mark = ERR_PTR(ret);
fanotify_free_mark(new_fsn_mark);
goto out;
}

fsn_mark = new_fsn_mark;
}
+out:
+ return fsn_mark;
+}

- ret = 0;
+static int fanotify_add_mark(struct fsnotify_group *group, struct inode *inode,
+ struct vfsmount *mnt, unsigned int flags, __u32 mask)
+{
+ struct fsnotify_mark *fsn_mark;

- spin_lock(&fsn_mark->lock);
- old_mask = fsn_mark->mask;
- fsn_mark->mask |= mask;
- new_mask = fsn_mark->mask;
- spin_unlock(&fsn_mark->lock);
+ pr_debug("%s: group=%p inode=%p mnt=%p flags=%x mask=%x\n",
+ __func__, group, inode, mnt, flags, mask);

- /* we made changes to a mask, update the group mask and the inode mask
- * so things happen quickly. */
- if (old_mask != new_mask) {
- /* more bits in old than in new? */
- int dropped = (old_mask & ~new_mask);
- /* more bits in this mark than the inode's mask? */
- int do_inode = (new_mask & ~inode->i_fsnotify_mask);
- /* more bits in this mark than the group? */
- int do_group = (new_mask & ~group->mask);
+ BUG_ON(inode && mnt);
+ BUG_ON(!inode && !mnt);

- /* update the inode with this new mark */
- if (dropped || do_inode)
- fsnotify_recalc_inode_mask(inode);
+ if (inode)
+ fsn_mark = fanotify_add_inode_mark(group, inode);
+ else if (mnt)
+ fsn_mark = fanotify_add_vfsmount_mark(group, mnt);
+ else
+ BUG();

- /* update the group mask with the new mask */
- if (dropped || do_group)
- fsnotify_recalc_group_mask(group);
- }
+ if (IS_ERR(fsn_mark))
+ goto out;
+
+ fanotify_update_object_mask(group, inode, mnt, fsn_mark, flags, mask);

/* match the init or the find.... */
fsnotify_put_mark(fsn_mark);
out:
- return ret;
+ return PTR_ERR(fsn_mark);
}

static int fanotify_update_mark(struct fsnotify_group *group,
- struct inode *inode, int flags,
- __u32 mask)
+ struct inode *inode, struct vfsmount *mnt,
+ int flags, __u32 mask)
{
- pr_debug("%s: group=%p inode=%p flags=%x mask=%x\n", __func__,
- group, inode, flags, mask);
+ pr_debug("%s: group=%p inode=%p mnt=%p flags=%x mask=%x\n",
+ __func__, group, inode, mnt, flags, mask);
+
+ BUG_ON(inode && mnt);
+ BUG_ON(!inode && !mnt);

if (flags & FAN_MARK_ADD)
- fanotify_add_mark(group, inode, mask);
+ fanotify_add_mark(group, inode, mnt, flags, mask);
else if (flags & FAN_MARK_REMOVE)
- fanotify_remove_mark(group, inode, mask);
+ fanotify_remove_mark(group, inode, mnt, flags, mask);
else
BUG();

@@ -507,7 +588,7 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags,
group = filp->private_data;

/* create/update an inode mark */
- ret = fanotify_update_mark(group, inode, flags, mask);
+ ret = fanotify_update_mark(group, inode, NULL, flags, mask);

path_put(&path);
fput_and_out:

2009-12-04 15:39:25

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 1/5] fsnotify/vfsmount: add fsnotify fields to struct vfsmount

On Thu, Dec 03, 2009 at 12:53:15AM -0500, Eric Paris wrote:
> From: Andreas Gruenbacher <[email protected]>
>
> This patch adds the list and mask fields needed to support vfsmount marks.
> These are the same fields fsnotify needs on an inode. They are not used,
> just declared and we note where the cleanup hook should be (the function is
> not yet defined)

What's the rationale for adding them?

2009-12-04 15:46:19

by Eric Paris

[permalink] [raw]
Subject: Re: [PATCH 1/5] fsnotify/vfsmount: add fsnotify fields to struct vfsmount

On Fri, 2009-12-04 at 10:39 -0500, Christoph Hellwig wrote:
> On Thu, Dec 03, 2009 at 12:53:15AM -0500, Eric Paris wrote:
> > From: Andreas Gruenbacher <[email protected]>
> >
> > This patch adds the list and mask fields needed to support vfsmount marks.
> > These are the same fields fsnotify needs on an inode. They are not used,
> > just declared and we note where the cleanup hook should be (the function is
> > not yet defined)
>
> What's the rationale for adding them?

As opposed to 'global' listeners I've discussed before, Andreas ask me
to write per mount listeners. It's somewhere in between global and per
inode. With this series One can now register to listen on a struct
vfsmount instead. I'll update this patch description to better explain
the point.

-Eric

2009-12-10 16:34:02

by Andreas Gruenbacher

[permalink] [raw]
Subject: Re: [PATCH 1/5] fsnotify/vfsmount: add fsnotify fields to struct vfsmount

On Friday 04 December 2009 16:39:25 Christoph Hellwig wrote:
> What's the rationale for adding them?

The idea is to provide a mechanism to watch for "all events", but in a
namespace aware way: for that, a process registers interest in all mount
points it can reach. With the previous hack, a global "I want it all" flag,
per-namespace listeners were not possible.

Root can use bind mounts onto themselves (e.g., mount --bind /foo/bar
/foo/bar) to watch arbitrary directories with the per-mount-point mechanism.
Mount as user will open up additional use cases.

It would be nice to be able to register interest in all events below a
directory which is not a mount point, too. The problem with that is that the
number of cached inodes below a given directory could be huge -- and we would
have to visit them all atomically in order to "mark" them, which is not
feasible. The per-mount-point mechanism will eventually give us *almost* the
same, and so I believe it's good enough.

Thanks,
Andreas

2009-12-15 11:12:25

by Niraj kumar

[permalink] [raw]
Subject: Re: [PATCH 1/5] fsnotify/vfsmount: add fsnotify fields to struct vfsmount

On Thu, Dec 10, 2009 at 10:01 PM, Andreas Gruenbacher <[email protected]> wrote:
> On Friday 04 December 2009 16:39:25 Christoph Hellwig wrote:
>> What's the rationale for adding them?
>
> The idea is to provide a mechanism to watch for "all events", but in a
> namespace aware way: for that, a process registers interest in all mount
> points it can reach. ?With the previous hack, a global "I want it all" flag,
> per-namespace listeners were not possible.
>
> Root can use bind mounts onto themselves (e.g., mount --bind /foo/bar
> /foo/bar) to watch arbitrary directories with the per-mount-point mechanism.
> Mount as user will open up additional use cases.
>
> It would be nice to be able to register interest in all events below a
> directory which is not a mount point, too. ?The problem with that is that the
> number of cached inodes below a given directory could be huge -- and we would
> have to visit them all atomically in order to "mark" them, which is not
> feasible. ?The per-mount-point mechanism will eventually give us *almost* the
> same, and so I believe it's good enough.
>

As another dimension of the same problem, it would be nice to
register interest in events generated by only a particular set of processes.
As a special case of this, can I register only for events generated
by myself and all my child processes. Is it already covered in some way?

There are situations where this could be very useful. One such case could be
somebody wanting to audit any random application. Collecting data for the whole
system and then filtering might be possible, but maynot be very efficient.
It might be good to add this while we are at it.

Let me know if I am missing something.

-Niraj

2009-12-03 05:53:34

by Eric Paris

[permalink] [raw]
Subject: [PATCH 3/5] fsnotify: Infrastructure for per-mount watches

From: Andreas Gruenbacher <[email protected]>

Per-mount watches allow groups to listen to fsnotify events on an entire
mount. This patch simply adds and initializes the fields needed in the
vfsmount struct to make this happen.

Signed-off-by: Andreas Gruenbacher <[email protected]>
Signed-off-by: Eric Paris <[email protected]>
---

fs/namespace.c | 1 +
fs/notify/fsnotify.c | 5 +++++
fs/notify/fsnotify.h | 2 ++
include/linux/fsnotify.h | 8 ++++++++
include/linux/fsnotify_backend.h | 4 ++++
5 files changed, 20 insertions(+), 0 deletions(-)

diff --git a/fs/namespace.c b/fs/namespace.c
index 2bff3b1..356d6cf 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -614,6 +614,7 @@ static inline void __mntput(struct vfsmount *mnt)
* provides barriers, so count_mnt_writers() below is safe. AV
*/
WARN_ON(count_mnt_writers(mnt));
+ fsnotify_vfsmount_delete(mnt);
dput(mnt->mnt_root);
free_vfsmnt(mnt);
deactivate_super(sb);
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index 8ed9fb5..990929b 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -35,6 +35,11 @@ void __fsnotify_inode_delete(struct inode *inode)
}
EXPORT_SYMBOL_GPL(__fsnotify_inode_delete);

+void __fsnotify_vfsmount_delete(struct vfsmount *mnt)
+{
+ fsnotify_clear_marks_by_mount(mnt);
+}
+
/*
* Given an inode, first check if we care what happens to our children. Inotify
* and dnotify both tell their parents about events. If we care about any event
diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h
index 38f3fb5..204353c 100644
--- a/fs/notify/fsnotify.h
+++ b/fs/notify/fsnotify.h
@@ -42,6 +42,8 @@ extern void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark);
extern void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark);
/* run the list of all marks associated with inode and flag them to be freed */
extern void fsnotify_clear_marks_by_inode(struct inode *inode);
+/* run the list of all marks associated with vfsmount and flag them to be freed */
+extern void fsnotify_clear_marks_by_mount(struct vfsmount *mnt);
/*
* update the dentry->d_flags of all of inode's children to indicate if inode cares
* about events that happen to its children.
diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h
index cc4dead..4d76d41 100644
--- a/include/linux/fsnotify.h
+++ b/include/linux/fsnotify.h
@@ -118,6 +118,14 @@ static inline void fsnotify_inode_delete(struct inode *inode)
}

/*
+ * fsnotify_vfsmount_delete - a vfsmount is being destroyed, clean up is needed
+ */
+static inline void fsnotify_vfsmount_delete(struct vfsmount *mnt)
+{
+ __fsnotify_vfsmount_delete(mnt);
+}
+
+/*
* fsnotify_nameremove - a filename was removed from a directory
*/
static inline void fsnotify_nameremove(struct dentry *dentry, int isdir)
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index f21ff1b..1af42cb 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -282,6 +282,7 @@ extern void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
const char *name, u32 cookie);
extern void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask);
extern void __fsnotify_inode_delete(struct inode *inode);
+extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt);
extern u32 fsnotify_get_cookie(void);

static inline int fsnotify_inode_watches_children(struct inode *inode)
@@ -402,6 +403,9 @@ static inline void __fsnotify_parent(struct path *path, struct dentry *dentry, _
static inline void __fsnotify_inode_delete(struct inode *inode)
{}

+static inline void __fsnotify_vfsmount_delete(struct vfsmount *mnt)
+{}
+
static inline void __fsnotify_update_dcache_flags(struct dentry *dentry)
{}