summaryrefslogtreecommitdiffstats
path: root/fs/notify/fanotify
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2019-01-08 14:02:44 +0100
committerJan Kara <jack@suse.cz>2019-02-18 12:41:16 +0100
commit40873284d7106fc0f0f4d2deae74b38fb18342cc (patch)
tree5d3965765a3d031321b026e576db2593242b77e0 /fs/notify/fanotify
parentca6f86998d810d4a9fe172bf4cb6d3353636881f (diff)
downloadlinux-40873284d7106fc0f0f4d2deae74b38fb18342cc.tar.bz2
fanotify: Track permission event state
Track whether permission event got already reported to userspace and whether userspace already answered to the permission event. Protect stores to this field together with updates to ->response field by group->notification_lock. This will allow aborting wait for reply to permission event from userspace. Reviewed-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/notify/fanotify')
-rw-r--r--fs/notify/fanotify/fanotify.c6
-rw-r--r--fs/notify/fanotify/fanotify.h10
-rw-r--r--fs/notify/fanotify/fanotify_user.c35
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);