inotify: Add flag IN_MASK_CREATE for inotify_add_watch()
authorHenry Wilson <henry.wilson@acentic.com>
Thu, 31 May 2018 09:43:03 +0000 (09:43 +0000)
committerJan Kara <jack@suse.cz>
Wed, 27 Jun 2018 17:21:25 +0000 (19:21 +0200)
The flag IN_MASK_CREATE is introduced as a flag for inotiy_add_watch()
which prevents inotify from modifying any existing watches when invoked.
If the pathname specified in the call has a watched inode associated
with it and IN_MASK_CREATE is specified, fail with an errno of EEXIST.

Use of IN_MASK_CREATE with IN_MASK_ADD is reserved for future use and
will return EINVAL.

RATIONALE

In the current implementation, there is no way to prevent
inotify_add_watch() from modifying existing watch descriptors. Even if
the caller keeps a record of all watch descriptors collected, this is
only sufficient to detect that an existing watch descriptor may have
been modified.

The assumption that a particular path will map to the same inode over
multiple calls to inotify_add_watch() cannot be made as files can be
renamed or deleted.  It is also not possible to assume that two distinct
paths do no map to the same inode, due to hard-links or a dereferenced
symbolic link. Further uses of inotify_add_watch() to revert the change
may cause other watch descriptors to be modified or created, merely
compunding the problem. There is currently no system call such as
inotify_modify_watch() to explicity modify a watch descriptor, which
would be able to revert unwanted changes. Thus the caller cannot
guarantee to be able to revert any changes to existing watch decriptors.

Additionally the caller cannot assume that the events that are
associated with a watch descriptor are within the set requested, as any
future calls to inotify_add_watch() may unintentionally modify a watch
descriptor's mask. Thus it cannot currently be guaranteed that a watch
descriptor will only generate events which have been requested. The
program must filter events which come through its watch descriptor to
within its expected range.

Reviewed-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Henry Wilson <henry.wilson@acentic.com>
Signed-off-by: Jan Kara <jack@suse.cz>
fs/notify/inotify/inotify_user.c
include/linux/inotify.h
include/uapi/linux/inotify.h

index 1cf5b779d862dc81f9b00454d06babd8fc3acb1f..6f48d325c35055b985946ac2fac51ad13005ea9e 100644 (file)
@@ -510,6 +510,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group,
        __u32 old_mask, new_mask;
        __u32 mask;
        int add = (arg & IN_MASK_ADD);
+       int create = (arg & IN_MASK_CREATE);
        int ret;
 
        mask = inotify_arg_to_mask(arg);
@@ -517,6 +518,8 @@ static int inotify_update_existing_watch(struct fsnotify_group *group,
        fsn_mark = fsnotify_find_mark(&inode->i_fsnotify_marks, group);
        if (!fsn_mark)
                return -ENOENT;
+       else if (create)
+               return -EEXIST;
 
        i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
 
@@ -718,6 +721,10 @@ SYSCALL_DEFINE3(inotify_add_watch, int, fd, const char __user *, pathname,
        if (unlikely(!f.file))
                return -EBADF;
 
+       /* IN_MASK_ADD and IN_MASK_CREATE don't make sense together */
+       if (unlikely((mask & IN_MASK_ADD) && (mask & IN_MASK_CREATE)))
+               return -EINVAL;
+
        /* verify that this is indeed an inotify instance */
        if (unlikely(f.file->f_op != &inotify_fops)) {
                ret = -EINVAL;
@@ -806,7 +813,7 @@ static int __init inotify_user_setup(void)
        BUILD_BUG_ON(IN_ISDIR != FS_ISDIR);
        BUILD_BUG_ON(IN_ONESHOT != FS_IN_ONESHOT);
 
-       BUG_ON(hweight32(ALL_INOTIFY_BITS) != 21);
+       BUG_ON(hweight32(ALL_INOTIFY_BITS) != 22);
 
        inotify_inode_mark_cachep = KMEM_CACHE(inotify_inode_mark, SLAB_PANIC);
 
index 44f9ffe72c874ed0a4a8098b2df5240e9ae90114..6a24905f6e1e2964ecea4487e3140913d4bccc57 100644 (file)
@@ -18,6 +18,6 @@ extern struct ctl_table inotify_table[]; /* for sysctl */
                          IN_DELETE_SELF | IN_MOVE_SELF | IN_UNMOUNT | \
                          IN_Q_OVERFLOW | IN_IGNORED | IN_ONLYDIR | \
                          IN_DONT_FOLLOW | IN_EXCL_UNLINK | IN_MASK_ADD | \
-                         IN_ISDIR | IN_ONESHOT)
+                         IN_MASK_CREATE | IN_ISDIR | IN_ONESHOT)
 
 #endif /* _LINUX_INOTIFY_H */
index 4800bf2a531d6bb6d947d698119d334e7e01855e..884b4846b6308ee9bd6a92b16e7156f295546075 100644 (file)
@@ -53,6 +53,7 @@ struct inotify_event {
 #define IN_ONLYDIR             0x01000000      /* only watch the path if it is a directory */
 #define IN_DONT_FOLLOW         0x02000000      /* don't follow a sym link */
 #define IN_EXCL_UNLINK         0x04000000      /* exclude events on unlinked objects */
+#define IN_MASK_CREATE         0x10000000      /* only create watches */
 #define IN_MASK_ADD            0x20000000      /* add to the mask of an already existing watch */
 #define IN_ISDIR               0x40000000      /* event occurred against dir */
 #define IN_ONESHOT             0x80000000      /* only send event once */