For handling of a crash of HSM service daemon, we need to somehow handle permission events which were already reported but not yet answered. We cannot just allow them as that will let the application access unpopulated content. Add support for resending these events on group shutdown. The intended use is that the HSM service will store fd pointing to its notification group info fd store so the notification group survives service crash. The newly started service can setup necessary watches and then destroy the old notification group at which point it will receive all the events pending against the old group. Signed-off-by: Jan Kara <jack@xxxxxxx> --- fs/notify/fanotify/fanotify.c | 8 +++++++- fs/notify/fanotify/fanotify.h | 1 + fs/notify/fanotify/fanotify_user.c | 13 ++++++++++--- fs/notify/fsnotify.c | 7 +++++++ include/uapi/linux/fanotify.h | 1 + 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 3083643b864b..3e2a09946603 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -230,7 +230,8 @@ static int fanotify_get_response(struct fsnotify_group *group, pr_debug("%s: group=%p event=%p\n", __func__, group, event); ret = wait_event_state(group->fanotify_data.access_waitq, - event->state == FAN_EVENT_ANSWERED, + event->state == FAN_EVENT_ANSWERED || + event->state == FAN_EVENT_RETRY, (TASK_KILLABLE|TASK_FREEZABLE)); /* Signal pending? */ @@ -258,6 +259,11 @@ static int fanotify_get_response(struct fsnotify_group *group, goto out; } + if (event->state == FAN_EVENT_RETRY) { + ret = -ERESTART; + goto out; + } + /* userspace responded, convert to something usable */ switch (event->response & FANOTIFY_RESPONSE_ACCESS) { case FAN_ALLOW: diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index b78308975082..ff96a5feae92 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -17,6 +17,7 @@ enum { FAN_EVENT_REPORTED, FAN_EVENT_ANSWERED, FAN_EVENT_CANCELED, + FAN_EVENT_RETRY, }; /* diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index b192ee068a7a..40922c4c7049 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -318,7 +318,10 @@ static void finish_permission_event(struct fsnotify_group *group, if (event->state == FAN_EVENT_CANCELED) destroy = true; else - event->state = FAN_EVENT_ANSWERED; + if (response) + event->state = FAN_EVENT_ANSWERED; + else + event->state = FAN_EVENT_RETRY; spin_unlock(&group->notification_lock); if (destroy) fsnotify_destroy_event(group, &event->fae.fse); @@ -1004,6 +1007,10 @@ static int fanotify_release(struct inode *ignored, struct file *file) { struct fsnotify_group *group = file->private_data; struct fsnotify_event *fsn_event; + u32 default_response = FAN_ALLOW; + + if (FAN_GROUP_FLAG(group, FAN_RETRY_UNANSWERED)) + default_response = 0; /* * Stop new events from arriving in the notification queue. since @@ -1023,7 +1030,7 @@ 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); - finish_permission_event(group, event, FAN_ALLOW, NULL); + finish_permission_event(group, event, default_response, NULL); spin_lock(&group->notification_lock); } @@ -1040,7 +1047,7 @@ static int fanotify_release(struct inode *ignored, struct file *file) fsnotify_destroy_event(group, fsn_event); } else { finish_permission_event(group, FANOTIFY_PERM(event), - FAN_ALLOW, NULL); + default_response, NULL); } spin_lock(&group->notification_lock); } diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index e2b4f17a48bb..b0eb86124e32 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -588,6 +588,7 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir, (!mnt_data || !mnt_data->ns->n_fsnotify_marks)) return 0; +resend: if (sb) marks_mask |= READ_ONCE(sb->s_fsnotify_mask); if (mnt) @@ -649,6 +650,12 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir, ret = 0; out: srcu_read_unlock(&fsnotify_mark_srcu, iter_info.srcu_idx); + /* + * Resend permission event in case some group got shutdown before it + * could answer + */ + if (ret == -ERESTART) + goto resend; return ret; } diff --git a/include/uapi/linux/fanotify.h b/include/uapi/linux/fanotify.h index e710967c7c26..4eb2313dbcf0 100644 --- a/include/uapi/linux/fanotify.h +++ b/include/uapi/linux/fanotify.h @@ -57,6 +57,7 @@ #define FAN_UNLIMITED_QUEUE 0x00000010 #define FAN_UNLIMITED_MARKS 0x00000020 #define FAN_ENABLE_AUDIT 0x00000040 +#define FAN_RETRY_UNANSWERED 0x00008000 /* Flags to determine fanotify event format */ #define FAN_REPORT_PIDFD 0x00000080 /* Report pidfd for event->pid */ -- 2.43.0 --h4lryuyboajgja5h--