diff options
Diffstat (limited to 'fs/notify')
-rw-r--r-- | fs/notify/fanotify/fanotify_user.c | 99 | ||||
-rw-r--r-- | fs/notify/fdinfo.c | 8 | ||||
-rw-r--r-- | fs/notify/fsnotify.h | 16 | ||||
-rw-r--r-- | fs/notify/inotify/inotify_user.c | 9 | ||||
-rw-r--r-- | fs/notify/mark.c | 103 |
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; |