diff options
author | Stephen Rothwell <sfr@canb.auug.org.au> | 2010-11-22 12:14:39 +1100 |
---|---|---|
committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2010-11-22 12:14:39 +1100 |
commit | 3f1a18692db1dc08bc5e3166c6185b7f9f31ed5b (patch) | |
tree | fc602f6c6e2b8b7c6d5a8e9d09df96d7a2e57213 | |
parent | 73d968d929f88dc47c89f2febd85a5a4d8719611 (diff) | |
parent | 3aa13e3ff6700929c0e3a1a4cdc51c82139707e4 (diff) |
Merge remote branch 'fsnotify/for-next'
-rw-r--r-- | fs/namei.c | 3 | ||||
-rw-r--r-- | fs/notify/fanotify/fanotify.c | 24 | ||||
-rw-r--r-- | fs/notify/fanotify/fanotify_user.c | 78 | ||||
-rw-r--r-- | fs/notify/mark.c | 36 | ||||
-rw-r--r-- | include/linux/fanotify.h | 7 | ||||
-rw-r--r-- | include/linux/fsnotify.h | 3 | ||||
-rw-r--r-- | include/linux/fsnotify_backend.h | 13 |
7 files changed, 97 insertions, 67 deletions
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/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index b04f88eed09e..cb576b84e9ea 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -180,13 +180,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 +193,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..ae36c73cc7c5 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,7 +117,7 @@ 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) { @@ -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 = ret; ret = prepare_for_access_response(group, event, fd); if (ret) goto out_close_fd; @@ -275,6 +287,13 @@ out_kill_access_response: remove_access_response(group, event, fd); out_close_fd: 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; } @@ -586,11 +605,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 +618,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 +636,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 +652,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 +661,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 */ @@ -693,7 +709,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); @@ -732,6 +748,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/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 0f0121467fc4..3db66ef817ee 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 @@ -96,7 +99,7 @@ struct fanotify_event_metadata { struct fanotify_response { __s32 fd; __u32 response; -} __attribute__ ((packed)); +}; /* Legit userspace responses to a _PERM event */ #define FAN_ALLOW 0x01 diff --git a/include/linux/fsnotify.h b/include/linux/fsnotify.h index 5c185fa27089..b10bcdeaef76 100644 --- a/include/linux/fsnotify.h +++ b/include/linux/fsnotify.h @@ -235,9 +235,6 @@ static inline void fsnotify_open(struct file *file) if (S_ISDIR(inode->i_mode)) mask |= FS_ISDIR; - /* FMODE_NONOTIFY must never be set from user */ - file->f_mode &= ~FMODE_NONOTIFY; - fsnotify_parent(path, NULL, mask); fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0); } diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 0a68f924f06f..d010f70c3b6c 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h @@ -168,6 +168,7 @@ struct fsnotify_group { wait_queue_head_t access_waitq; bool bypass_perm; /* protected by access_mutex */ #endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */ + bool readonly_fallback; int f_flags; unsigned int max_marks; struct user_struct *user; @@ -412,8 +413,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 */ @@ -427,6 +426,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, |