[v5,15/17] fanotify: support events with data type FSNOTIFY_EVENT_INODE
diff mbox series

Message ID 20190110170444.30616-16-amir73il@gmail.com
State New
Headers show
Series
  • fanotify: add support for more event types
Related show

Commit Message

Amir Goldstein Jan. 10, 2019, 5:04 p.m. UTC
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 reporting those
events and we use an identifier inode to encode fid.

The inode to use as identifier when reporting fid depends 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@gmail.com>
---
 fs/notify/fanotify/fanotify.c      | 67 ++++++++++++++++++++----------
 fs/notify/fanotify/fanotify.h      |  2 +-
 fs/notify/fanotify/fanotify_user.c |  3 +-
 3 files changed, 48 insertions(+), 24 deletions(-)

Comments

Jan Kara Feb. 7, 2019, 3:18 p.m. UTC | #1
On Thu 10-01-19 19:04:42, Amir Goldstein wrote:
> 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 reporting those
> events and we use an identifier inode to encode fid.
> 
> The inode to use as identifier when reporting fid depends 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@gmail.com>
> ---
>  fs/notify/fanotify/fanotify.c      | 67 ++++++++++++++++++++----------
>  fs/notify/fanotify/fanotify.h      |  2 +-
>  fs/notify/fanotify/fanotify_user.c |  3 +-
>  3 files changed, 48 insertions(+), 24 deletions(-)
> 
> diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
> index fcb98ea99508..e3ca1632feb8 100644
> --- a/fs/notify/fanotify/fanotify.c
> +++ b/fs/notify/fanotify/fanotify.c
> @@ -96,7 +96,7 @@ 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;
>  }
>  
> @@ -106,9 +106,10 @@ static int fanotify_get_response(struct fsnotify_group *group,
>   * been included within the event mask, but have not been explicitly
>   * requested by the user, will not be present in the returned mask.
>   */
> -static u32 fanotify_group_event_mask(struct fsnotify_iter_info *iter_info,
> -				       u32 event_mask, const void *data,
> -				       int data_type)
> +static u32 fanotify_group_event_mask(struct fsnotify_group *group,
> +				     struct fsnotify_iter_info *iter_info,
> +				     u32 event_mask, const void *data,
> +				     int data_type)
>  {
>  	__u32 marks_mask = 0, marks_ignored_mask = 0;
>  	const struct path *path = data;
> @@ -118,14 +119,14 @@ 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))
> +	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;

Hum, does this mean that fanotify won't report O_PATH open on symlink
unlike inotify? So shouldn't the condition rather be:

	if (!FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
		if (data_type != FSNOTIFY_EVENT_PATH)
			return 0;
		if (!d_is_reg(path->dentry) && !d_can_lookup(path->dentry))
			return 0;
	}

?
								Honza
Amir Goldstein Feb. 7, 2019, 4:10 p.m. UTC | #2
On Thu, Feb 7, 2019 at 5:18 PM Jan Kara <jack@suse.cz> wrote:
>
> On Thu 10-01-19 19:04:42, Amir Goldstein wrote:
> > 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 reporting those
> > events and we use an identifier inode to encode fid.
> >
> > The inode to use as identifier when reporting fid depends 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@gmail.com>
> > ---
> >  fs/notify/fanotify/fanotify.c      | 67 ++++++++++++++++++++----------
> >  fs/notify/fanotify/fanotify.h      |  2 +-
> >  fs/notify/fanotify/fanotify_user.c |  3 +-
> >  3 files changed, 48 insertions(+), 24 deletions(-)
> >
> > diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
> > index fcb98ea99508..e3ca1632feb8 100644
> > --- a/fs/notify/fanotify/fanotify.c
> > +++ b/fs/notify/fanotify/fanotify.c
> > @@ -96,7 +96,7 @@ 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;
> >  }
> >
> > @@ -106,9 +106,10 @@ static int fanotify_get_response(struct fsnotify_group *group,
> >   * been included within the event mask, but have not been explicitly
> >   * requested by the user, will not be present in the returned mask.
> >   */
> > -static u32 fanotify_group_event_mask(struct fsnotify_iter_info *iter_info,
> > -                                    u32 event_mask, const void *data,
> > -                                    int data_type)
> > +static u32 fanotify_group_event_mask(struct fsnotify_group *group,
> > +                                  struct fsnotify_iter_info *iter_info,
> > +                                  u32 event_mask, const void *data,
> > +                                  int data_type)
> >  {
> >       __u32 marks_mask = 0, marks_ignored_mask = 0;
> >       const struct path *path = data;
> > @@ -118,14 +119,14 @@ 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))
> > +     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;
>
> Hum, does this mean that fanotify won't report O_PATH open on symlink
> unlike inotify? So shouldn't the condition rather be:
>
>         if (!FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
>                 if (data_type != FSNOTIFY_EVENT_PATH)
>                         return 0;
>                 if (!d_is_reg(path->dentry) && !d_can_lookup(path->dentry))
>                         return 0;
>         }
>
> ?

