When event data type is FSNOTIFY_EVENT_INODE, we don't have a refernece to the mount, so we will not be able to open a file descriptor when user reads the event. However, if the listener has enabled reporting file identifier with the FAN_REPORT_FID init flag, we allow repoting those events and we use an indentifier inode to encode fid. The inode to use as indetifier when reporting fid depedns on the event. For dirent modification events, we report the modified directory inode and we report the "victim" inode otherwise. For example: FS_ATTRIB reports the child inode even if reported on a watched parent. FS_CREATE reports the modified dir inode and not the created inode. Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> --- fs/notify/fanotify/fanotify.c | 62 ++++++++++++++++++++---------- fs/notify/fanotify/fanotify.h | 2 +- fs/notify/fanotify/fanotify_user.c | 3 +- 3 files changed, 45 insertions(+), 22 deletions(-) diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index ef567d07f5a6..3e8b3abe4952 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -114,14 +114,11 @@ static u32 fanotify_group_event_mask(struct fsnotify_iter_info *iter_info, pr_debug("%s: report_mask=%x mask=%x data=%p data_type=%d\n", __func__, iter_info->report_mask, event_mask, data, data_type); - /* If we don't have enough info to send an event to userspace say no */ - if (data_type != FSNOTIFY_EVENT_PATH) - return 0; - - /* Sorry, fanotify only gives a damn about files and dirs */ - if (!d_is_reg(path->dentry) && - !d_can_lookup(path->dentry)) - return 0; + if (data_type == FSNOTIFY_EVENT_PATH) { + /* Path type events are only relevant for files and dirs */ + if (!d_is_reg(path->dentry) && !d_can_lookup(path->dentry)) + return 0; + } fsnotify_foreach_obj_type(type) { if (!fsnotify_iter_should_report_type(iter_info, type)) @@ -155,7 +152,7 @@ static u32 fanotify_group_event_mask(struct fsnotify_iter_info *iter_info, ~marks_ignored_mask; } -static struct fanotify_event_fid *fanotify_alloc_fid(const struct path *path, +static struct fanotify_event_fid *fanotify_alloc_fid(struct inode *inode, gfp_t gfp) { struct fanotify_event_fid *fid = NULL; @@ -164,7 +161,7 @@ static struct fanotify_event_fid *fanotify_alloc_fid(const struct path *path, dwords = 0; err = -ENOENT; - type = exportfs_encode_fh(path->dentry, NULL, &dwords, 0); + type = exportfs_encode_inode_fh(inode, NULL, &dwords, NULL); if (!dwords) goto out_err; @@ -174,8 +171,8 @@ static struct fanotify_event_fid *fanotify_alloc_fid(const struct path *path, if (!fid) return NULL; - type = exportfs_encode_fh(path->dentry, (struct fid *)fid->f_handle, - &dwords, 0); + type = exportfs_encode_inode_fh(inode, (struct fid *)fid->f_handle, + &dwords, NULL); err = -EINVAL; if (type == FILEID_INVALID || bytes != dwords << 2) goto out_err; @@ -186,20 +183,42 @@ static struct fanotify_event_fid *fanotify_alloc_fid(const struct path *path, return fid; out_err: - pr_warn_ratelimited("fanotify: failed to encode fid of %pd2 (bytes=%d, err=%i)\n", - path->dentry, bytes, err); + pr_warn_ratelimited("fanotify: failed to encode fid (ino=%lu, type=%d, bytes=%d, err=%i)\n", + inode->i_ino, type, bytes, err); kfree(fid); return ERR_PTR(err); } +/* + * The inode to use as indetifier when reporting fid depedns on the event. + * Report the modified directory inode on dirent modification events. + * Report the "victim" inode otherwise. + * For example: + * FS_ATTRIB reports the child inode even if reported on a watched parent. + * FS_CREATE reports the modified dir inode and not the created inode. + */ +static struct inode *fanotify_report_id(struct inode *to_tell, u32 event_mask, + const void *data, int data_type) +{ + if (event_mask & ALL_FSNOTIFY_DIRENT_EVENTS) + return to_tell; + else if (data_type == FSNOTIFY_EVENT_INODE) + return (struct inode *)data; + else if (data_type == FSNOTIFY_EVENT_PATH) + return d_inode(((struct path *)data)->dentry); + else + return NULL; +} + struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group, struct inode *inode, u32 mask, - const struct path *path, + const void *data, int data_type, __kernel_fsid_t *fsid) { struct fanotify_event *event = NULL; struct fanotify_event_fid *fid = NULL; gfp_t gfp = GFP_KERNEL_ACCOUNT; + struct inode *id = fanotify_report_id(inode, mask, data, data_type); /* * For queues with unlimited length lost events are not expected and @@ -212,8 +231,8 @@ struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group, /* Whoever is interested in the event, pays for the allocation. */ memalloc_use_memcg(group->memcg); - if (path && fsid && FAN_GROUP_FLAG(group, FAN_REPORT_FID)) { - fid = fanotify_alloc_fid(path, gfp); + if (id && fsid && FAN_GROUP_FLAG(group, FAN_REPORT_FID)) { + fid = fanotify_alloc_fid(id, gfp); /* Treat failure to allocate fid as failure to allocate event */ if (!fid) goto out; @@ -245,8 +264,8 @@ init: __maybe_unused event->pid = get_pid(task_tgid(current)); if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) { fanotify_set_fid(event, fid); - } else if (path) { - event->path = *path; + } else if (data_type == FSNOTIFY_EVENT_PATH) { + event->path = *((struct path *)data); path_get(&event->path); } else { event->path.mnt = NULL; @@ -283,10 +302,13 @@ static int fanotify_handle_event(struct fsnotify_group *group, BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 12); + /* Events without path data require FAN_REPORT_FID */ if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) { __fsid.val[0] = 0; __fsid.val[1] = 0; fsid = &__fsid; + } else if (data_type != FSNOTIFY_EVENT_PATH) { + return 0; } mask = fanotify_group_event_mask(iter_info, mask, data, data_type, @@ -306,7 +328,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, return 0; } - event = fanotify_alloc_event(group, inode, mask, data, fsid); + event = fanotify_alloc_event(group, inode, mask, data, data_type, fsid); ret = -ENOMEM; if (unlikely(!event)) { /* diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index b9938626b2a3..3c21c9a1278d 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -100,5 +100,5 @@ static inline struct fanotify_event *FANOTIFY_E(struct fsnotify_event *fse) struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group, struct inode *inode, u32 mask, - const struct path *path, + const void *data, int data_type, __kernel_fsid_t *fsid); diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 92afb297fdea..9479bd418c31 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -799,7 +799,8 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) atomic_inc(&user->fanotify_listeners); group->memcg = get_mem_cgroup_from_mm(current->mm); - oevent = fanotify_alloc_event(group, NULL, FS_Q_OVERFLOW, NULL, NULL); + oevent = fanotify_alloc_event(group, NULL, FS_Q_OVERFLOW, NULL, + FSNOTIFY_EVENT_NONE, NULL); if (unlikely(!oevent)) { fd = -ENOMEM; goto out_destroy_group; -- 2.17.1