diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2010-11-30 11:31:06 +1100 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2010-11-30 11:31:06 +1100 |
commit | dbe1a6281f7c3c460b60253b75ab9447f478fb1f (patch) | |
tree | 52aed9c71cc3603d13a86d9dcadf90edc266bd0d /fs | |
parent | c82813e295ee1c7b02b7645440238af40dc4ea7d (diff) | |
parent | 9f71741c75e5206e1fea72ddf2adf5661105444d (diff) |
Merge remote branch 'fsnotify/for-next'
Diffstat (limited to 'fs')
-rw-r--r-- | fs/cachefiles/namei.c | 1 | ||||
-rw-r--r-- | fs/cachefiles/xattr.c | 1 | ||||
-rw-r--r-- | fs/namei.c | 3 | ||||
-rw-r--r-- | fs/nfsd/vfs.c | 3 | ||||
-rw-r--r-- | fs/notify/fanotify/fanotify.c | 30 | ||||
-rw-r--r-- | fs/notify/fanotify/fanotify_user.c | 112 | ||||
-rw-r--r-- | fs/notify/inotify/inotify_user.c | 1 | ||||
-rw-r--r-- | fs/notify/mark.c | 36 |
8 files changed, 107 insertions, 80 deletions
diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c index 42c7fafc8bfe..e33b9a00b3bb 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/namei.c b/fs/namei.c index 5362af9b7372..4ff7ca530533 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1748,6 +1748,9 @@ struct file *do_filp_open(int dfd, const char *pathname, if (!(open_flag & O_CREAT)) mode = 0; + /* Must never be set by userspace */ + open_flag &= ~FMODE_NONOTIFY; + /* * O_SYNC is implemented as __O_SYNC|O_DSYNC. As many places only * check for O_DSYNC if the need any syncing at all we enforce it's diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 184938fcff04..5f30270ae644 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -20,7 +20,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> @@ -924,7 +923,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); out: @@ -1035,7 +1033,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/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index b04f88eed09e..c2ba86a972e1 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -92,7 +92,11 @@ static int fanotify_get_response_from_access(struct fsnotify_group *group, pr_debug("%s: group=%p event=%p\n", __func__, group, event); - wait_event(group->fanotify_data.access_waitq, event->response); + wait_event(group->fanotify_data.access_waitq, event->response || + atomic_read(&group->fanotify_data.bypass_perm)); + + if (!event->response) /* bypass_perm set */ + return 0; /* userspace responded, convert to something usable */ spin_lock(&event->lock); @@ -180,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) { @@ -200,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 063224812b7e..5d2e68370950 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 @@ -106,10 +117,12 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) return client_fd; } -static ssize_t fill_event_metadata(struct fsnotify_group *group, +static int fill_event_metadata(struct fsnotify_group *group, struct fanotify_event_metadata *metadata, struct fsnotify_event *event) { + int ret = 0; + pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, group, metadata, event); @@ -117,9 +130,15 @@ static ssize_t fill_event_metadata(struct fsnotify_group *group, metadata->vers = FANOTIFY_METADATA_VERSION; metadata->mask = event->mask & FAN_ALL_OUTGOING_EVENTS; metadata->pid = pid_vnr(event->tgid); - metadata->fd = create_fd(group, event); + if (unlikely(event->mask & FAN_Q_OVERFLOW)) + metadata->fd = FAN_NOFD; + else { + metadata->fd = create_fd(group, event); + if (metadata->fd < 0) + ret = metadata->fd; + } - return metadata->fd; + return ret; } #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS @@ -199,14 +218,6 @@ static int prepare_for_access_response(struct fsnotify_group *group, re->fd = fd; mutex_lock(&group->fanotify_data.access_mutex); - - if (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); @@ -257,10 +268,11 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, pr_debug("%s: group=%p event=%p\n", __func__, group, event); - fd = fill_event_metadata(group, &fanotify_event_metadata, event); - if (fd < 0) - return fd; + ret = fill_event_metadata(group, &fanotify_event_metadata, event); + if (ret < 0) + goto out; + fd = fanotify_event_metadata.fd; ret = prepare_for_access_response(group, event, fd); if (ret) goto out_close_fd; @@ -274,7 +286,15 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, out_kill_access_response: remove_access_response(group, event, fd); out_close_fd: - sys_close(fd); + if (fd != FAN_NOFD) + sys_close(fd); +out: +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + if (event->mask & FAN_ALL_PERM_EVENTS) { + event->response = FAN_DENY; + wake_up(&group->fanotify_data.access_waitq); + } +#endif return ret; } @@ -382,7 +402,7 @@ static int fanotify_release(struct inode *ignored, struct file *file) mutex_lock(&group->fanotify_data.access_mutex); - group->fanotify_data.bypass_perm = true; + atomic_inc(&group->fanotify_data.bypass_perm); list_for_each_entry_safe(re, lre, &group->fanotify_data.access_list, list) { pr_debug("%s: found group=%p re=%p event=%p\n", __func__, group, @@ -586,11 +606,10 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, { struct fsnotify_mark *fsn_mark; __u32 added; + int ret = 0; fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); if (!fsn_mark) { - int ret; - if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks) return -ENOSPC; @@ -600,17 +619,16 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, fsnotify_init_mark(fsn_mark, fanotify_free_mark); ret = fsnotify_add_mark(fsn_mark, group, NULL, mnt, 0); - if (ret) { - fanotify_free_mark(fsn_mark); - return ret; - } + if (ret) + goto err; } added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); - fsnotify_put_mark(fsn_mark); + if (added & ~mnt->mnt_fsnotify_mask) fsnotify_recalc_vfsmount_mask(mnt); - - return 0; +err: + fsnotify_put_mark(fsn_mark); + return ret; } static int fanotify_add_inode_mark(struct fsnotify_group *group, @@ -619,6 +637,7 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, { struct fsnotify_mark *fsn_mark; __u32 added; + int ret = 0; pr_debug("%s: group=%p inode=%p\n", __func__, group, inode); @@ -634,8 +653,6 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) { - int ret; - if (atomic_read(&group->num_marks) > group->fanotify_data.max_marks) return -ENOSPC; @@ -645,16 +662,16 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, fsnotify_init_mark(fsn_mark, fanotify_free_mark); ret = fsnotify_add_mark(fsn_mark, group, inode, NULL, 0); - if (ret) { - fanotify_free_mark(fsn_mark); - return ret; - } + if (ret) + goto err; } added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); - fsnotify_put_mark(fsn_mark); + if (added & ~inode->i_fsnotify_mask) fsnotify_recalc_inode_mask(inode); - return 0; +err: + fsnotify_put_mark(fsn_mark); + return ret; } /* fanotify syscalls */ @@ -687,17 +704,20 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) /* fsnotify_alloc_group takes a ref. Dropped in fanotify_release */ group = fsnotify_alloc_group(&fanotify_fsnotify_ops); - if (IS_ERR(group)) + if (IS_ERR(group)) { + free_uid(user); return PTR_ERR(group); + } 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); INIT_LIST_HEAD(&group->fanotify_data.access_list); + atomic_set(&group->fanotify_data.bypass_perm, 0); #endif switch (flags & FAN_ALL_CLASS_BITS) { case FAN_CLASS_NOTIF: @@ -732,6 +752,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; @@ -764,8 +792,10 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, if (flags & ~FAN_ALL_MARK_FLAGS) return -EINVAL; switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_FLUSH)) { - case FAN_MARK_ADD: + case FAN_MARK_ADD: /* fallthrough */ case FAN_MARK_REMOVE: + if (!mask) + return -EINVAL; case FAN_MARK_FLUSH: break; default: diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 444c305a468c..4cd5d5d78f9f 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -752,6 +752,7 @@ SYSCALL_DEFINE1(inotify_init1, int, flags) if (ret >= 0) return ret; + fsnotify_put_group(group); atomic_dec(&user->inotify_devs); out_free_uid: free_uid(user); 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); |