When event data type is FSNOTIFY_EVENT_DENTRY, 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 the dentry to encode fid. Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx> --- fs/notify/fanotify/fanotify.c | 63 ++++++++++++++++++++---------- fs/notify/fanotify/fanotify.h | 4 +- fs/notify/fanotify/fanotify_user.c | 6 ++- fs/statfs.c | 3 +- include/linux/statfs.h | 3 ++ 5 files changed, 53 insertions(+), 26 deletions(-) diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 844b748f0b74..9ee15af2f83d 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -92,10 +92,26 @@ static int fanotify_get_response(struct fsnotify_group *group, pr_debug("%s: group=%p event=%p about to return ret=%d\n", __func__, group, event, ret); - + return ret; } +static struct dentry *fanotify_event_dentry(const void *data, int data_type) +{ + struct dentry *dentry = NULL; + + if (data_type == FSNOTIFY_EVENT_PATH) { + dentry = ((struct path *)data)->dentry; + /* Path type events are only relevant for files and dirs */ + if (!d_is_reg(dentry) && !d_can_lookup(dentry)) + return NULL; + } else if (data_type == FSNOTIFY_EVENT_DENTRY) { + dentry = (struct dentry *)data; + } + + return dentry; +} + /* * This function returns a mask for an event that only contains the flags * that have been specifically requested by the user. Flags that may have @@ -107,7 +123,7 @@ static u32 fanotify_group_event_mask(struct fsnotify_iter_info *iter_info, int data_type) { __u32 marks_mask = 0, marks_ignored_mask = 0; - const struct path *path = data; + struct dentry *dentry = fanotify_event_dentry(data, data_type); struct fsnotify_mark *mark; int type; @@ -115,12 +131,7 @@ static u32 fanotify_group_event_mask(struct fsnotify_iter_info *iter_info, __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)) + if (!dentry) return 0; fsnotify_foreach_obj_type(type) { @@ -140,7 +151,7 @@ static u32 fanotify_group_event_mask(struct fsnotify_iter_info *iter_info, marks_ignored_mask |= mark->ignored_mask; } - if (d_is_dir(path->dentry) && + if (d_is_dir(dentry) && !(marks_mask & FS_ISDIR & ~marks_ignored_mask)) return 0; @@ -148,7 +159,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 dentry *dentry, gfp_t gfp) { struct fanotify_event_fid *fid = NULL; @@ -158,11 +169,11 @@ 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_fh(dentry, NULL, &dwords, 0); if (!dwords) goto out_err; - err = vfs_statfs(path, &stat); + err = statfs_by_dentry(dentry, &stat); if (err) goto out_err; @@ -172,7 +183,7 @@ 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, + type = exportfs_encode_fh(dentry, (struct fid *)fid->f_handle, &dwords, 0); err = -EINVAL; if (type == FILEID_INVALID || bytes != dwords << 2) @@ -186,15 +197,16 @@ static struct fanotify_event_fid *fanotify_alloc_fid(const struct path *path, out_err: pr_warn_ratelimited("fanotify: failed to encode fid of %pd2 (bytes=%d, err=%i)\n", - path->dentry, bytes, err); + dentry, bytes, err); kfree(fid); return ERR_PTR(err); } struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group, - struct inode *inode, u32 mask, - const struct path *path) + struct inode *inode, u32 mask, + const void *data, int data_type) { + struct dentry *dentry = fanotify_event_dentry(data, data_type); struct fanotify_event *event = NULL; struct fanotify_event_fid *fid = NULL; gfp_t gfp = GFP_KERNEL_ACCOUNT; @@ -210,8 +222,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 && FAN_GROUP_FLAG(group, FAN_REPORT_FID)) { - fid = fanotify_alloc_fid(path, gfp); + if (dentry && FAN_GROUP_FLAG(group, FAN_REPORT_FID)) { + fid = fanotify_alloc_fid(dentry, gfp); /* Treat failure to allocate fid as failure to allocate event */ if (!fid) goto out; @@ -241,9 +253,13 @@ 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 && data_type == FSNOTIFY_EVENT_PATH) { + event->path = *((const struct path *)data); path_get(&event->path); + pr_debug("%s: mnt=%p, dentry=%p parent=%p d_flags=%x\n", + __func__, event->path.mnt, event->path.dentry, + event->path.dentry->d_parent, + event->path.dentry->d_flags); } else { event->path.mnt = NULL; event->path.dentry = NULL; @@ -278,6 +294,11 @@ static int fanotify_handle_event(struct fsnotify_group *group, BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 12); + /* Dentry type events cannot report fd, so require FAN_REPORT_FID */ + if (data_type == FSNOTIFY_EVENT_DENTRY && + !FAN_GROUP_FLAG(group, FAN_REPORT_FID)) + return 0; + mask = fanotify_group_event_mask(iter_info, mask, data, data_type); if (!mask) return 0; @@ -294,7 +315,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, return 0; } - event = fanotify_alloc_event(group, inode, mask, data); + event = fanotify_alloc_event(group, inode, mask, data, data_type); ret = -ENOMEM; if (unlikely(!event)) { /* diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index 26752d2f2bd6..0ecbbe04798e 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -99,5 +99,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); + struct inode *inode, u32 mask, + const void *data, int data_type); diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 8ded9b5c0e76..aa68f617964b 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -158,7 +158,8 @@ static int fill_event_metadata(struct fsnotify_group *group, metadata->mask = fsn_event->mask & FANOTIFY_OUTGOING_EVENTS; metadata->pid = pid_vnr(event->pid); if (FAN_GROUP_FLAG(group, FAN_REPORT_FID) || - unlikely(fsn_event->mask & FAN_Q_OVERFLOW)) { + unlikely(fsn_event->mask & FAN_Q_OVERFLOW) || + !event->path.mnt || !event->path.dentry) { metadata->fd = FAN_NOFD; metadata->event_len += round_event_fid_len(fsn_event); } else { @@ -796,7 +797,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); + oevent = fanotify_alloc_event(group, NULL, FS_Q_OVERFLOW, NULL, + FSNOTIFY_EVENT_NONE); if (unlikely(!oevent)) { fd = -ENOMEM; goto out_destroy_group; diff --git a/fs/statfs.c b/fs/statfs.c index f0216629621d..fd718ea14b14 100644 --- a/fs/statfs.c +++ b/fs/statfs.c @@ -50,7 +50,8 @@ static int calculate_f_flags(struct vfsmount *mnt) flags_by_sb(mnt->mnt_sb->s_flags); } -static int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf) +/* Does not set buf->f_flags */ +int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf) { int retval; diff --git a/include/linux/statfs.h b/include/linux/statfs.h index 3142e98546ac..c733f5a9503c 100644 --- a/include/linux/statfs.h +++ b/include/linux/statfs.h @@ -41,4 +41,7 @@ struct kstatfs { #define ST_NODIRATIME 0x0800 /* do not update directory access times */ #define ST_RELATIME 0x1000 /* update atime relative to mtime/ctime */ +struct dentry; +extern int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf); + #endif -- 2.17.1