diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2011-02-02 13:31:45 +1100 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2011-02-02 13:31:45 +1100 |
commit | 0ddbdcc3bd7ec01bf61fc84ac43b7ecd6ea9339d (patch) | |
tree | 7b13f98bca45544ec3463f5d7b3f3e4c953f82d6 | |
parent | 6cb85d73dc927c9acb22d3c8f52089cf3b2ff9e7 (diff) | |
parent | ef9bf3b7144bee6ce1da5616015cabc8771206af (diff) |
Merge remote branch 'fsnotify/for-next'
-rw-r--r-- | fs/cachefiles/namei.c | 1 | ||||
-rw-r--r-- | fs/cachefiles/xattr.c | 1 | ||||
-rw-r--r-- | fs/nfsd/vfs.c | 3 | ||||
-rw-r--r-- | fs/notify/dnotify/dnotify.c | 15 | ||||
-rw-r--r-- | fs/notify/fanotify/fanotify.c | 24 | ||||
-rw-r--r-- | fs/notify/fanotify/fanotify_user.c | 101 | ||||
-rw-r--r-- | fs/notify/group.c | 1 | ||||
-rw-r--r-- | fs/notify/mark.c | 36 | ||||
-rw-r--r-- | include/linux/fanotify.h | 5 | ||||
-rw-r--r-- | include/linux/fsnotify_backend.h | 14 |
10 files changed, 121 insertions, 80 deletions
diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index a0358c2189cb..3f458310e287 100644 --- a/fs/cachefiles/namei.c +++ b/fs/cachefiles/namei.c @@ -13,7 +13,6 @@ #include <linux/sched.h> #include <linux/file.h> #include <linux/fs.h> -#include <linux/fsnotify.h> #include <linux/quotaops.h> #include <linux/xattr.h> #include <linux/mount.h> diff --git a/fs/cachefiles/xattr.c b/fs/cachefiles/xattr.c index e18b183b47e1..6e050686e9b3 100644 --- a/fs/cachefiles/xattr.c +++ b/fs/cachefiles/xattr.c @@ -13,7 +13,6 @@ #include <linux/sched.h> #include <linux/file.h> #include <linux/fs.h> -#include <linux/fsnotify.h> #include <linux/quotaops.h> #include <linux/xattr.h> #include <linux/slab.h> diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 641117f2188d..f38d5dfaccdf 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -19,7 +19,6 @@ #include <linux/fcntl.h> #include <linux/namei.h> #include <linux/delay.h> -#include <linux/fsnotify.h> #include <linux/posix_acl_xattr.h> #include <linux/xattr.h> #include <linux/jhash.h> @@ -906,7 +905,6 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, nfsdstats.io_read += host_err; *count = host_err; err = 0; - fsnotify_access(file); } else err = nfserrno(host_err); return err; @@ -1008,7 +1006,6 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, goto out_nfserr; *cnt = host_err; nfsdstats.io_write += host_err; - fsnotify_modify(file); /* clear setuid/setgid flag after write */ if (inode->i_mode & (S_ISUID | S_ISGID)) diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 3344bdd5506e..89ec7e0c15bc 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -31,7 +31,6 @@ int dir_notify_enable __read_mostly = 1; static struct kmem_cache *dnotify_struct_cache __read_mostly; static struct kmem_cache *dnotify_mark_cache __read_mostly; static struct fsnotify_group *dnotify_group __read_mostly; -static DEFINE_MUTEX(dnotify_mark_mutex); /* * dnotify will attach one of these to each inode (i_fsnotify_marks) which @@ -183,7 +182,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id) return; dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); - mutex_lock(&dnotify_mark_mutex); + mutex_lock(&dnotify_group->mutex); spin_lock(&fsn_mark->lock); prev = &dn_mark->dn; @@ -199,11 +198,11 @@ void dnotify_flush(struct file *filp, fl_owner_t id) spin_unlock(&fsn_mark->lock); - /* nothing else could have found us thanks to the dnotify_mark_mutex */ + /* nothing else could have found us thanks to the dnotify_group mutex */ if (dn_mark->dn == NULL) fsnotify_destroy_mark(fsn_mark); - mutex_unlock(&dnotify_mark_mutex); + mutex_unlock(&dnotify_group->mutex); fsnotify_put_mark(fsn_mark); } @@ -326,7 +325,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) new_dn_mark->dn = NULL; /* this is needed to prevent the fcntl/close race described below */ - mutex_lock(&dnotify_mark_mutex); + mutex_lock(&dnotify_group->mutex); /* add the new_fsn_mark or find an old one. */ fsn_mark = fsnotify_find_inode_mark(dnotify_group, inode); @@ -348,8 +347,8 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) /* if (f != filp) means that we lost a race and another task/thread * actually closed the fd we are still playing with before we grabbed - * the dnotify_mark_mutex and fsn_mark->lock. Since closing the fd is the - * only time we clean up the marks we need to get our mark off + * the dnotify_group mutex and fsn_mark->lock. Since closing the fd is + * the only time we clean up the marks we need to get our mark off * the list. */ if (f != filp) { /* if we added ourselves, shoot ourselves, it's possible that @@ -387,7 +386,7 @@ out: if (destroy) fsnotify_destroy_mark(fsn_mark); - mutex_unlock(&dnotify_mark_mutex); + mutex_unlock(&dnotify_group->mutex); fsnotify_put_mark(fsn_mark); out_err: if (new_fsn_mark) diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index f35794b97e8e..c2ba86a972e1 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -184,13 +184,6 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, marks_mask = (vfsmnt_mark->mask | inode_mark->mask); marks_ignored_mask = (vfsmnt_mark->ignored_mask | inode_mark->ignored_mask); } else if (inode_mark) { - /* - * if the event is for a child and this inode doesn't care about - * events on the child, don't send it! - */ - if ((event_mask & FS_EVENT_ON_CHILD) && - !(inode_mark->mask & FS_EVENT_ON_CHILD)) - return false; marks_mask = inode_mark->mask; marks_ignored_mask = inode_mark->ignored_mask; } else if (vfsmnt_mark) { @@ -204,10 +197,21 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, (marks_ignored_mask & FS_ISDIR)) return false; - if (event_mask & marks_mask & ~marks_ignored_mask) - return true; + /* + * if the event is for a child and this inode doesn't care about + * events on the child, don't send it! + */ + if ((event_mask & FS_EVENT_ON_CHILD) && + !(marks_mask & FS_EVENT_ON_CHILD)) + return false; - return false; + /* + * It might seem logical to check: + * if (event_mask & marks_mask & ~marks_ignored_mask) + * return true; + * but we we know this was true from the caller so just return true. + */ + return true; } static void fanotify_free_group_priv(struct fsnotify_group *group) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 8b61220cffc5..2d4925b98bdb 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -62,6 +62,7 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) struct dentry *dentry; struct vfsmount *mnt; struct file *new_file; + unsigned int flags; pr_debug("%s: group=%p event=%p\n", __func__, group, event); @@ -83,12 +84,22 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) mnt = mntget(event->path.mnt); /* it's possible this event was an overflow event. in that case dentry and mnt * are NULL; That's fine, just don't call dentry open */ - if (dentry && mnt) - new_file = dentry_open(dentry, mnt, - group->fanotify_data.f_flags | FMODE_NONOTIFY, - current_cred()); - else + if (dentry && mnt) { + flags = group->fanotify_data.f_flags; + new_file = dentry_open(dentry, mnt, flags, current_cred()); + /* + * Attempt fallback to read-only access if writable was not possible + * in order to at least provide something to the listener. + */ + if (IS_ERR(new_file) && group->fanotify_data.readonly_fallback) { + flags &= ~O_ACCMODE; + flags |= O_RDONLY; + new_file = dentry_open(dentry, mnt, flags, + current_cred()); + } + } else { new_file = ERR_PTR(-EOVERFLOW); + } if (IS_ERR(new_file)) { /* * we still send an event even if we can't open the file. this @@ -208,14 +219,6 @@ static int prepare_for_access_response(struct fsnotify_group *group, re->fd = fd; mutex_lock(&group->fanotify_data.access_mutex); - - if (atomic_read(&group->fanotify_data.bypass_perm)) { - mutex_unlock(&group->fanotify_data.access_mutex); - kmem_cache_free(fanotify_response_event_cache, re); - event->response = FAN_ALLOW; - return 0; - } - list_add_tail(&re->list, &group->fanotify_data.access_list); mutex_unlock(&group->fanotify_data.access_mutex); @@ -516,6 +519,7 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, unsigned int flags) { __u32 oldmask; + int destroy_mark; spin_lock(&fsn_mark->lock); if (!(flags & FAN_MARK_IGNORED_MASK)) { @@ -525,9 +529,10 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, oldmask = fsn_mark->ignored_mask; fsnotify_set_mark_ignored_mask_locked(fsn_mark, (oldmask & ~mask)); } + destroy_mark = (!fsn_mark->mask && !fsn_mark->ignored_mask); spin_unlock(&fsn_mark->lock); - if (!(oldmask & ~mask)) + if (destroy_mark) fsnotify_destroy_mark(fsn_mark); return mask & oldmask; @@ -539,17 +544,23 @@ static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group, { struct fsnotify_mark *fsn_mark = NULL; __u32 removed; + int ret; + mutex_lock(&group->mutex); + ret = -ENOENT; fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); if (!fsn_mark) - return -ENOENT; + goto err; removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags); fsnotify_put_mark(fsn_mark); if (removed & mnt->mnt_fsnotify_mask) fsnotify_recalc_vfsmount_mask(mnt); + ret = 0; +err: + mutex_unlock(&group->mutex); - return 0; + return ret; } static int fanotify_remove_inode_mark(struct fsnotify_group *group, @@ -558,18 +569,24 @@ static int fanotify_remove_inode_mark(struct fsnotify_group *group, { struct fsnotify_mark *fsn_mark = NULL; __u32 removed; + int ret; + mutex_lock(&group->mutex); + ret = -ENOENT; fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) - return -ENOENT; + goto err; removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags); /* matches the fsnotify_find_inode_mark() */ fsnotify_put_mark(fsn_mark); if (removed & inode->i_fsnotify_mask) fsnotify_recalc_inode_mask(inode); + ret = 0; +err: + mutex_unlock(&group->mutex); - return 0; + return ret; } static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, @@ -605,28 +622,35 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, { struct fsnotify_mark *fsn_mark; __u32 added; - int ret = 0; + int ret; + mutex_lock(&group->mutex); fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); if (!fsn_mark) { - if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks) - return -ENOSPC; + ret = -ENOSPC; + if (atomic_read(&group->num_marks) > + group->fanotify_data.max_marks) + goto err; + ret = -ENOMEM; fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); if (!fsn_mark) - return -ENOMEM; + goto err; fsnotify_init_mark(fsn_mark, fanotify_free_mark); ret = fsnotify_add_mark(fsn_mark, group, NULL, mnt, 0); if (ret) - goto err; + goto err2; } added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); if (added & ~mnt->mnt_fsnotify_mask) fsnotify_recalc_vfsmount_mask(mnt); -err: + ret = 0; +err2: fsnotify_put_mark(fsn_mark); +err: + mutex_unlock(&group->mutex); return ret; } @@ -636,7 +660,7 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, { struct fsnotify_mark *fsn_mark; __u32 added; - int ret = 0; + int ret; pr_debug("%s: group=%p inode=%p\n", __func__, group, inode); @@ -650,26 +674,33 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, (atomic_read(&inode->i_writecount) > 0)) return 0; + mutex_lock(&group->mutex); fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) { - if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks) - return -ENOSPC; + ret = -ENOSPC; + if (atomic_read(&group->num_marks) > + group->fanotify_data.max_marks) + goto err; + ret = -ENOMEM; fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); if (!fsn_mark) - return -ENOMEM; + goto err; fsnotify_init_mark(fsn_mark, fanotify_free_mark); ret = fsnotify_add_mark(fsn_mark, group, inode, NULL, 0); if (ret) - goto err; + goto err2; } added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); if (added & ~inode->i_fsnotify_mask) fsnotify_recalc_inode_mask(inode); -err: + ret = 0; +err2: fsnotify_put_mark(fsn_mark); +err: + mutex_unlock(&group->mutex); return ret; } @@ -711,7 +742,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) group->fanotify_data.user = user; atomic_inc(&user->fanotify_listeners); - group->fanotify_data.f_flags = event_f_flags; + group->fanotify_data.f_flags = event_f_flags | FMODE_NONOTIFY; #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS mutex_init(&group->fanotify_data.access_mutex); init_waitqueue_head(&group->fanotify_data.access_waitq); @@ -751,6 +782,14 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) group->fanotify_data.max_marks = FANOTIFY_DEFAULT_MAX_MARKS; } + fd = -EINVAL; + if (flags & FAN_READONLY_FALLBACK) { + if ((event_f_flags & O_ACCMODE) == O_RDWR) + group->fanotify_data.readonly_fallback = true; + else + goto out_put_group; + } + fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags); if (fd < 0) goto out_put_group; diff --git a/fs/notify/group.c b/fs/notify/group.c index d309f38449cb..cc341d33f5c8 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -90,6 +90,7 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) */ atomic_set(&group->num_marks, 1); + mutex_init(&group->mutex); mutex_init(&group->notification_mutex); INIT_LIST_HEAD(&group->notification_list); init_waitqueue_head(&group->notification_waitq); diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 325185e514bb..28b64eb03e33 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -103,17 +103,6 @@ static DEFINE_SPINLOCK(destroy_lock); static LIST_HEAD(destroy_list); static DECLARE_WAIT_QUEUE_HEAD(destroy_waitq); -void fsnotify_get_mark(struct fsnotify_mark *mark) -{ - atomic_inc(&mark->refcnt); -} - -void fsnotify_put_mark(struct fsnotify_mark *mark) -{ - if (atomic_dec_and_test(&mark->refcnt)) - mark->free_mark(mark); -} - /* * Any time a mark is getting freed we end up here. * The caller had better be holding a reference to this mark so we don't actually @@ -217,7 +206,7 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, int allow_dups) { - int ret = 0; + int ret; BUG_ON(inode && mnt); BUG_ON(!inode && !mnt); @@ -232,23 +221,20 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, spin_lock(&group->mark_lock); mark->flags |= FSNOTIFY_MARK_FLAG_ALIVE; - mark->group = group; list_add(&mark->g_list, &group->marks_list); - atomic_inc(&group->num_marks); fsnotify_get_mark(mark); /* for i_list and g_list */ + atomic_inc(&group->num_marks); - if (inode) { + ret = 0; + if (inode) ret = fsnotify_add_inode_mark(mark, group, inode, allow_dups); - if (ret) - goto err; - } else if (mnt) { + else if (mnt) ret = fsnotify_add_vfsmount_mark(mark, group, mnt, allow_dups); - if (ret) - goto err; - } else { + else BUG(); - } + if (ret) + goto err; spin_unlock(&group->mark_lock); @@ -260,7 +246,7 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, if (inode) __fsnotify_update_child_dentry_flags(inode); - return ret; + return 0; err: mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; list_del_init(&mark->g_list); @@ -346,6 +332,10 @@ static int fsnotify_mark_destroy(void *ignored) synchronize_srcu(&fsnotify_mark_srcu); + /* + * at this point we cannot be found via the i_list or g_list so + * drop that reference. + */ list_for_each_entry_safe(mark, next, &private_destroy_list, destroy_list) { list_del_init(&mark->destroy_list); fsnotify_put_mark(mark); diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h index 6c6133f76e16..b5fac2ba4a07 100644 --- a/include/linux/fanotify.h +++ b/include/linux/fanotify.h @@ -36,9 +36,12 @@ #define FAN_UNLIMITED_QUEUE 0x00000010 #define FAN_UNLIMITED_MARKS 0x00000020 +/* Attempt read-only open if read-write failed. */ +#define FAN_READONLY_FALLBACK 0x00000040 + #define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK | \ FAN_ALL_CLASS_BITS | FAN_UNLIMITED_QUEUE |\ - FAN_UNLIMITED_MARKS) + FAN_UNLIMITED_MARKS | FAN_READONLY_FALLBACK) /* flags used for fanotify_modify_mark() */ #define FAN_MARK_ADD 0x00000001 diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 69ad89b50489..8aa4731c9f6f 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -125,6 +125,7 @@ struct fsnotify_group { const struct fsnotify_ops *ops; /* how this group handles things */ + struct mutex mutex; /* needed to send notification to userspace */ struct mutex notification_mutex; /* protect the notification_list */ struct list_head notification_list; /* list of event_holder this group needs to send to userspace */ @@ -168,6 +169,7 @@ struct fsnotify_group { wait_queue_head_t access_waitq; atomic_t bypass_perm; #endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */ + bool readonly_fallback; int f_flags; unsigned int max_marks; struct user_struct *user; @@ -415,8 +417,6 @@ extern void fsnotify_clear_inode_marks_by_group(struct fsnotify_group *group); extern void fsnotify_clear_marks_by_group_flags(struct fsnotify_group *group, unsigned int flags); /* run all the marks in a group, and flag them to be freed */ extern void fsnotify_clear_marks_by_group(struct fsnotify_group *group); -extern void fsnotify_get_mark(struct fsnotify_mark *mark); -extern void fsnotify_put_mark(struct fsnotify_mark *mark); extern void fsnotify_unmount_inodes(struct list_head *list); /* put here because inotify does some weird stuff when destroying watches */ @@ -430,6 +430,16 @@ extern struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_ev extern int fsnotify_replace_event(struct fsnotify_event_holder *old_holder, struct fsnotify_event *new_event); +static inline void fsnotify_get_mark(struct fsnotify_mark *mark) +{ + atomic_inc(&mark->refcnt); +} + +static inline void fsnotify_put_mark(struct fsnotify_mark *mark) +{ + if (atomic_dec_and_test(&mark->refcnt)) + mark->free_mark(mark); +} #else static inline int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, |