Commit d61fd650 authored by Amir Goldstein's avatar Amir Goldstein Committed by Jan Kara
Browse files

fanotify: introduce group flag FAN_REPORT_TARGET_FID

FAN_REPORT_FID is ambiguous in that it reports the fid of the child for
some events and the fid of the parent for create/delete/move events.

The new FAN_REPORT_TARGET_FID flag is an implicit request to report
the fid of the target object of the operation (a.k.a the child inode)
also in create/delete/move events in addition to the fid of the parent
and the name of the child.

To reduce the test matrix for uninteresting use cases, the new
FAN_REPORT_TARGET_FID flag requires both FAN_REPORT_NAME and
FAN_REPORT_FID.  The convenience macro FAN_REPORT_DFID_NAME_TARGET
combines FAN_REPORT_TARGET_FID with all the required flags.

Link: https://lore.kernel.org/r/20211129201537.1932819-4-amir73il@gmail.com


Signed-off-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent 1c9007d6
Loading
Loading
Loading
Loading
+36 −12
Original line number Diff line number Diff line
@@ -458,17 +458,41 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
}

/*
 * The inode to use as identifier when reporting fid depends on the event.
 * Report the modified directory inode on dirent modification events.
 * Report the "victim" inode otherwise.
 * FAN_REPORT_FID is ambiguous in that it reports the fid of the child for
 * some events and the fid of the parent for create/delete/move events.
 *
 * With the FAN_REPORT_TARGET_FID flag, the fid of the child is reported
 * also in create/delete/move events in addition to the fid of the parent
 * and the name of the child.
 */
static inline bool fanotify_report_child_fid(unsigned int fid_mode, u32 mask)
{
	if (mask & ALL_FSNOTIFY_DIRENT_EVENTS)
		return (fid_mode & FAN_REPORT_TARGET_FID);

	return (fid_mode & FAN_REPORT_FID) && !(mask & FAN_ONDIR);
}

/*
 * The inode to use as identifier when reporting fid depends on the event
 * and the group flags.
 *
 * With the group flag FAN_REPORT_TARGET_FID, always report the child fid.
 *
 * Without the group flag FAN_REPORT_TARGET_FID, report the modified directory
 * fid on dirent events and the child fid 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.
 * FS_ATTRIB reports the child fid even if reported on a watched parent.
 * FS_CREATE reports the modified dir fid without FAN_REPORT_TARGET_FID.
 *       and reports the created child fid with FAN_REPORT_TARGET_FID.
 */
static struct inode *fanotify_fid_inode(u32 event_mask, const void *data,
					int data_type, struct inode *dir)
					int data_type, struct inode *dir,
					unsigned int fid_mode)
{
	if (event_mask & ALL_FSNOTIFY_DIRENT_EVENTS)
	if ((event_mask & ALL_FSNOTIFY_DIRENT_EVENTS) &&
	    !(fid_mode & FAN_REPORT_TARGET_FID))
		return dir;

	return fsnotify_data_inode(data, data_type);
@@ -647,10 +671,11 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
{
	struct fanotify_event *event = NULL;
	gfp_t gfp = GFP_KERNEL_ACCOUNT;
	struct inode *id = fanotify_fid_inode(mask, data, data_type, dir);
	unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
	struct inode *id = fanotify_fid_inode(mask, data, data_type, dir,
					      fid_mode);
	struct inode *dirid = fanotify_dfid_inode(mask, data, data_type, dir);
	const struct path *path = fsnotify_data_path(data, data_type);
	unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
	struct mem_cgroup *old_memcg;
	struct inode *child = NULL;
	bool name_event = false;
@@ -660,11 +685,10 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,

	if ((fid_mode & FAN_REPORT_DIR_FID) && dirid) {
		/*
		 * With both flags FAN_REPORT_DIR_FID and FAN_REPORT_FID, we
		 * report the child fid for events reported on a non-dir child
		 * For certain events and group flags, report the child fid
		 * in addition to reporting the parent fid and maybe child name.
		 */
		if ((fid_mode & FAN_REPORT_FID) && id != dirid && !ondir)
		if (fanotify_report_child_fid(fid_mode, mask) && id != dirid)
			child = id;

		id = dirid;
+10 −1
Original line number Diff line number Diff line
@@ -1275,6 +1275,15 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
	if ((fid_mode & FAN_REPORT_NAME) && !(fid_mode & FAN_REPORT_DIR_FID))
		return -EINVAL;

	/*
	 * FAN_REPORT_TARGET_FID requires FAN_REPORT_NAME and FAN_REPORT_FID
	 * and is used as an indication to report both dir and child fid on all
	 * dirent events.
	 */
	if ((fid_mode & FAN_REPORT_TARGET_FID) &&
	    (!(fid_mode & FAN_REPORT_NAME) || !(fid_mode & FAN_REPORT_FID)))
		return -EINVAL;

	f_flags = O_RDWR | FMODE_NONOTIFY;
	if (flags & FAN_CLOEXEC)
		f_flags |= O_CLOEXEC;
@@ -1667,7 +1676,7 @@ static int __init fanotify_user_setup(void)
				     FANOTIFY_DEFAULT_MAX_USER_MARKS);

	BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS);
	BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 11);
	BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 12);
	BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 9);

	fanotify_mark_cache = KMEM_CACHE(fsnotify_mark,
+1 −1
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@ extern struct ctl_table fanotify_table[]; /* for sysctl */

#define FANOTIFY_CLASS_BITS	(FAN_CLASS_NOTIF | FANOTIFY_PERM_CLASSES)

#define FANOTIFY_FID_BITS	(FAN_REPORT_FID | FAN_REPORT_DFID_NAME)
#define FANOTIFY_FID_BITS	(FAN_REPORT_DFID_NAME_TARGET)

#define FANOTIFY_INFO_MODES	(FANOTIFY_FID_BITS | FAN_REPORT_PIDFD)

+4 −0
Original line number Diff line number Diff line
@@ -57,9 +57,13 @@
#define FAN_REPORT_FID		0x00000200	/* Report unique file id */
#define FAN_REPORT_DIR_FID	0x00000400	/* Report unique directory id */
#define FAN_REPORT_NAME		0x00000800	/* Report events with name */
#define FAN_REPORT_TARGET_FID	0x00001000	/* Report dirent target id  */

/* Convenience macro - FAN_REPORT_NAME requires FAN_REPORT_DIR_FID */
#define FAN_REPORT_DFID_NAME	(FAN_REPORT_DIR_FID | FAN_REPORT_NAME)
/* Convenience macro - FAN_REPORT_TARGET_FID requires all other FID flags */
#define FAN_REPORT_DFID_NAME_TARGET (FAN_REPORT_DFID_NAME | \
				     FAN_REPORT_FID | FAN_REPORT_TARGET_FID)

/* Deprecated - do not use this in programs and do not add new flags here! */
#define FAN_ALL_INIT_FLAGS	(FAN_CLOEXEC | FAN_NONBLOCK | \