vfs: introduce FMODE_NONOTIFY
authorEric Paris <eparis@redhat.com>
Fri, 18 Dec 2009 02:24:25 +0000 (21:24 -0500)
committerEric Paris <eparis@redhat.com>
Wed, 28 Jul 2010 13:58:54 +0000 (09:58 -0400)
This is a new f_mode which can only be set by the kernel.  It indicates
that the fd was opened by fanotify and should not cause future fanotify
events.  This is needed to prevent fanotify livelock.  An example of
obvious livelock is from fanotify close events.

Process A closes file1
This creates a close event for file1.
fanotify opens file1 for Listener X
Listener X deals with the event and closes its fd for file1.
This creates a close event for file1.
fanotify opens file1 for Listener X
Listener X deals with the event and closes its fd for file1.
This creates a close event for file1.
fanotify opens file1 for Listener X
Listener X deals with the event and closes its fd for file1.
notice a pattern?

The fix is to add the FMODE_NONOTIFY bit to the open filp done by the kernel
for fanotify.  Thus when that file is used it will not generate future
events.

This patch simply defines the bit.

Signed-off-by: Eric Paris <eparis@redhat.com>
include/asm-generic/fcntl.h
include/linux/fs.h
include/linux/fsnotify.h

index fcd268ce06744ab616aeb6a5e429c723426906f6..009bd6149d99518f983e6529cc9f6c2b59ab104d 100644 (file)
@@ -3,6 +3,14 @@
 
 #include <linux/types.h>
 
+/*
+ * FMODE_EXEC is 0x20
+ * FMODE_NONOTIFY is 0x800000
+ * These cannot be used by userspace O_* until internal and external open
+ * flags are split.
+ * -Eric Paris
+ */
+
 #define O_ACCMODE      00000003
 #define O_RDONLY       00000000
 #define O_WRONLY       00000001
index 85fe89c43487cfbd9e8c5388837ce659ab7145e7..50ef4d4c95bf0baaac2f74578e5a64acd8b9e460 100644 (file)
@@ -90,6 +90,9 @@ struct inodes_stat_t {
 /* Expect random access pattern */
 #define FMODE_RANDOM           ((__force fmode_t)0x1000)
 
+/* File was opened by fanotify and shouldn't generate fanotify events */
+#define FMODE_NONOTIFY         ((__force fmode_t)8388608)
+
 /*
  * The below are the various read and write types that we support. Some of
  * them include behavioral modifiers that send information down to the
@@ -2508,7 +2511,8 @@ int proc_nr_files(struct ctl_table *table, int write,
 int __init get_filesystem_list(char *buf);
 
 #define ACC_MODE(x) ("\004\002\006\006"[(x)&O_ACCMODE])
-#define OPEN_FMODE(flag) ((__force fmode_t)((flag + 1) & O_ACCMODE))
+#define OPEN_FMODE(flag) ((__force fmode_t)(((flag + 1) & O_ACCMODE) | \
+                                           (flag & FMODE_NONOTIFY)))
 
 #endif /* __KERNEL__ */
 #endif /* _LINUX_FS_H */
index 62e93a9dd115074c9a9b1475b63608cc49a1ebbe..5184a2b786c1e817cf3432354056f6298773f9dd 100644 (file)
@@ -165,8 +165,10 @@ static inline void fsnotify_access(struct file *file)
        if (S_ISDIR(inode->i_mode))
                mask |= FS_IN_ISDIR;
 
-       fsnotify_parent(path, NULL, mask);
-       fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
+       if (!(file->f_mode & FMODE_NONOTIFY)) {
+               fsnotify_parent(path, NULL, mask);
+               fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
+       }
 }
 
 /*
@@ -181,8 +183,10 @@ static inline void fsnotify_modify(struct file *file)
        if (S_ISDIR(inode->i_mode))
                mask |= FS_IN_ISDIR;
 
-       fsnotify_parent(path, NULL, mask);
-       fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
+       if (!(file->f_mode & FMODE_NONOTIFY)) {
+               fsnotify_parent(path, NULL, mask);
+               fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
+       }
 }
 
 /*
@@ -197,8 +201,10 @@ static inline void fsnotify_open(struct file *file)
        if (S_ISDIR(inode->i_mode))
                mask |= FS_IN_ISDIR;
 
-       fsnotify_parent(path, NULL, mask);
-       fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
+       if (!(file->f_mode & FMODE_NONOTIFY)) {
+               fsnotify_parent(path, NULL, mask);
+               fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
+       }
 }
 
 /*
@@ -214,8 +220,10 @@ static inline void fsnotify_close(struct file *file)
        if (S_ISDIR(inode->i_mode))
                mask |= FS_IN_ISDIR;
 
-       fsnotify_parent(path, NULL, mask);
-       fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
+       if (!(file->f_mode & FMODE_NONOTIFY)) {
+               fsnotify_parent(path, NULL, mask);
+               fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
+       }
 }
 
 /*