diff options
-rw-r--r-- | fs/notify/fanotify/fanotify.c | 6 | ||||
-rw-r--r-- | fs/notify/fanotify/fanotify.h | 10 | ||||
-rw-r--r-- | fs/notify/fanotify/fanotify_user.c | 35 |
3 files changed, 40 insertions, 11 deletions
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 4ff84bc5772e..812c975df7ec 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -85,7 +85,8 @@ static int fanotify_get_response(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->state == FAN_EVENT_ANSWERED); /* userspace responded, convert to something usable */ switch (event->response & ~FAN_AUDIT) { @@ -101,8 +102,6 @@ static int fanotify_get_response(struct fsnotify_group *group, if (event->response & FAN_AUDIT) audit_fanotify(event->response & ~FAN_AUDIT); - event->response = 0; - pr_debug("%s: group=%p event=%p about to return ret=%d\n", __func__, group, event, ret); @@ -275,6 +274,7 @@ struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group, goto out; event = &pevent->fae; pevent->response = 0; + pevent->state = FAN_EVENT_INIT; goto init; } event = kmem_cache_alloc(fanotify_event_cachep, gfp); diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index e84d68c6840a..480f281996d4 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -8,6 +8,13 @@ extern struct kmem_cache *fanotify_mark_cache; extern struct kmem_cache *fanotify_event_cachep; extern struct kmem_cache *fanotify_perm_event_cachep; +/* Possible states of the permission event */ +enum { + FAN_EVENT_INIT, + FAN_EVENT_REPORTED, + FAN_EVENT_ANSWERED +}; + /* * 3 dwords are sufficient for most local fs (64bit ino, 32bit generation). * For 32bit arch, fid increases the size of fanotify_event by 12 bytes and @@ -109,7 +116,8 @@ static inline void *fanotify_event_fh(struct fanotify_event *event) */ struct fanotify_perm_event { struct fanotify_event fae; - int response; /* userspace answer to question */ + unsigned short response; /* userspace answer to the event */ + unsigned short state; /* state of the event */ int fd; /* fd we passed to userspace for this event */ }; diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index a73ada49fd3e..3c272f61d341 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -64,7 +64,8 @@ static int fanotify_event_info_len(struct fanotify_event *event) /* * Get an fsnotify notification event if one exists and is small * enough to fit in "count". Return an error pointer if the count - * is not large enough. + * is not large enough. When permission event is dequeued, its state is + * updated accordingly. */ static struct fsnotify_event *get_one_event(struct fsnotify_group *group, size_t count) @@ -88,6 +89,8 @@ static struct fsnotify_event *get_one_event(struct fsnotify_group *group, goto out; } fsn_event = fsnotify_remove_first_event(group); + if (fanotify_is_perm_event(FANOTIFY_E(fsn_event)->mask)) + FANOTIFY_PE(fsn_event)->state = FAN_EVENT_REPORTED; out: spin_unlock(&group->notification_lock); return fsn_event; @@ -135,6 +138,21 @@ static int create_fd(struct fsnotify_group *group, return client_fd; } +/* + * Finish processing of permission event by setting it to ANSWERED state and + * drop group->notification_lock. + */ +static void finish_permission_event(struct fsnotify_group *group, + struct fanotify_perm_event *event, + unsigned int response) + __releases(&group->notification_lock) +{ + assert_spin_locked(&group->notification_lock); + event->response = response; + event->state = FAN_EVENT_ANSWERED; + spin_unlock(&group->notification_lock); +} + static int process_access_response(struct fsnotify_group *group, struct fanotify_response *response_struct) { @@ -170,8 +188,7 @@ static int process_access_response(struct fsnotify_group *group, continue; list_del_init(&event->fae.fse.list); - event->response = response; - spin_unlock(&group->notification_lock); + finish_permission_event(group, event, response); wake_up(&group->fanotify_data.access_waitq); return 0; } @@ -354,7 +371,9 @@ static ssize_t fanotify_read(struct file *file, char __user *buf, fsnotify_destroy_event(group, kevent); } else { if (ret <= 0) { - FANOTIFY_PE(kevent)->response = FAN_DENY; + spin_lock(&group->notification_lock); + finish_permission_event(group, + FANOTIFY_PE(kevent), FAN_DENY); wake_up(&group->fanotify_data.access_waitq); } else { spin_lock(&group->notification_lock); @@ -423,7 +442,8 @@ static int fanotify_release(struct inode *ignored, struct file *file) event = list_first_entry(&group->fanotify_data.access_list, struct fanotify_perm_event, fae.fse.list); list_del_init(&event->fae.fse.list); - event->response = FAN_ALLOW; + finish_permission_event(group, event, FAN_ALLOW); + spin_lock(&group->notification_lock); } /* @@ -436,10 +456,11 @@ static int fanotify_release(struct inode *ignored, struct file *file) if (!(FANOTIFY_E(fsn_event)->mask & FANOTIFY_PERM_EVENTS)) { spin_unlock(&group->notification_lock); fsnotify_destroy_event(group, fsn_event); - spin_lock(&group->notification_lock); } else { - FANOTIFY_PE(fsn_event)->response = FAN_ALLOW; + finish_permission_event(group, FANOTIFY_PE(fsn_event), + FAN_ALLOW); } + spin_lock(&group->notification_lock); } spin_unlock(&group->notification_lock); |