All of lore.kernel.org
 help / color / mirror / Atom feed
From: Amir Goldstein <amir73il@gmail.com>
To: Jan Kara <jack@suse.cz>
Cc: linux-fsdevel@vger.kernel.org
Subject: [PATCH v2 12/16] fanotify: record name info for FAN_DIR_MODIFY event
Date: Mon, 17 Feb 2020 15:14:51 +0200	[thread overview]
Message-ID: <20200217131455.31107-13-amir73il@gmail.com> (raw)
In-Reply-To: <20200217131455.31107-1-amir73il@gmail.com>

For FAN_DIR_MODIFY event, allocate a larger event struct to store the
dir entry name along side the directory fid.

We are going to add support for reporting parent fid, name and child fid
for events reported on children.  FAN_DIR_MODIFY event does not record
nor report the child fid, but in order to stay consistent with events
"on child", we store the directory fid in struct fanotify_name_event and
not in the base struct fanotify_event as we do for other event types.

This wastes a few unused bytes (16) of memory per FAN_DIR_MODIFY event,
but keeps the code simpler and avoids creating a custom kmem_cache pool
just for FAN_DIR_MODIFY events.

At this point, name info reporting is not yet implemented, so trying to
set FAN_DIR_MODIFY in mark mask will return -EINVAL.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---
 fs/notify/fanotify/fanotify.c      | 87 +++++++++++++++++++++++++++---
 fs/notify/fanotify/fanotify.h      | 65 +++++++++++++++++++++-
 fs/notify/fanotify/fanotify_user.c |  5 +-
 3 files changed, 149 insertions(+), 8 deletions(-)

diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index 3bc28f08aad1..fc75dc53a218 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -33,7 +33,22 @@ static bool should_merge(struct fsnotify_event *old_fsn,
 	if (fanotify_event_has_path(old)) {
 		return old->path.mnt == new->path.mnt &&
 			old->path.dentry == new->path.dentry;
-	} else if (fanotify_event_has_fid(old)) {
+	}
+
+	if (!fanotify_fsid_equal(&old->fsid, &new->fsid))
+		return false;
+
+	if (fanotify_event_has_dfid_name(old)) {
+		if (!fanotify_dfid_name_equal(FANOTIFY_NE(old_fsn),
+					      FANOTIFY_NE(new_fsn)))
+			return false;
+
+		/* FAN_DIR_MODIFY does not encode the "child" fid */
+		if (!fanotify_event_has_fid(old))
+			return true;
+	}
+
+	if (fanotify_event_has_fid(old)) {
 		/*
 		 * We want to merge many dirent events in the same dir (i.e.
 		 * creates/unlinks/renames), but we do not want to merge dirent
@@ -43,7 +58,6 @@ static bool should_merge(struct fsnotify_event *old_fsn,
 		 * unlink pair or rmdir+create pair of events.
 		 */
 		return (old->mask & FS_ISDIR) == (new->mask & FS_ISDIR) &&
-			fanotify_fsid_equal(&old->fsid, &new->fsid) &&
 			fanotify_fid_equal(&old->fid, &new->fid, old->fh.len);
 	}
 
@@ -279,13 +293,16 @@ static struct inode *fanotify_fid_inode(struct inode *to_tell, u32 event_mask,
 struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
 					    struct inode *inode, u32 mask,
 					    const void *data, int data_type,
+					    const struct qstr *file_name,
 					    __kernel_fsid_t *fsid)
 {
 	struct fanotify_event *event = NULL;
+	struct fanotify_name_event *fne = NULL;
 	gfp_t gfp = GFP_KERNEL_ACCOUNT;
 	struct inode *id = fanotify_fid_inode(inode, mask, data, data_type);
 	const struct path *path = fsnotify_data_path(data, data_type);
 	struct dentry *dentry = fsnotify_data_dentry(data, data_type);
+	struct inode *dir = NULL;
 
 	/*
 	 * For queues with unlimited length lost events are not expected and
@@ -310,12 +327,56 @@ struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
 		event = &pevent->fae;
 		pevent->response = 0;
 		pevent->state = FAN_EVENT_INIT;
+		/*
+		 * Make sure that fanotify_event_has_fid() and
+		 * fanotify_event_has_name() are false for permission events.
+		 */
+		id = NULL;
+		event->dfh.type = FILEID_ROOT;
+		goto init;
+	}
+
+	/*
+	 * For FAN_DIR_MODIFY event, we report the fid of the directory and
+	 * the name of the modified entry.
+	 * Allocate an fanotify_name_event struct and copy the name.
+	 */
+	if (mask & FAN_DIR_MODIFY && !(WARN_ON_ONCE(!file_name))) {
+		char *name = NULL;
+
+		/*
+		 * Make sure that fanotify_event_has_name() is true and that
+		 * fanotify_event_has_fid() is false for FAN_DIR_MODIFY events.
+		 */
+		id = NULL;
+		dir = inode;
+		if (file_name->len + 1 > FANOTIFY_INLINE_NAME_LEN) {
+			name = kmalloc(file_name->len + 1, gfp);
+			if (!name)
+				goto out;
+		}
+
+		fne = kmem_cache_alloc(fanotify_name_event_cachep, gfp);
+		if (!fne)
+			goto out;
+
+		event = &fne->fae;
+		if (!name)
+			name = fne->inline_name;
+		strcpy(name, file_name->name);
+		fne->name.name = name;
+		fne->name.len = file_name->len;
+		event->fh.type = FILEID_INVALID;
+		event->dfh.type = FILEID_INVALID;
 		goto init;
 	}
+
 	event = kmem_cache_alloc(fanotify_event_cachep, gfp);
 	if (!event)
 		goto out;
-init: __maybe_unused
+
+	event->dfh.type = FILEID_ROOT;
+init:
 	/*
 	 * Use the dentry instead of inode as tag for event queue, so event
 	 * reported on parent is merged with event reported on child when both
@@ -328,11 +389,16 @@ init: __maybe_unused
 	else
 		event->pid = get_pid(task_tgid(current));
 	event->fh.len = 0;
+	event->dfh.len = 0;
 	if (fsid)
 		event->fsid = *fsid;
-	if (id && FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
+	if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
 		/* Report the event without a file identifier on encode error */
-		event->fh_type = fanotify_encode_fid(event, id, gfp, fsid);
+		if (id)
+			event->fh = fanotify_encode_fid(&event->fid, id, gfp);
+		/* The reported name is relative to 'dir' */
+		if (fne)
+			event->dfh = fanotify_encode_fid(&fne->dfid, dir, gfp);
 	} else if (path) {
 		event->fh.type = FILEID_ROOT;
 		event->path = *path;
@@ -439,7 +505,7 @@ static int fanotify_handle_event(struct fsnotify_group *group,
 	}
 
 	event = fanotify_alloc_event(group, inode, mask, data, data_type,
-				     &fsid);
+				     file_name, &fsid);
 	ret = -ENOMEM;
 	if (unlikely(!event)) {
 		/*
@@ -494,6 +560,15 @@ static void fanotify_free_event(struct fsnotify_event *fsn_event)
 		kmem_cache_free(fanotify_perm_event_cachep,
 				FANOTIFY_PE(fsn_event));
 		return;
+	} else if (fanotify_event_has_dfid_name(event)) {
+		struct fanotify_name_event *fne = FANOTIFY_NE(fsn_event);
+
+		if (fanotify_fid_has_ext_fh(&event->dfh))
+			kfree(fne->dfid.ext_fh);
+		if (fanotify_event_has_ext_name(fne))
+			kfree(fne->name.name);
+		kmem_cache_free(fanotify_name_event_cachep, fne);
+		return;
 	}
 	kmem_cache_free(fanotify_event_cachep, event);
 }
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index 4fee002235b6..e4a67a2d77b8 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -6,6 +6,7 @@
 
 extern struct kmem_cache *fanotify_mark_cache;
 extern struct kmem_cache *fanotify_event_cachep;
+extern struct kmem_cache *fanotify_name_event_cachep;
 extern struct kmem_cache *fanotify_perm_event_cachep;
 
 /* Possible states of the permission event */
@@ -84,9 +85,10 @@ struct fanotify_event {
 	 * on 64bit arch and to use fh.type as an indication of whether path
 	 * or fid are used in the union:
 	 * FILEID_ROOT (0) for path, > 0 for fid, FILEID_INVALID for neither.
+	 * Non zero dfh.type indicates embedded in an fanotify_name_event.
 	 */
 	struct fanotify_fid_hdr fh;
-	u16 pad;
+	struct fanotify_fid_hdr dfh;
 	__kernel_fsid_t fsid;
 	union {
 		/*
@@ -114,6 +116,66 @@ static inline bool fanotify_event_has_fid(struct fanotify_event *event)
 	return fanotify_fid_has_fh(&event->fh);
 }
 
+/*
+ * Structure for fanotify events with name info.
+ * DNAME_INLINE_LEN is good enough for dentry name, so it's good enough for us.
+ * It also happens to bring the size of this struct to 128 bytes on 64bit arch.
+ */
+#define FANOTIFY_INLINE_NAME_LEN DNAME_INLINE_LEN
+
+struct fanotify_name_event {
+	struct fanotify_event fae;
+	struct fanotify_fid  dfid;
+	struct qstr name;
+	unsigned char inline_name[FANOTIFY_INLINE_NAME_LEN];
+};
+
+static inline struct fanotify_name_event *
+FANOTIFY_NE(struct fsnotify_event *fse)
+{
+	return container_of(fse, struct fanotify_name_event, fae.fse);
+}
+
+static inline bool fanotify_event_has_dfid_name(struct fanotify_event *event)
+{
+	return event->dfh.type != FILEID_ROOT;
+}
+
+static inline unsigned int fanotify_event_name_len(struct fanotify_event *event)
+{
+	return event->dfh.type != FILEID_ROOT ?
+		FANOTIFY_NE(&event->fse)->name.len : 0;
+}
+
+static inline bool fanotify_event_has_ext_name(struct fanotify_name_event *fne)
+{
+	return fne->name.len + 1 > FANOTIFY_INLINE_NAME_LEN;
+}
+
+static inline bool fanotify_dfid_name_equal(struct fanotify_name_event *fne1,
+					    struct fanotify_name_event *fne2)
+{
+	struct qstr *name1 = &fne1->name;
+	struct qstr *name2 = &fne2->name;
+	struct fanotify_fid_hdr *dfh1 = &fne1->fae.dfh;
+	struct fanotify_fid_hdr *dfh2 = &fne2->fae.dfh;
+
+	if (dfh1->type != dfh2->type || dfh1->len != dfh2->len ||
+	    name1->len != name2->len)
+		return false;
+
+	/* Could be pointing to same external_name */
+	if (name1->len && name1->name != name2->name &&
+	    strcmp(name1->name, name2->name))
+		return false;
+
+	/* No dfid means that encoding failed */
+	if (!dfh1->len)
+		return true;
+
+	return fanotify_fid_equal(&fne1->dfid, &fne2->dfid, dfh1->len);
+}
+
 /*
  * Structure for permission fanotify events. It gets allocated and freed in
  * fanotify_handle_event() since we wait there for user response. When the
@@ -148,4 +210,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 void *data, int data_type,
+					    const struct qstr *file_name,
 					    __kernel_fsid_t *fsid);
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index beb9f0661a7c..284f3548bb79 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -47,6 +47,7 @@ extern const struct fsnotify_ops fanotify_fsnotify_ops;
 
 struct kmem_cache *fanotify_mark_cache __read_mostly;
 struct kmem_cache *fanotify_event_cachep __read_mostly;
+struct kmem_cache *fanotify_name_event_cachep __read_mostly;
 struct kmem_cache *fanotify_perm_event_cachep __read_mostly;
 
 #define FANOTIFY_EVENT_ALIGN 4
@@ -831,7 +832,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
 	group->memcg = get_mem_cgroup_from_mm(current->mm);
 
 	oevent = fanotify_alloc_event(group, NULL, FS_Q_OVERFLOW, NULL,
-				      FSNOTIFY_EVENT_NONE, NULL);
+				      FSNOTIFY_EVENT_NONE, NULL, NULL);
 	if (unlikely(!oevent)) {
 		fd = -ENOMEM;
 		goto out_destroy_group;
@@ -1147,6 +1148,8 @@ static int __init fanotify_user_setup(void)
 	fanotify_mark_cache = KMEM_CACHE(fsnotify_mark,
 					 SLAB_PANIC|SLAB_ACCOUNT);
 	fanotify_event_cachep = KMEM_CACHE(fanotify_event, SLAB_PANIC);
+	fanotify_name_event_cachep = KMEM_CACHE(fanotify_name_event,
+						SLAB_PANIC);
 	if (IS_ENABLED(CONFIG_FANOTIFY_ACCESS_PERMISSIONS)) {
 		fanotify_perm_event_cachep =
 			KMEM_CACHE(fanotify_perm_event, SLAB_PANIC);
-- 
2.17.1


  parent reply	other threads:[~2020-02-17 13:15 UTC|newest]

Thread overview: 68+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-02-17 13:14 [PATCH v2 00/16] Fanotify event with name info Amir Goldstein
2020-02-17 13:14 ` [PATCH v2 01/16] fsnotify: tidy up FS_ and FAN_ constants Amir Goldstein
2020-02-17 13:14 ` [PATCH v2 02/16] fsnotify: factor helpers fsnotify_dentry() and fsnotify_file() Amir Goldstein
2020-02-25 13:46   ` Jan Kara
2020-02-25 14:27     ` Amir Goldstein
2020-02-26 13:59       ` Jan Kara
2020-02-17 13:14 ` [PATCH v2 03/16] fsnotify: funnel all dirent events through fsnotify_name() Amir Goldstein
2020-02-17 13:14 ` [PATCH v2 04/16] fsnotify: use helpers to access data by data_type Amir Goldstein
2020-02-17 13:14 ` [PATCH v2 05/16] fsnotify: simplify arguments passing to fsnotify_parent() Amir Goldstein
2020-02-19 10:50   ` kbuild test robot
2020-02-19 10:50     ` kbuild test robot
2020-02-19 11:11   ` Amir Goldstein
2020-02-17 13:14 ` [PATCH v2 06/16] fsnotify: pass dentry instead of inode for events possible on child Amir Goldstein
2020-02-17 13:14 ` [PATCH v2 07/16] fsnotify: replace inode pointer with tag Amir Goldstein
2020-02-26  8:20   ` Jan Kara
2020-02-26  9:34     ` Amir Goldstein
2020-02-26  8:52   ` Jan Kara
2020-02-17 13:14 ` [PATCH v2 08/16] fanotify: merge duplicate events on parent and child Amir Goldstein
2020-02-26  9:18   ` Jan Kara
2020-02-26 12:14     ` Amir Goldstein
2020-02-26 14:38       ` Jan Kara
2021-01-22 13:59         ` fanotify_merge improvements Amir Goldstein
2021-01-23 13:30           ` Amir Goldstein
2021-01-25 13:01             ` Jan Kara
2021-01-26 16:21               ` Amir Goldstein
2021-01-27 11:24                 ` Jan Kara
2021-01-27 12:57                   ` Amir Goldstein
2021-01-27 15:15                     ` Jan Kara
2021-01-27 18:03                       ` Amir Goldstein
2021-01-28 10:27                         ` Jan Kara
2021-01-28 18:50                           ` Amir Goldstein
2020-02-17 13:14 ` [PATCH v2 09/16] fanotify: fix merging marks masks with FAN_ONDIR Amir Goldstein
2020-02-17 13:14 ` [PATCH v2 10/16] fanotify: send FAN_DIR_MODIFY event flavor with dir inode and name Amir Goldstein
2020-02-17 13:14 ` [PATCH v2 11/16] fanotify: prepare to encode both parent and child fid's Amir Goldstein
2020-02-26 10:23   ` Jan Kara
2020-02-26 11:53     ` Amir Goldstein
2020-02-26 17:07       ` Jan Kara
2020-02-26 17:50         ` Amir Goldstein
2020-02-27  9:06           ` Amir Goldstein
2020-02-27 11:27             ` Jan Kara
2020-02-27 12:12               ` Amir Goldstein
2020-02-27 13:30                 ` Jan Kara
2020-02-27 14:06                   ` Amir Goldstein
2020-03-01 16:26                     ` Amir Goldstein
2020-03-05 15:49                       ` Jan Kara
2020-03-06 11:19                         ` Amir Goldstein
2020-03-08  7:29                           ` Amir Goldstein
2020-03-18 17:51                             ` Jan Kara
2020-03-18 18:50                               ` Amir Goldstein
2020-03-19  9:30                                 ` Jan Kara
2020-03-19 10:07                                   ` Amir Goldstein
2020-03-30 19:29                                 ` Amir Goldstein
2020-02-27 11:01           ` Jan Kara
2020-02-17 13:14 ` Amir Goldstein [this message]
2020-02-17 13:14 ` [PATCH v2 13/16] fanotify: report name info for FAN_DIR_MODIFY event Amir Goldstein
2020-02-19  9:43   ` kbuild test robot
2020-02-19  9:43     ` kbuild test robot
2020-02-19 10:17   ` kbuild test robot
2020-02-19 10:17     ` kbuild test robot
2020-02-19 11:22   ` Amir Goldstein
2020-04-16 12:16   ` Michael Kerrisk (man-pages)
2020-04-20 15:53     ` Jan Kara
2020-04-20 18:45     ` Amir Goldstein
2020-04-20 18:47       ` Michael Kerrisk (man-pages)
2020-02-17 13:14 ` [PATCH v2 14/16] fanotify: report parent fid + name with FAN_REPORT_NAME Amir Goldstein
2020-02-17 13:14 ` [PATCH v2 15/16] fanotify: refine rules for when name is reported Amir Goldstein
2020-02-17 13:14 ` [BONUS][PATCH v2 16/16] fanotify: support limited functionality for unprivileged users Amir Goldstein
2020-02-20 22:10 ` [PATCH v2 00/16] Fanotify event with name info Matthew Bobrowski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20200217131455.31107-13-amir73il@gmail.com \
    --to=amir73il@gmail.com \
    --cc=jack@suse.cz \
    --cc=linux-fsdevel@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.