@@ -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)) {
/*
@@ -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);
@@ -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;
@@ -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;
@@ -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
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@gmail.com> --- 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(-)