If group requested FAN_REPORT_FID and event has file identifier copy that information to user reading the event after reading event metadata. metadata->event_len includes the length of the fid information. Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> --- fs/notify/fanotify/fanotify.h | 3 ++ fs/notify/fanotify/fanotify_user.c | 72 +++++++++++++++++++++++++++--- 2 files changed, 68 insertions(+), 7 deletions(-) diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index d6ed31134321..26752d2f2bd6 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -10,6 +10,9 @@ extern struct kmem_cache *fanotify_perm_event_cachep; /* The size of the variable length buffer storing fsid and file handle */ #define FANOTIFY_FID_LEN(handle_bytes) \ (sizeof(struct fanotify_event_fid) + (handle_bytes)) +#define FANOTIFY_FID_INFO_LEN(event) \ + (sizeof(struct fanotify_event_info) + \ + FANOTIFY_FID_LEN((event)->info.fid->handle_bytes)) struct fanotify_info { struct fanotify_event_fid *fid; diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 29f61060af66..7e38d7a1854b 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -47,6 +47,16 @@ struct kmem_cache *fanotify_mark_cache __read_mostly; struct kmem_cache *fanotify_event_cachep __read_mostly; struct kmem_cache *fanotify_perm_event_cachep __read_mostly; +static int round_event_fid_len(struct fsnotify_event *fsn_event) +{ + struct fanotify_event *event = FANOTIFY_E(fsn_event); + + if (!FANOTIFY_HAS_FID(event)) + return 0; + + return roundup(FANOTIFY_FID_INFO_LEN(event), FAN_EVENT_METADATA_LEN); +} + /* * Get an fsnotify notification event if one exists and is small * enough to fit in "count". Return an error pointer if the count @@ -57,6 +67,9 @@ struct kmem_cache *fanotify_perm_event_cachep __read_mostly; static struct fsnotify_event *get_one_event(struct fsnotify_group *group, size_t count) { + size_t event_size = FAN_EVENT_METADATA_LEN; + struct fsnotify_event *event; + assert_spin_locked(&group->notification_lock); pr_debug("%s: group=%p count=%zd\n", __func__, group, count); @@ -64,11 +77,18 @@ static struct fsnotify_event *get_one_event(struct fsnotify_group *group, if (fsnotify_notify_queue_is_empty(group)) return NULL; - if (FAN_EVENT_METADATA_LEN > count) + if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) { + event = fsnotify_peek_first_event(group); + event_size += round_event_fid_len(event); + } + + if (event_size > count) return ERR_PTR(-EINVAL); - /* held the notification_lock the whole time, so this is the - * same event we peeked above */ + /* + * Held the notification_lock the whole time, so this is the + * same event we peeked above + */ return fsnotify_remove_first_event(group); } @@ -129,7 +149,7 @@ static int fill_event_metadata(struct fsnotify_group *group, group, metadata, fsn_event); *file = NULL; - event = container_of(fsn_event, struct fanotify_event, fse); + event = FANOTIFY_E(fsn_event); metadata->event_len = FAN_EVENT_METADATA_LEN; metadata->metadata_len = FAN_EVENT_METADATA_LEN; metadata->vers = FANOTIFY_METADATA_VERSION; @@ -139,6 +159,7 @@ static int fill_event_metadata(struct fsnotify_group *group, if (FAN_GROUP_FLAG(group, FAN_REPORT_FID) || unlikely(fsn_event->mask & FAN_Q_OVERFLOW)) { metadata->fd = FAN_NOFD; + metadata->event_len += round_event_fid_len(fsn_event); } else { metadata->fd = create_fd(group, event, file); if (metadata->fd < 0) @@ -208,6 +229,38 @@ static int process_access_response(struct fsnotify_group *group, return 0; } +static int copy_fid_to_user(struct fsnotify_event *fsn_event, char __user *buf) +{ + struct fanotify_event *event = FANOTIFY_E(fsn_event); + struct fanotify_event_info ei; + size_t fid_len; + size_t pad_len = round_event_fid_len(fsn_event); + + if (!pad_len) + return 0; + + /* Copy event info header followed by fid buffer */ + fid_len = FANOTIFY_FID_INFO_LEN(event); + pad_len -= fid_len; + ei.info_type = FAN_EVENT_INFO_TYPE_FID; + ei.reserved = 0; + ei.info_len = fid_len; + if (copy_to_user(buf, &ei, sizeof(ei))) + return -EFAULT; + + buf += sizeof(ei); + fid_len -= sizeof(ei); + if (copy_to_user(buf, event->info.fid, fid_len)) + return -EFAULT; + + /* Pad with 0's */ + buf += fid_len; + if (pad_len && clear_user(buf, pad_len)) + return -EFAULT; + + return 0; +} + static ssize_t copy_event_to_user(struct fsnotify_group *group, struct fsnotify_event *event, char __user *buf) @@ -224,15 +277,20 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, fd = fanotify_event_metadata.fd; ret = -EFAULT; - if (copy_to_user(buf, &fanotify_event_metadata, - fanotify_event_metadata.event_len)) + if (copy_to_user(buf, &fanotify_event_metadata, FAN_EVENT_METADATA_LEN)) goto out_close_fd; if (fanotify_is_perm_event(event->mask)) FANOTIFY_PE(event)->fd = fd; - if (fd != FAN_NOFD) + if (fd != FAN_NOFD) { fd_install(fd, f); + } else if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) { + ret = copy_fid_to_user(event, buf + FAN_EVENT_METADATA_LEN); + if (ret < 0) + return ret; + } + return fanotify_event_metadata.event_len; out_close_fd: -- 2.17.1