fsnotify: report FS_ISDIR flag with MOVE_SELF and DELETE_SELF events
authorAmir Goldstein <amir73il@gmail.com>
Thu, 10 Jan 2019 17:04:40 +0000 (19:04 +0200)
committerJan Kara <jack@suse.cz>
Thu, 7 Feb 2019 15:38:35 +0000 (16:38 +0100)
We need to report FS_ISDIR flag with MOVE_SELF and DELETE_SELF events
for fanotify, because fanotify API requires the user to explicitly
request events on directories by FAN_ONDIR flag.

inotify never reported IN_ISDIR with those events. It looks like an
oversight, but to avoid the risk of breaking existing inotify programs,
mask the FS_ISDIR flag out when reprting those events to inotify backend.

We also add the FS_ISDIR flag with FS_ATTRIB event in the case of rename
over an empty target directory. inotify did not report IN_ISDIR in this
case, but it normally does report IN_ISDIR along with IN_ATTRIB event,
so in this case, we do not mask out the FS_ISDIR flag.

[JK: Simplify the checks in fsnotify_move()]

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>
fs/notify/inotify/inotify_fsnotify.c
include/linux/fsnotify.h

index fe97299..ff30abd 100644 (file)
@@ -113,6 +113,15 @@ int inotify_handle_event(struct fsnotify_group *group,
                return -ENOMEM;
        }
 
+       /*
+        * We now report FS_ISDIR flag with MOVE_SELF and DELETE_SELF events
+        * for fanotify. inotify never reported IN_ISDIR with those events.
+        * It looks like an oversight, but to avoid the risk of breaking
+        * existing inotify programs, mask the flag out from those events.
+        */
+       if (mask & (IN_MOVE_SELF | IN_DELETE_SELF))
+               mask &= ~IN_ISDIR;
+
        fsn_event = &event->fse;
        fsnotify_init_event(fsn_event, inode);
        event->mask = mask;
index 39b22e8..9becae6 100644 (file)
@@ -87,7 +87,12 @@ static inline int fsnotify_perm(struct file *file, int mask)
  */
 static inline void fsnotify_link_count(struct inode *inode)
 {
-       fsnotify(inode, FS_ATTRIB, inode, FSNOTIFY_EVENT_INODE, NULL, 0);
+       __u32 mask = FS_ATTRIB;
+
+       if (S_ISDIR(inode->i_mode))
+               mask |= FS_ISDIR;
+
+       fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0);
 }
 
 /*
@@ -95,12 +100,14 @@ static inline void fsnotify_link_count(struct inode *inode)
  */
 static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
                                 const unsigned char *old_name,
-                                int isdir, struct inode *target, struct dentry *moved)
+                                int isdir, struct inode *target,
+                                struct dentry *moved)
 {
        struct inode *source = moved->d_inode;
        u32 fs_cookie = fsnotify_get_cookie();
        __u32 old_dir_mask = FS_MOVED_FROM;
        __u32 new_dir_mask = FS_MOVED_TO;
+       __u32 mask = FS_MOVE_SELF;
        const unsigned char *new_name = moved->d_name.name;
 
        if (old_dir == new_dir)
@@ -109,6 +116,7 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
        if (isdir) {
                old_dir_mask |= FS_ISDIR;
                new_dir_mask |= FS_ISDIR;
+               mask |= FS_ISDIR;
        }
 
        fsnotify(old_dir, old_dir_mask, source, FSNOTIFY_EVENT_INODE, old_name,
@@ -120,7 +128,7 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
                fsnotify_link_count(target);
 
        if (source)
-               fsnotify(source, FS_MOVE_SELF, moved->d_inode, FSNOTIFY_EVENT_INODE, NULL, 0);
+               fsnotify(source, mask, source, FSNOTIFY_EVENT_INODE, NULL, 0);
        audit_inode_child(new_dir, moved, AUDIT_TYPE_CHILD_CREATE);
 }
 
@@ -178,7 +186,12 @@ static inline void fsnotify_nameremove(struct dentry *dentry, int isdir)
  */
 static inline void fsnotify_inoderemove(struct inode *inode)
 {
-       fsnotify(inode, FS_DELETE_SELF, inode, FSNOTIFY_EVENT_INODE, NULL, 0);
+       __u32 mask = FS_DELETE_SELF;
+
+       if (S_ISDIR(inode->i_mode))
+               mask |= FS_ISDIR;
+
+       fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0);
        __fsnotify_inode_delete(inode);
 }