@@ -339,6 +339,13 @@ static inline int fanotify_event_dir_fh_len(struct fanotify_event *event)
return info ? fanotify_info_dir_fh_len(info) : 0;
}
+static inline int fanotify_event_dir2_fh_len(struct fanotify_event *event)
+{
+ struct fanotify_info *info = fanotify_event_info(event);
+
+ return info ? fanotify_info_dir2_fh_len(info) : 0;
+}
+
static inline bool fanotify_event_has_object_fh(struct fanotify_event *event)
{
/* For error events, even zeroed fh are reported. */
@@ -352,6 +359,16 @@ static inline bool fanotify_event_has_dir_fh(struct fanotify_event *event)
return fanotify_event_dir_fh_len(event) > 0;
}
+/* For MOVED_FROM event with FAN_REPORT_TARGET_FID */
+static inline bool fanotify_event_has_two_names(struct fanotify_event *event)
+{
+ struct fanotify_info *info = fanotify_event_info(event);
+
+ return info && info->name_len && info->name2_len &&
+ fanotify_info_dir_fh_len(info) > 0 &&
+ fanotify_info_dir2_fh_len(info) > 0;
+}
+
struct fanotify_path_event {
struct fanotify_event fae;
struct path path;
@@ -134,7 +134,7 @@ static size_t fanotify_event_len(unsigned int info_mode,
{
size_t event_len = FAN_EVENT_METADATA_LEN;
struct fanotify_info *info;
- int dir_fh_len;
+ int dir_fh_len, dir2_fh_len;
int fh_len;
int dot_len = 0;
@@ -149,6 +149,11 @@ static size_t fanotify_event_len(unsigned int info_mode,
if (fanotify_event_has_dir_fh(event)) {
dir_fh_len = fanotify_event_dir_fh_len(event);
event_len += fanotify_fid_info_len(dir_fh_len, info->name_len);
+ if (fanotify_event_has_two_names(event)) {
+ dir2_fh_len = fanotify_event_dir2_fh_len(event);
+ event_len += fanotify_fid_info_len(dir2_fh_len,
+ info->name2_len);
+ }
} else if ((info_mode & FAN_REPORT_NAME) &&
(event->mask & FAN_ONDIR)) {
/*
@@ -379,6 +384,7 @@ static int copy_fid_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
return -EFAULT;
break;
case FAN_EVENT_INFO_TYPE_DFID_NAME:
+ case FAN_EVENT_INFO_TYPE_DFID_NAME2:
if (WARN_ON_ONCE(!name || !name_len))
return -EFAULT;
break;
@@ -478,7 +484,10 @@ static int copy_info_records_to_user(struct fanotify_event *event,
unsigned int pidfd_mode = info_mode & FAN_REPORT_PIDFD;
/*
- * Event info records order is as follows: dir fid + name, child fid.
+ * Event info records order is as follows:
+ * 1. dir fid + name
+ * 2. (optional) new dir fid + new name
+ * 3. (optional) child fid
*/
if (fanotify_event_has_dir_fh(event)) {
info_type = info->name_len ? FAN_EVENT_INFO_TYPE_DFID_NAME :
@@ -496,6 +505,22 @@ static int copy_info_records_to_user(struct fanotify_event *event,
total_bytes += ret;
}
+ /* New dir fid + name can only be reported after old dir fid + name */
+ if (info_type && fanotify_event_has_two_names(event)) {
+ info_type = FAN_EVENT_INFO_TYPE_DFID_NAME2;
+ ret = copy_fid_info_to_user(fanotify_event_fsid(event),
+ fanotify_info_dir2_fh(info),
+ info_type,
+ fanotify_info_name2(info),
+ info->name2_len, buf, count);
+ if (ret < 0)
+ return ret;
+
+ buf += ret;
+ count -= ret;
+ total_bytes += ret;
+ }
+
if (fanotify_event_has_object_fh(event)) {
const char *dot = NULL;
int dot_len = 0;
@@ -131,6 +131,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_DFID_NAME2 6 /* For FAN_MOVED_FROM */
/* Variable length info record following event metadata */
struct fanotify_event_info_header {
In the special case of MOVED_FROM event, if we are reporting the child fid due to FAN_REPORT_TARGET_FID init flag, we also report the new parent and name. The new parent and name are reported using a new info record of type FAN_EVENT_INFO_TYPE_DFID_NAME2 that follows the info record of type FAN_EVENT_INFO_TYPE_DFID_NAME with the old parent and name. Signed-off-by: Amir Goldstein <amir73il@gmail.com> --- fs/notify/fanotify/fanotify.h | 17 +++++++++++++++++ fs/notify/fanotify/fanotify_user.c | 29 +++++++++++++++++++++++++++-- include/uapi/linux/fanotify.h | 1 + 3 files changed, 45 insertions(+), 2 deletions(-)