}
 
 /*
- * 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);
 {
        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;
 
        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;
 
        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;
                                     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,
 
 #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 | \