};
 
 static int
-nfsd_file_fsnotify_handle_event(struct fsnotify_group *group,
-                               struct inode *to_tell,
-                               u32 mask, const void *data, int data_type,
+nfsd_file_fsnotify_handle_event(struct fsnotify_group *group, u32 mask,
+                               const void *data, int data_type,
+                               struct inode *dir,
                                const struct qstr *file_name, u32 cookie,
                                struct fsnotify_iter_info *iter_info)
 {
 
  * destroy the dnotify struct if it was not registered to receive multiple
  * events.
  */
-static int dnotify_handle_event(struct fsnotify_group *group,
-                               struct inode *inode,
-                               u32 mask, const void *data, int data_type,
+static int dnotify_handle_event(struct fsnotify_group *group, u32 mask,
+                               const void *data, int data_type,
+                               struct inode *dir,
                                const struct qstr *file_name, u32 cookie,
                                struct fsnotify_iter_info *iter_info)
 {
        __u32 test_mask = mask & ~FS_EVENT_ON_CHILD;
 
        /* not a dir, dnotify doesn't care */
-       if (!S_ISDIR(inode->i_mode))
+       if (!dir && !(mask & FS_ISDIR))
                return 0;
 
        if (WARN_ON(fsnotify_iter_vfsmount_mark(iter_info)))
 
  * 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)
+static struct inode *fanotify_fid_inode(u32 event_mask, const void *data,
+                                       int data_type, struct inode *dir)
 {
        if (event_mask & ALL_FSNOTIFY_DIRENT_EVENTS)
-               return to_tell;
+               return dir;
 
        return fsnotify_data_inode(data, data_type);
 }
 }
 
 static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
-                                                  struct inode *inode, u32 mask,
-                                                  const void *data, int data_type,
+                                                  u32 mask, const void *data,
+                                                  int data_type, struct inode *dir,
                                                   const struct qstr *file_name,
                                                   __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);
+       struct inode *id = fanotify_fid_inode(mask, data, data_type, dir);
        const struct path *path = fsnotify_data_path(data, data_type);
 
        /*
        return fsid;
 }
 
-static int fanotify_handle_event(struct fsnotify_group *group,
-                                struct inode *inode,
-                                u32 mask, const void *data, int data_type,
+static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
+                                const void *data, int data_type,
+                                struct inode *dir,
                                 const struct qstr *file_name, u32 cookie,
                                 struct fsnotify_iter_info *iter_info)
 {
        if (!mask)
                return 0;
 
-       pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode,
-                mask);
+       pr_debug("%s: group=%p mask=%x\n", __func__, group, mask);
 
        if (fanotify_is_perm_event(mask)) {
                /*
                        return 0;
        }
 
-       event = fanotify_alloc_event(group, inode, mask, data, data_type,
+       event = fanotify_alloc_event(group, mask, data, data_type, dir,
                                     file_name, &fsid);
        ret = -ENOMEM;
        if (unlikely(!event)) {
 
 }
 EXPORT_SYMBOL_GPL(__fsnotify_parent);
 
-static int send_to_group(struct inode *to_tell,
-                        __u32 mask, const void *data,
-                        int data_is, u32 cookie,
-                        const struct qstr *file_name,
-                        struct fsnotify_iter_info *iter_info)
+static int send_to_group(__u32 mask, const void *data, int data_type,
+                        struct inode *dir, const struct qstr *file_name,
+                        u32 cookie, struct fsnotify_iter_info *iter_info)
 {
        struct fsnotify_group *group = NULL;
        __u32 test_mask = (mask & ALL_FSNOTIFY_EVENTS);
                }
        }
 
-       pr_debug("%s: group=%p to_tell=%p mask=%x marks_mask=%x marks_ignored_mask=%x"
-                " data=%p data_is=%d cookie=%d\n",
-                __func__, group, to_tell, mask, marks_mask, marks_ignored_mask,
-                data, data_is, cookie);
+       pr_debug("%s: group=%p mask=%x marks_mask=%x marks_ignored_mask=%x data=%p data_type=%d dir=%p cookie=%d\n",
+                __func__, group, mask, marks_mask, marks_ignored_mask,
+                data, data_type, dir, cookie);
 
        if (!(test_mask & marks_mask & ~marks_ignored_mask))
                return 0;
 
-       return group->ops->handle_event(group, to_tell, mask, data, data_is,
+       return group->ops->handle_event(group, mask, data, data_type, dir,
                                        file_name, cookie, iter_info);
 }
 
  * out to all of the registered fsnotify_group.  Those groups can then use the
  * notification event in whatever means they feel necessary.
  */
-int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_is,
+int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_type,
             const struct qstr *file_name, u32 cookie)
 {
-       const struct path *path = fsnotify_data_path(data, data_is);
+       const struct path *path = fsnotify_data_path(data, data_type);
        struct fsnotify_iter_info iter_info = {};
        struct super_block *sb = to_tell->i_sb;
+       struct inode *dir = file_name ? to_tell : NULL;
        struct mount *mnt = NULL;
        int ret = 0;
        __u32 test_mask, marks_mask;
         * That's why this traversal is so complicated...
         */
        while (fsnotify_iter_select_report_types(&iter_info)) {
-               ret = send_to_group(to_tell, mask, data, data_is, cookie,
-                                   file_name, &iter_info);
+               ret = send_to_group(mask, data, data_type, dir, file_name,
+                                   cookie, &iter_info);
 
                if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS))
                        goto out;
 
 
 extern void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark,
                                           struct fsnotify_group *group);
