summaryrefslogtreecommitdiffstats
path: root/fs/notify
diff options
context:
space:
mode:
Diffstat (limited to 'fs/notify')
-rw-r--r--fs/notify/fanotify/fanotify_user.c99
-rw-r--r--fs/notify/fdinfo.c8
-rw-r--r--fs/notify/fsnotify.h16
-rw-r--r--fs/notify/inotify/inotify_user.c9
-rw-r--r--fs/notify/mark.c103
5 files changed, 118 insertions, 117 deletions
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 0cf45041dc32..69054886915b 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -525,17 +525,16 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark,
return mask & oldmask;
}
-static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group,
- struct vfsmount *mnt, __u32 mask,
- unsigned int flags)
+static int fanotify_remove_mark(struct fsnotify_group *group,
+ fsnotify_connp_t *connp, __u32 mask,
+ unsigned int flags)
{
struct fsnotify_mark *fsn_mark = NULL;
__u32 removed;
int destroy_mark;
mutex_lock(&group->mark_mutex);
- fsn_mark = fsnotify_find_mark(&real_mount(mnt)->mnt_fsnotify_marks,
- group);
+ fsn_mark = fsnotify_find_mark(connp, group);
if (!fsn_mark) {
mutex_unlock(&group->mark_mutex);
return -ENOENT;
@@ -543,47 +542,33 @@ static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group,
removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags,
&destroy_mark);
- if (removed & real_mount(mnt)->mnt_fsnotify_mask)
- fsnotify_recalc_mask(real_mount(mnt)->mnt_fsnotify_marks);
+ if (removed & fsnotify_conn_mask(fsn_mark->connector))
+ fsnotify_recalc_mask(fsn_mark->connector);
if (destroy_mark)
fsnotify_detach_mark(fsn_mark);
mutex_unlock(&group->mark_mutex);
if (destroy_mark)
fsnotify_free_mark(fsn_mark);
+ /* matches the fsnotify_find_mark() */
fsnotify_put_mark(fsn_mark);
return 0;
}
+static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group,
+ struct vfsmount *mnt, __u32 mask,
+ unsigned int flags)
+{
+ return fanotify_remove_mark(group, &real_mount(mnt)->mnt_fsnotify_marks,
+ mask, flags);
+}
+
static int fanotify_remove_inode_mark(struct fsnotify_group *group,
struct inode *inode, __u32 mask,
unsigned int flags)
{
- struct fsnotify_mark *fsn_mark = NULL;
- __u32 removed;
- int destroy_mark;
-
- mutex_lock(&group->mark_mutex);
- fsn_mark = fsnotify_find_mark(&inode->i_fsnotify_marks, group);
- if (!fsn_mark) {
- mutex_unlock(&group->mark_mutex);
- return -ENOENT;
- }
-
- removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags,
- &destroy_mark);
- if (removed & inode->i_fsnotify_mask)
- fsnotify_recalc_mask(inode->i_fsnotify_marks);
- if (destroy_mark)
- fsnotify_detach_mark(fsn_mark);
- mutex_unlock(&group->mark_mutex);
- if (destroy_mark)
- fsnotify_free_mark(fsn_mark);
-
- /* matches the fsnotify_find_mark() */
- fsnotify_put_mark(fsn_mark);
-
- return 0;
+ return fanotify_remove_mark(group, &inode->i_fsnotify_marks, mask,
+ flags);
}
static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
@@ -616,8 +601,8 @@ static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
}
static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
- struct inode *inode,
- struct vfsmount *mnt)
+ fsnotify_connp_t *connp,
+ unsigned int type)
{
struct fsnotify_mark *mark;
int ret;
@@ -630,7 +615,7 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
return ERR_PTR(-ENOMEM);
fsnotify_init_mark(mark, group);
- ret = fsnotify_add_mark_locked(mark, inode, mnt, 0);
+ ret = fsnotify_add_mark_locked(mark, connp, type, 0);
if (ret) {
fsnotify_put_mark(mark);
return ERR_PTR(ret);
@@ -640,39 +625,43 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
}
-static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
- struct vfsmount *mnt, __u32 mask,
- unsigned int flags)
+static int fanotify_add_mark(struct fsnotify_group *group,
+ fsnotify_connp_t *connp, unsigned int type,
+ __u32 mask, unsigned int flags)
{
struct fsnotify_mark *fsn_mark;
__u32 added;
mutex_lock(&group->mark_mutex);
- fsn_mark = fsnotify_find_mark(&real_mount(mnt)->mnt_fsnotify_marks,
- group);
+ fsn_mark = fsnotify_find_mark(connp, group);
if (!fsn_mark) {
- fsn_mark = fanotify_add_new_mark(group, NULL, mnt);
+ fsn_mark = fanotify_add_new_mark(group, connp, type);
if (IS_ERR(fsn_mark)) {
mutex_unlock(&group->mark_mutex);
return PTR_ERR(fsn_mark);
}
}
added = fanotify_mark_add_to_mask(fsn_mark, mask, flags);
- if (added & ~real_mount(mnt)->mnt_fsnotify_mask)
- fsnotify_recalc_mask(real_mount(mnt)->mnt_fsnotify_marks);
+ if (added & ~fsnotify_conn_mask(fsn_mark->connector))
+ fsnotify_recalc_mask(fsn_mark->connector);
mutex_unlock(&group->mark_mutex);
fsnotify_put_mark(fsn_mark);
return 0;
}
+static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
+ struct vfsmount *mnt, __u32 mask,
+ unsigned int flags)
+{
+ return fanotify_add_mark(group, &real_mount(mnt)->mnt_fsnotify_marks,
+ FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags);
+}
+
static int fanotify_add_inode_mark(struct fsnotify_group *group,
struct inode *inode, __u32 mask,
unsigned int flags)
{
- struct fsnotify_mark *fsn_mark;
- __u32 added;
-
pr_debug("%s: group=%p inode=%p\n", __func__, group, inode);
/*
@@ -685,22 +674,8 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group,
(atomic_read(&inode->i_writecount) > 0))
return 0;
- mutex_lock(&group->mark_mutex);
- fsn_mark = fsnotify_find_mark(&inode->i_fsnotify_marks, group);
- if (!fsn_mark) {
- fsn_mark = fanotify_add_new_mark(group, inode, NULL);
- if (IS_ERR(fsn_mark)) {
- mutex_unlock(&group->mark_mutex);
- return PTR_ERR(fsn_mark);
- }
- }
- added = fanotify_mark_add_to_mask(fsn_mark, mask, flags);
- if (added & ~inode->i_fsnotify_mask)
- fsnotify_recalc_mask(inode->i_fsnotify_marks);
- mutex_unlock(&group->mark_mutex);
-
- fsnotify_put_mark(fsn_mark);
- return 0;
+ return fanotify_add_mark(group, &inode->i_fsnotify_marks,
+ FSNOTIFY_OBJ_TYPE_INODE, mask, flags);
}
/* fanotify syscalls */
diff --git a/fs/notify/fdinfo.c b/fs/notify/fdinfo.c
index 10aac1942c9f..86fcf5814279 100644
--- a/fs/notify/fdinfo.c
+++ b/fs/notify/fdinfo.c
@@ -15,7 +15,7 @@
#include <linux/exportfs.h>
#include "inotify/inotify.h"
-#include "../fs/mount.h"
+#include "fsnotify.h"
#if defined(CONFIG_PROC_FS)
@@ -81,7 +81,7 @@ static void inotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
return;
inode_mark = container_of(mark, struct inotify_inode_mark, fsn_mark);
- inode = igrab(mark->connector->inode);
+ inode = igrab(fsnotify_conn_inode(mark->connector));
if (inode) {
/*
* IN_ALL_EVENTS represents all of the mask bits
@@ -117,7 +117,7 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
mflags |= FAN_MARK_IGNORED_SURV_MODIFY;
if (mark->connector->type == FSNOTIFY_OBJ_TYPE_INODE) {
- inode = igrab(mark->connector->inode);
+ inode = igrab(fsnotify_conn_inode(mark->connector));
if (!inode)
return;
seq_printf(m, "fanotify ino:%lx sdev:%x mflags:%x mask:%x ignored_mask:%x ",
@@ -127,7 +127,7 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
seq_putc(m, '\n');
iput(inode);
} else if (mark->connector->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) {
- struct mount *mnt = real_mount(mark->connector->mnt);
+ struct mount *mnt = fsnotify_conn_mount(mark->connector);
seq_printf(m, "fanotify mnt_id:%x mflags:%x mask:%x ignored_mask:%x\n",
mnt->mnt_id, mflags, mark->mask, mark->ignored_mask);
diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h
index 34515d2c4ba3..7902653dd577 100644
--- a/fs/notify/fsnotify.h
+++ b/fs/notify/fsnotify.h
@@ -9,6 +9,18 @@
#include "../mount.h"
+static inline struct inode *fsnotify_conn_inode(
+ struct fsnotify_mark_connector *conn)
+{
+ return container_of(conn->obj, struct inode, i_fsnotify_marks);
+}
+
+static inline struct mount *fsnotify_conn_mount(
+ struct fsnotify_mark_connector *conn)
+{
+ return container_of(conn->obj, struct mount, mnt_fsnotify_marks);
+}
+
/* destroy all events sitting in this groups notification queue */
extern void fsnotify_flush_notify(struct fsnotify_group *group);
@@ -19,8 +31,8 @@ extern struct srcu_struct fsnotify_mark_srcu;
extern int fsnotify_compare_groups(struct fsnotify_group *a,
struct fsnotify_group *b);
-/* Destroy all marks connected via given connector */
-extern void fsnotify_destroy_marks(struct fsnotify_mark_connector __rcu **connp);
+/* Destroy all marks attached to an object via connector */
+extern void fsnotify_destroy_marks(fsnotify_connp_t *connp);
/* run the list of all marks associated with inode and destroy them */
static inline void fsnotify_clear_marks_by_inode(struct inode *inode)
{
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
index 749c46ababa0..ac6978d3208c 100644
--- a/fs/notify/inotify/inotify_user.c
+++ b/fs/notify/inotify/inotify_user.c
@@ -511,6 +511,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group,
__u32 old_mask, new_mask;
__u32 mask;
int add = (arg & IN_MASK_ADD);
+ int create = (arg & IN_MASK_CREATE);
int ret;
mask = inotify_arg_to_mask(arg);
@@ -518,6 +519,8 @@ static int inotify_update_existing_watch(struct fsnotify_group *group,
fsn_mark = fsnotify_find_mark(&inode->i_fsnotify_marks, group);
if (!fsn_mark)
return -ENOENT;
+ else if (create)
+ return -EEXIST;
i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
@@ -720,6 +723,10 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname,
if (unlikely(!f.file))
return -EBADF;
+ /* IN_MASK_ADD and IN_MASK_CREATE don't make sense together */
+ if (unlikely((mask & IN_MASK_ADD) && (mask & IN_MASK_CREATE)))
+ return -EINVAL;
+
/* verify that this is indeed an inotify instance */
if (unlikely(f.file->f_op != &inotify_fops)) {
ret = -EINVAL;
@@ -808,7 +815,7 @@ static int __init inotify_user_setup(void)
BUILD_BUG_ON(IN_ISDIR != FS_ISDIR);
BUILD_BUG_ON(IN_ONESHOT != FS_IN_ONESHOT);
- BUG_ON(hweight32(ALL_INOTIFY_BITS) != 21);
+ BUG_ON(hweight32(ALL_INOTIFY_BITS) != 22);
inotify_inode_mark_cachep = KMEM_CACHE(inotify_inode_mark,
SLAB_PANIC|SLAB_ACCOUNT);
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index 61f4c5fa34c7..05506d60131c 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -109,6 +109,23 @@ void fsnotify_get_mark(struct fsnotify_mark *mark)
refcount_inc(&mark->refcnt);
}
+static __u32 *fsnotify_conn_mask_p(struct fsnotify_mark_connector *conn)
+{
+ if (conn->type == FSNOTIFY_OBJ_TYPE_INODE)
+ return &fsnotify_conn_inode(conn)->i_fsnotify_mask;
+ else if (conn->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT)
+ return &fsnotify_conn_mount(conn)->mnt_fsnotify_mask;
+ return NULL;
+}
+
+__u32 fsnotify_conn_mask(struct fsnotify_mark_connector *conn)
+{
+ if (WARN_ON(!fsnotify_valid_obj_type(conn->type)))
+ return 0;
+
+ return *fsnotify_conn_mask_p(conn);
+}
+
static void __fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
{
u32 new_mask = 0;
@@ -119,15 +136,15 @@ static void __fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
if (mark->flags & FSNOTIFY_MARK_FLAG_ATTACHED)
new_mask |= mark->mask;
}
- if (conn->type == FSNOTIFY_OBJ_TYPE_INODE)
- conn->inode->i_fsnotify_mask = new_mask;
- else if (conn->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT)
- real_mount(conn->mnt)->mnt_fsnotify_mask = new_mask;
+ if (WARN_ON(!fsnotify_valid_obj_type(conn->type)))
+ return;
+
+ *fsnotify_conn_mask_p(conn) = new_mask;
}
/*
* Calculate mask of events for a list of marks. The caller must make sure
- * connector and connector->inode cannot disappear under us. Callers achieve
+ * connector and connector->obj cannot disappear under us. Callers achieve
* this by holding a mark->lock or mark->group->mark_mutex for a mark on this
* list.
*/
@@ -140,7 +157,8 @@ void fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
__fsnotify_recalc_mask(conn);
spin_unlock(&conn->lock);
if (conn->type == FSNOTIFY_OBJ_TYPE_INODE)
- __fsnotify_update_child_dentry_flags(conn->inode);
+ __fsnotify_update_child_dentry_flags(
+ fsnotify_conn_inode(conn));
}
/* Free all connectors queued for freeing once SRCU period ends */
@@ -166,20 +184,20 @@ static struct inode *fsnotify_detach_connector_from_object(
{
struct inode *inode = NULL;
+ if (conn->type == FSNOTIFY_OBJ_TYPE_DETACHED)
+ return NULL;
+
if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) {
- inode = conn->inode;
- rcu_assign_pointer(inode->i_fsnotify_marks, NULL);
+ inode = fsnotify_conn_inode(conn);
inode->i_fsnotify_mask = 0;
- conn->inode = NULL;
- conn->type = FSNOTIFY_OBJ_TYPE_DETACHED;
} else if (conn->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) {
- rcu_assign_pointer(real_mount(conn->mnt)->mnt_fsnotify_marks,
- NULL);
- real_mount(conn->mnt)->mnt_fsnotify_mask = 0;
- conn->mnt = NULL;
- conn->type = FSNOTIFY_OBJ_TYPE_DETACHED;
+ fsnotify_conn_mount(conn)->mnt_fsnotify_mask = 0;
}
+ rcu_assign_pointer(*(conn->obj), NULL);
+ conn->obj = NULL;
+ conn->type = FSNOTIFY_OBJ_TYPE_DETACHED;
+
return inode;
}
@@ -436,11 +454,10 @@ int fsnotify_compare_groups(struct fsnotify_group *a, struct fsnotify_group *b)
return -1;
}
-static int fsnotify_attach_connector_to_object(
- struct fsnotify_mark_connector __rcu **connp,
- struct inode *inode,
- struct vfsmount *mnt)
+static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
+ unsigned int type)
{
+ struct inode *inode = NULL;
struct fsnotify_mark_connector *conn;
conn = kmem_cache_alloc(fsnotify_mark_connector_cachep, GFP_KERNEL);
@@ -448,13 +465,10 @@ static int fsnotify_attach_connector_to_object(
return -ENOMEM;
spin_lock_init(&conn->lock);
INIT_HLIST_HEAD(&conn->list);
- if (inode) {
- conn->type = FSNOTIFY_OBJ_TYPE_INODE;
- conn->inode = igrab(inode);
- } else {
- conn->type = FSNOTIFY_OBJ_TYPE_VFSMOUNT;
- conn->mnt = mnt;
- }
+ conn->type = type;
+ conn->obj = connp;
+ if (conn->type == FSNOTIFY_OBJ_TYPE_INODE)
+ inode = igrab(fsnotify_conn_inode(conn));
/*
* cmpxchg() provides the barrier so that readers of *connp can see
* only initialized structure
@@ -476,7 +490,7 @@ static int fsnotify_attach_connector_to_object(
* they are sure list cannot go away under them.
*/
static struct fsnotify_mark_connector *fsnotify_grab_connector(
- struct fsnotify_mark_connector __rcu **connp)
+ fsnotify_connp_t *connp)
{
struct fsnotify_mark_connector *conn;
int idx;
@@ -503,27 +517,22 @@ out:
* priority, highest number first, and then by the group's location in memory.
*/
static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
- struct inode *inode, struct vfsmount *mnt,
+ fsnotify_connp_t *connp, unsigned int type,
int allow_dups)
{
struct fsnotify_mark *lmark, *last = NULL;
struct fsnotify_mark_connector *conn;
- struct fsnotify_mark_connector __rcu **connp;
int cmp;
int err = 0;
- if (WARN_ON(!inode && !mnt))
+ if (WARN_ON(!fsnotify_valid_obj_type(type)))
return -EINVAL;
- if (inode)
- connp = &inode->i_fsnotify_marks;
- else
- connp = &real_mount(mnt)->mnt_fsnotify_marks;
restart:
spin_lock(&mark->lock);
conn = fsnotify_grab_connector(connp);
if (!conn) {
spin_unlock(&mark->lock);
- err = fsnotify_attach_connector_to_object(connp, inode, mnt);
+ err = fsnotify_attach_connector_to_object(connp, type);
if (err)
return err;
goto restart;
@@ -569,14 +578,13 @@ out_err:
* These marks may be used for the fsnotify backend to determine which
* event types should be delivered to which group.
*/
-int fsnotify_add_mark_locked(struct fsnotify_mark *mark, struct inode *inode,
- struct vfsmount *mnt, int allow_dups)
+int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
+ fsnotify_connp_t *connp, unsigned int type,
+ int allow_dups)
{
struct fsnotify_group *group = mark->group;
int ret = 0;
- BUG_ON(inode && mnt);
- BUG_ON(!inode && !mnt);
BUG_ON(!mutex_is_locked(&group->mark_mutex));
/*
@@ -593,7 +601,7 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark, struct inode *inode,
fsnotify_get_mark(mark); /* for g_list */
spin_unlock(&mark->lock);
- ret = fsnotify_add_mark_list(mark, inode, mnt, allow_dups);
+ ret = fsnotify_add_mark_list(mark, connp, type, allow_dups);
if (ret)
goto err;
@@ -613,14 +621,14 @@ err:
return ret;
}
-int fsnotify_add_mark(struct fsnotify_mark *mark, struct inode *inode,
- struct vfsmount *mnt, int allow_dups)
+int fsnotify_add_mark(struct fsnotify_mark *mark, fsnotify_connp_t *connp,
+ unsigned int type, int allow_dups)
{
int ret;
struct fsnotify_group *group = mark->group;
mutex_lock(&group->mark_mutex);
- ret = fsnotify_add_mark_locked(mark, inode, mnt, allow_dups);
+ ret = fsnotify_add_mark_locked(mark, connp, type, allow_dups);
mutex_unlock(&group->mark_mutex);
return ret;
}
@@ -629,9 +637,8 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, struct inode *inode,
* Given a list of marks, find the mark associated with given group. If found
* take a reference to that mark and return it, else return NULL.
*/
-struct fsnotify_mark *fsnotify_find_mark(
- struct fsnotify_mark_connector __rcu **connp,
- struct fsnotify_group *group)
+struct fsnotify_mark *fsnotify_find_mark(fsnotify_connp_t *connp,
+ struct fsnotify_group *group)
{
struct fsnotify_mark_connector *conn;
struct fsnotify_mark *mark;
@@ -697,8 +704,8 @@ clear:
}
}
-/* Destroy all marks attached to inode / vfsmount */
-void fsnotify_destroy_marks(struct fsnotify_mark_connector __rcu **connp)
+/* Destroy all marks attached to an object via connector */
+void fsnotify_destroy_marks(fsnotify_connp_t *connp)
{
struct fsnotify_mark_connector *conn;
struct fsnotify_mark *mark, *old_mark = NULL;