Makes sense.

Thanks,
Amir.

Patch
diff mbox series

diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index fcb98ea99508..e3ca1632feb8 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -96,7 +96,7 @@  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;
 }
 
@@ -106,9 +106,10 @@  static int fanotify_get_response(struct fsnotify_group *group,
  * been included within the event mask, but have not been explicitly
  * requested by the user, will not be present in the returned mask.
  */
-static u32 fanotify_group_event_mask(struct fsnotify_iter_info *iter_info,
-				       u32 event_mask, const void *data,
-				       int data_type)
+static u32 fanotify_group_event_mask(struct fsnotify_group *group,
+				     struct fsnotify_iter_info *iter_info,
+				     u32 event_mask, const void *data,
+				     int data_type)
 {
 	__u32 marks_mask = 0, marks_ignored_mask = 0;
 	const struct path *path = data;
@@ -118,14 +119,14 @@  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))
+	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;
+	} else if (!FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
+		/* Events without path data require FAN_REPORT_FID */
 		return 0;
+	}
 
 	fsnotify_foreach_obj_type(type) {
 		if (!fsnotify_iter_should_report_type(iter_info, type))
@@ -153,7 +154,7 @@  static u32 fanotify_group_event_mask(struct fsnotify_iter_info *iter_info,
 }
 
 static int fanotify_encode_fid(struct fanotify_event *event,
-			       const struct path *path, gfp_t gfp,
+			       struct inode *inode, gfp_t gfp,
 			       __kernel_fsid_t *fsid)
 {
 	struct fanotify_fid *fid = &event->fid;
@@ -166,7 +167,7 @@  static int fanotify_encode_fid(struct fanotify_event *event,
 	fid->ext_fh = NULL;
 	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;
 
@@ -179,8 +180,8 @@  static int fanotify_encode_fid(struct fanotify_event *event,
 			goto out_err;
 	}
 
-	type = exportfs_encode_fh(path->dentry, fanotify_fid_fh(fid, bytes),
-				  &dwords,  0);
+	type = exportfs_encode_inode_fh(inode, fanotify_fid_fh(fid, bytes),
+					&dwords,  NULL);
 	err = -EINVAL;
 	if (!type || type == FILEID_INVALID || bytes != dwords << 2)
 		goto out_err;
@@ -201,13 +202,34 @@  static int fanotify_encode_fid(struct fanotify_event *event,
 	return FILEID_INVALID;
 }
 
+/*
+ * The inode to use as indentifier when reporting fid depends 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_fid_inode(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);
+	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;
 	gfp_t gfp = GFP_KERNEL_ACCOUNT;
+	struct inode *id = fanotify_fid_inode(inode, mask, data, data_type);
 
 	/*
 	 * For queues with unlimited length lost events are not expected and
@@ -241,12 +263,12 @@  init: __maybe_unused
 	else
 		event->pid = get_pid(task_tgid(current));
 	event->fh_len = 0;
-	if (path && FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
+	if (id && FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
 		/* Report the event without a file identifier on encode error */
-		event->fh_type = fanotify_encode_fid(event, path, gfp, fsid);
-	} else if (path) {
+		event->fh_type = fanotify_encode_fid(event, id, gfp, fsid);
+	} else if (data_type == FSNOTIFY_EVENT_PATH) {
 		event->fh_type = FILEID_ROOT;
-		event->path = *path;
+		event->path = *((struct path *)data);
 		path_get(&event->path);
 	} else {
 		event->fh_type = FILEID_INVALID;
@@ -306,7 +328,8 @@  static int fanotify_handle_event(struct fsnotify_group *group,
 
 	BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 12);
 
-	mask = fanotify_group_event_mask(iter_info, mask, data, data_type);
+	mask = fanotify_group_event_mask(group, iter_info, mask, data,
+					 data_type);
 	if (!mask)
 		return 0;
 
@@ -325,7 +348,7 @@  static int fanotify_handle_event(struct fsnotify_group *group,
 	if (FAN_GROUP_FLAG(group, FAN_REPORT_FID))
 		fsid = fanotify_get_fsid(iter_info, &__fsid);
 
-	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 5b072afa4e19..e84d68c6840a 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -132,5 +132,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 4254bfedb40b..8ac3ebc7b5ed 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -800,7 +800,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;