[RFC][PATCH 2/2] fanotify: report mntid info record with FAN_UNMOUNT events

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Report mntid in an info record of type FAN_EVENT_INFO_TYPE_MNTID
with FAN_UNMOUNT event in addition to the fid info record.

Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx>
---
 fs/notify/fanotify/fanotify.c      | 37 +++++++++++++++++++------
 fs/notify/fanotify/fanotify.h      | 20 +++++++++++++-
 fs/notify/fanotify/fanotify_user.c | 44 +++++++++++++++++++++++++++---
 include/uapi/linux/fanotify.h      | 10 +++++++
 4 files changed, 97 insertions(+), 14 deletions(-)

diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index 384d2b2e55e7..c204259be6cc 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -17,6 +17,7 @@
 #include <linux/stringhash.h>
 
 #include "fanotify.h"
+#include "../../mount.h" /* for mnt_id */
 
 static bool fanotify_path_equal(const struct path *p1, const struct path *p2)
 {
@@ -41,6 +42,11 @@ static unsigned int fanotify_hash_fsid(__kernel_fsid_t *fsid)
 		hash_32(fsid->val[1], FANOTIFY_EVENT_HASH_BITS);
 }
 
+static unsigned int fanotify_hash_mntid(int mntid)
+{
+	return hash_32(mntid, FANOTIFY_EVENT_HASH_BITS);
+}
+
 static bool fanotify_fh_equal(struct fanotify_fh *fh1,
 			      struct fanotify_fh *fh2)
 {
@@ -133,6 +139,12 @@ static bool fanotify_error_event_equal(struct fanotify_error_event *fee1,
 	return true;
 }
 
+/*
+ * FAN_RENAME and FAN_UNMOUNT are reported with special info record types,
+ * so we cannot merge them with other events.
+ */
+#define FANOTIFY_NO_MERGE_EVENTS (FAN_RENAME | FAN_UNMOUNT)
+
 static bool fanotify_should_merge(struct fanotify_event *old,
 				  struct fanotify_event *new)
 {
@@ -153,11 +165,8 @@ static bool fanotify_should_merge(struct fanotify_event *old,
 	if ((old->mask & FS_ISDIR) != (new->mask & FS_ISDIR))
 		return false;
 
-	/*
-	 * FAN_RENAME event is reported with special info record types,
-	 * so we cannot merge it with other events.
-	 */
-	if ((old->mask & FAN_RENAME) != (new->mask & FAN_RENAME))
+	if ((old->mask & FANOTIFY_NO_MERGE_EVENTS) ||
+	    (new->mask & FANOTIFY_NO_MERGE_EVENTS))
 		return false;
 
 	switch (old->type) {
@@ -593,9 +602,11 @@ static struct fanotify_event *fanotify_alloc_perm_event(const struct path *path,
 
 static struct fanotify_event *fanotify_alloc_fid_event(struct inode *id,
 						       __kernel_fsid_t *fsid,
+						       struct mount *mnt,
 						       unsigned int *hash,
 						       gfp_t gfp)
 {
+	unsigned int fh_len = fanotify_encode_fh_len(id);
 	struct fanotify_fid_event *ffe;
 
 	ffe = kmem_cache_alloc(fanotify_fid_event_cachep, gfp);
@@ -605,8 +616,14 @@ static struct fanotify_event *fanotify_alloc_fid_event(struct inode *id,
 	ffe->fae.type = FANOTIFY_EVENT_TYPE_FID;
 	ffe->fsid = *fsid;
 	*hash ^= fanotify_hash_fsid(fsid);
-	fanotify_encode_fh(&ffe->object_fh, id, fanotify_encode_fh_len(id),
-			   hash, gfp);
+	fanotify_encode_fh(&ffe->object_fh, id, fh_len, hash, gfp);
+	/* Record fid event with fsid, mntid and empty fh */
+	if (mnt && !WARN_ON_ONCE(fh_len)) {
+		ffe->mnt_id = mnt->mnt_id;
+		ffe->object_fh.flags = FANOTIFY_FH_FLAG_MNT_ID;
+		if (hash)
+			*hash ^= fanotify_hash_mntid(mnt->mnt_id);
+	}
 
 	return &ffe->fae;
 }
@@ -737,6 +754,7 @@ static struct fanotify_event *fanotify_alloc_event(
 					      fid_mode);
 	struct inode *dirid = fanotify_dfid_inode(mask, data, data_type, dir);
 	const struct path *path = fsnotify_data_path(data, data_type);
+	struct mount *mnt = NULL;
 	struct mem_cgroup *old_memcg;
 	struct dentry *moved = NULL;
 	struct inode *child = NULL;
@@ -746,7 +764,8 @@ static struct fanotify_event *fanotify_alloc_event(
 	struct pid *pid;
 
 	if (mask & FAN_UNMOUNT && !WARN_ON_ONCE(!path || !fid_mode)) {
-		/* Record fid event with fsid and empty fh */
+		/* Record fid event with fsid, mntid and empty fh */
+		mnt = real_mount(path->mnt);
 		id = NULL;
 	} else if ((fid_mode & FAN_REPORT_DIR_FID) && dirid) {
 		/*
@@ -834,7 +853,7 @@ static struct fanotify_event *fanotify_alloc_event(
 		event = fanotify_alloc_name_event(dirid, fsid, file_name, child,
 						  moved, &hash, gfp);
 	} else if (fid_mode) {
-		event = fanotify_alloc_fid_event(id, fsid, &hash, gfp);
+		event = fanotify_alloc_fid_event(id, fsid, mnt, &hash, gfp);
 	} else {
 		event = fanotify_alloc_path_event(path, &hash, gfp);
 	}
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index f98dcf5b7a19..3d8391a77031 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -33,6 +33,7 @@ struct fanotify_fh {
 	u8 type;
 	u8 len;
 #define FANOTIFY_FH_FLAG_EXT_BUF 1
+#define FANOTIFY_FH_FLAG_MNT_ID  2
 	u8 flags;
 	u8 pad;
 	unsigned char buf[];
@@ -279,7 +280,10 @@ static inline void fanotify_init_event(struct fanotify_event *event,
 struct {								\
 	struct fanotify_fh (name);					\
 	/* Space for object_fh.buf[] - access with fanotify_fh_buf() */	\
-	unsigned char _inline_fh_buf[(size)];				\
+	union {								\
+		unsigned char _inline_fh_buf[(size)];			\
+		int mnt_id;	/* For FAN_UNMOUNT */			\
+	};								\
 }
 
 struct fanotify_fid_event {
@@ -335,6 +339,20 @@ static inline __kernel_fsid_t *fanotify_event_fsid(struct fanotify_event *event)
 		return NULL;
 }
 
+static inline int fanotify_event_mntid(struct fanotify_event *event)
+{
+	struct fanotify_fh *fh = NULL;
+
+	if (event->mask & FAN_UNMOUNT &&
+	    event->type == FANOTIFY_EVENT_TYPE_FID)
+		fh = &FANOTIFY_FE(event)->object_fh;
+
+	if (fh && !fh->len && fh->flags == FANOTIFY_FH_FLAG_MNT_ID)
+		return FANOTIFY_FE(event)->mnt_id;
+
+	return 0;
+}
+
 static inline struct fanotify_fh *fanotify_event_object_fh(
 						struct fanotify_event *event)
 {
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 0b3de6218c56..db3b79b8e901 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -120,7 +120,9 @@ struct kmem_cache *fanotify_perm_event_cachep __read_mostly;
 #define FANOTIFY_EVENT_ALIGN 4
 #define FANOTIFY_FID_INFO_HDR_LEN \
 	(sizeof(struct fanotify_event_info_fid) + sizeof(struct file_handle))
-#define FANOTIFY_PIDFD_INFO_HDR_LEN \
+#define FANOTIFY_MNTID_INFO_LEN \
+	sizeof(struct fanotify_event_info_mntid)
+#define FANOTIFY_PIDFD_INFO_LEN \
 	sizeof(struct fanotify_event_info_pidfd)
 #define FANOTIFY_ERROR_INFO_LEN \
 	(sizeof(struct fanotify_event_info_error))
@@ -178,8 +180,11 @@ static size_t fanotify_event_len(unsigned int info_mode,
 		dot_len = 1;
 	}
 
+	if (fanotify_event_mntid(event))
+		event_len += FANOTIFY_MNTID_INFO_LEN;
+
 	if (info_mode & FAN_REPORT_PIDFD)
-		event_len += FANOTIFY_PIDFD_INFO_HDR_LEN;
+		event_len += FANOTIFY_PIDFD_INFO_LEN;
 
 	if (fanotify_event_has_object_fh(event)) {
 		fh_len = fanotify_event_object_fh_len(event);
@@ -515,7 +520,7 @@ static int copy_pidfd_info_to_user(int pidfd,
 				   size_t count)
 {
 	struct fanotify_event_info_pidfd info = { };
-	size_t info_len = FANOTIFY_PIDFD_INFO_HDR_LEN;
+	size_t info_len = FANOTIFY_PIDFD_INFO_LEN;
 
 	if (WARN_ON_ONCE(info_len > count))
 		return -EFAULT;
@@ -530,6 +535,26 @@ static int copy_pidfd_info_to_user(int pidfd,
 	return info_len;
 }
 
+static int copy_mntid_info_to_user(int mntid,
+				   char __user *buf,
+				   size_t count)
+{
+	struct fanotify_event_info_mntid info = { };
+	size_t info_len = FANOTIFY_MNTID_INFO_LEN;
+
+	if (WARN_ON_ONCE(info_len > count))
+		return -EFAULT;
+
+	info.hdr.info_type = FAN_EVENT_INFO_TYPE_MNTID;
+	info.hdr.len = info_len;
+	info.mnt_id = mntid;
+
+	if (copy_to_user(buf, &info, info_len))
+		return -EFAULT;
+
+	return info_len;
+}
+
 static int copy_info_records_to_user(struct fanotify_event *event,
 				     struct fanotify_info *info,
 				     unsigned int info_mode, int pidfd,
@@ -538,6 +563,7 @@ static int copy_info_records_to_user(struct fanotify_event *event,
 	int ret, total_bytes = 0, info_type = 0;
 	unsigned int fid_mode = info_mode & FANOTIFY_FID_BITS;
 	unsigned int pidfd_mode = info_mode & FAN_REPORT_PIDFD;
+	int mntid = fanotify_event_mntid(event);
 
 	/*
 	 * Event info records order is as follows:
@@ -632,6 +658,16 @@ static int copy_info_records_to_user(struct fanotify_event *event,
 		total_bytes += ret;
 	}
 
+	if (mntid) {
+		ret = copy_mntid_info_to_user(mntid, buf, count);
+		if (ret < 0)
+			return ret;
+
+		buf += ret;
+		count -= ret;
+		total_bytes += ret;
+	}
+
 	if (pidfd_mode) {
 		ret = copy_pidfd_info_to_user(pidfd, buf, count);
 		if (ret < 0)
@@ -1770,7 +1806,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
 	 * inotify sends unsoliciled IN_UNMOUNT per marked inode on sb shutdown.
 	 * FAN_UNMOUNT event is about unmount of a mount, not about sb shutdown,
 	 * so allow setting it only in mount mark mask.
-	 * FAN_UNMOUNT requires FAN_REPORT_FID to report fsid with empty fh.
+	 * FAN_UNMOUNT requires FAN_REPORT_FID to report fsid and mntid.
 	 */
 	if (mask & FAN_UNMOUNT &&
 	    (!(fid_mode & FAN_REPORT_FID) || mark_type != FAN_MARK_MOUNT))
diff --git a/include/uapi/linux/fanotify.h b/include/uapi/linux/fanotify.h
index 70f2d43e8ba4..886efbd877ba 100644
--- a/include/uapi/linux/fanotify.h
+++ b/include/uapi/linux/fanotify.h
@@ -144,6 +144,7 @@ struct fanotify_event_metadata {
 #define FAN_EVENT_INFO_TYPE_DFID	3
 #define FAN_EVENT_INFO_TYPE_PIDFD	4
 #define FAN_EVENT_INFO_TYPE_ERROR	5
+#define FAN_EVENT_INFO_TYPE_MNTID	6
 
 /* Special info types for FAN_RENAME */
 #define FAN_EVENT_INFO_TYPE_OLD_DFID_NAME	10
@@ -184,6 +185,15 @@ struct fanotify_fhandle {
 	__u64 fh_ino;
 };
 
+/*
+ * This structure is used for info records of type FAN_EVENT_INFO_TYPE_MNTID.
+ */
+struct fanotify_event_info_mntid {
+	struct fanotify_event_info_header hdr;
+	/* matches mount_id from name_to_handle_at(2) */
+	__s32 mnt_id;
+};
+
 /*
  * This structure is used for info records of type FAN_EVENT_INFO_TYPE_PIDFD.
  * It holds a pidfd for the pid that was responsible for generating an event.
-- 
2.34.1




[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux