diff mbox series

[v2,7/9] fanotify: support events with data type FSNOTIFY_EVENT_DENTRY

Message ID 20181115184544.30681-8-amir73il@gmail.com (mailing list archive)
State New, archived
Headers show
Series fanotify: add support for more event types | expand

Commit Message

Amir Goldstein Nov. 15, 2018, 6:45 p.m. UTC
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(-)
diff mbox series

Patch

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