-extern int inotify_handle_event(struct fsnotify_group *group,
-                               struct inode *inode,
-                               u32 mask, const void *data, int data_type,
+extern int inotify_handle_event(struct fsnotify_group *group, u32 mask,
+                               const void *data, int data_type,
+                               struct inode *dir,
                                const struct qstr *file_name, u32 cookie,
                                struct fsnotify_iter_info *iter_info);
 
 
        return event_compare(last_event, event);
 }
 
-int inotify_handle_event(struct fsnotify_group *group,
-                        struct inode *inode,
-                        u32 mask, const void *data, int data_type,
+int inotify_handle_event(struct fsnotify_group *group, u32 mask,
+                        const void *data, int data_type, struct inode *dir,
                         const struct qstr *file_name, u32 cookie,
                         struct fsnotify_iter_info *iter_info)
 {
                alloc_len += len + 1;
        }
 
-       pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode,
+       pr_debug("%s: group=%p mark=%p mask=%x\n", __func__, group, inode_mark,
                 mask);
 
        i_mark = container_of(inode_mark, struct inotify_inode_mark,
 
                                           fsn_mark);
 
        /* Queue ignore event for the watch */
-       inotify_handle_event(group, NULL, FS_IN_IGNORED, NULL,
-                            FSNOTIFY_EVENT_NONE, NULL, 0, &iter_info);
+       inotify_handle_event(group, FS_IN_IGNORED, NULL, FSNOTIFY_EVENT_NONE,
+                            NULL, NULL, 0, &iter_info);
 
        i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
        /* remove this mark from the idr */
 
  * these operations for each relevant group.
  *
  * handle_event - main call for a group to handle an fs event
+ * @group:     group to notify
+ * @mask:      event type and flags
+ * @data:      object that event happened on
+ * @data_type: type of object for fanotify_data_XXX() accessors
+ * @dir:       optional directory associated with event -
+ *             if @file_name is not NULL, this is the directory that
+ *             @file_name is relative to
+ * @file_name: optional file name associated with event
+ * @cookie:    inotify rename cookie
+ * @iter_info: array of marks from this group that are interested in the event
+ *
  * free_group_priv - called when a group refcnt hits 0 to clean up the private union
  * freeing_mark - called when a mark is being destroyed for some reason.  The group
  *             MUST be holding a reference on each mark and that reference must be
  *             userspace messages that marks have been removed.
  */
 struct fsnotify_ops {
-       int (*handle_event)(struct fsnotify_group *group,
-                           struct inode *inode,
-                           u32 mask, const void *data, int data_type,
+       int (*handle_event)(struct fsnotify_group *group, u32 mask,
+                           const void *data, int data_type, struct inode *dir,
                            const struct qstr *file_name, u32 cookie,
                            struct fsnotify_iter_info *iter_info);
        void (*free_group_priv)(struct fsnotify_group *group);
 
 }
 
 /* Update mark data in audit rules based on fsnotify events. */
-static int audit_mark_handle_event(struct fsnotify_group *group,
-                                   struct inode *to_tell,
-                                   u32 mask, const void *data, int data_type,
-                                   const struct qstr *dname, u32 cookie,
-                                   struct fsnotify_iter_info *iter_info)
+static int audit_mark_handle_event(struct fsnotify_group *group, u32 mask,
+                                  const void *data, int data_type,
+                                  struct inode *dir,
+                                  const struct qstr *dname, u32 cookie,
+                                  struct fsnotify_iter_info *iter_info)
 {
        struct fsnotify_mark *inode_mark = fsnotify_iter_inode_mark(iter_info);
        struct audit_fsnotify_mark *audit_mark;
 
                audit_schedule_prune();
 }
 
-static int audit_tree_handle_event(struct fsnotify_group *group,
-                                  struct inode *to_tell,
-                                  u32 mask, const void *data, int data_type,
+static int audit_tree_handle_event(struct fsnotify_group *group, u32 mask,
+                                  const void *data, int data_type,
+                                  struct inode *dir,
                                   const struct qstr *file_name, u32 cookie,
                                   struct fsnotify_iter_info *iter_info)
 {
 
 }
 
 /* Update watch data in audit rules based on fsnotify events. */
-static int audit_watch_handle_event(struct fsnotify_group *group,
-                                   struct inode *to_tell,
-                                   u32 mask, const void *data, int data_type,
+static int audit_watch_handle_event(struct fsnotify_group *group, u32 mask,
+                                   const void *data, int data_type,
+                                   struct inode *dir,
                                    const struct qstr *dname, u32 cookie,
                                    struct fsnotify_iter_info *iter_info)
 {