fanotify: allow users to set an ignored_mask
Eric Paris [Fri, 18 Dec 2009 02:24:33 +0000 (21:24 -0500)]
Change the sys_fanotify_mark() system call so users can set ignored_masks
on inodes.  Remember, if a user new sets a real mask, and only sets ignored
masks, the ignore will never be pinned in memory.  Thus ignored_masks can
be lost under memory pressure and the user may again get events they
previously thought were ignored.

Signed-off-by: Eric Paris <eparis@redhat.com>

fs/notify/fanotify/fanotify_user.c
include/linux/fanotify.h

index 3320f0c..ad02d47 100644 (file)
@@ -296,13 +296,20 @@ out:
        return ret;
 }
 
-static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, __u32 mask)
+static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark,
+                                           __u32 mask,
+                                           unsigned int flags)
 {
        __u32 oldmask;
 
        spin_lock(&fsn_mark->lock);
-       oldmask = fsn_mark->mask;
-       fsnotify_set_mark_mask_locked(fsn_mark, (oldmask & ~mask));
+       if (!(flags & FAN_MARK_IGNORED_MASK)) {
+               oldmask = fsn_mark->mask;
+               fsnotify_set_mark_mask_locked(fsn_mark, (oldmask & ~mask));
+       } else {
+               oldmask = fsn_mark->ignored_mask;
+               fsnotify_set_mark_ignored_mask_locked(fsn_mark, (oldmask & ~mask));
+       }
        spin_unlock(&fsn_mark->lock);
 
        if (!(oldmask & ~mask))
@@ -312,7 +319,8 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, __u3
 }
 
 static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group,
-                                        struct vfsmount *mnt, __u32 mask)
+                                        struct vfsmount *mnt, __u32 mask,
+                                        unsigned int flags)
 {
        struct fsnotify_mark *fsn_mark = NULL;
        __u32 removed;
@@ -321,7 +329,7 @@ static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group,
        if (!fsn_mark)
                return -ENOENT;
 
-       removed = fanotify_mark_remove_from_mask(fsn_mark, mask);
+       removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags);
        fsnotify_put_mark(fsn_mark);
        if (removed & group->mask)
                fsnotify_recalc_group_mask(group);
@@ -332,7 +340,8 @@ static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group,
 }
 
 static int fanotify_remove_inode_mark(struct fsnotify_group *group,
-                                     struct inode *inode, __u32 mask)
+                                     struct inode *inode, __u32 mask,
+                                     unsigned int flags)
 {
        struct fsnotify_mark *fsn_mark = NULL;
        __u32 removed;
@@ -341,7 +350,7 @@ static int fanotify_remove_inode_mark(struct fsnotify_group *group,
        if (!fsn_mark)
                return -ENOENT;
 
-       removed = fanotify_mark_remove_from_mask(fsn_mark, mask);
+       removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags);
        /* matches the fsnotify_find_inode_mark() */
        fsnotify_put_mark(fsn_mark);
 
@@ -353,20 +362,28 @@ static int fanotify_remove_inode_mark(struct fsnotify_group *group,
        return 0;
 }
 
-static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, __u32 mask)
+static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
+                                      __u32 mask,
+                                      unsigned int flags)
 {
        __u32 oldmask;
 
        spin_lock(&fsn_mark->lock);
-       oldmask = fsn_mark->mask;
-       fsnotify_set_mark_mask_locked(fsn_mark, (oldmask | mask));
+       if (!(flags & FAN_MARK_IGNORED_MASK)) {
+               oldmask = fsn_mark->mask;
+               fsnotify_set_mark_mask_locked(fsn_mark, (oldmask | mask));
+       } else {
+               oldmask = fsn_mark->ignored_mask;
+               fsnotify_set_mark_ignored_mask_locked(fsn_mark, (oldmask | mask));
+       }
        spin_unlock(&fsn_mark->lock);
 
        return mask & ~oldmask;
 }
 
 static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
-                                     struct vfsmount *mnt, __u32 mask)
+                                     struct vfsmount *mnt, __u32 mask,
+                                     unsigned int flags)
 {
        struct fsnotify_mark *fsn_mark;
        __u32 added;
@@ -386,7 +403,7 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
                        return ret;
                }
        }
-       added = fanotify_mark_add_to_mask(fsn_mark, mask);
+       added = fanotify_mark_add_to_mask(fsn_mark, mask, flags);
        fsnotify_put_mark(fsn_mark);
        if (added) {
                if (added & ~group->mask)
@@ -398,7 +415,8 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
 }
 
 static int fanotify_add_inode_mark(struct fsnotify_group *group,
-                                  struct inode *inode, __u32 mask)
+                                  struct inode *inode, __u32 mask,
+                                  unsigned int flags)
 {
        struct fsnotify_mark *fsn_mark;
        __u32 added;
@@ -420,7 +438,7 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group,
                        return ret;
                }
        }
-       added = fanotify_mark_add_to_mask(fsn_mark, mask);
+       added = fanotify_mark_add_to_mask(fsn_mark, mask, flags);
        fsnotify_put_mark(fsn_mark);
        if (added) {
                if (added & ~group->mask)
@@ -528,15 +546,15 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags,
        switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) {
        case FAN_MARK_ADD:
                if (flags & FAN_MARK_MOUNT)
-                       ret = fanotify_add_vfsmount_mark(group, mnt, mask);
+                       ret = fanotify_add_vfsmount_mark(group, mnt, mask, flags);
                else
-                       ret = fanotify_add_inode_mark(group, inode, mask);
+                       ret = fanotify_add_inode_mark(group, inode, mask, flags);
                break;
        case FAN_MARK_REMOVE:
                if (flags & FAN_MARK_MOUNT)
-                       ret = fanotify_remove_vfsmount_mark(group, mnt, mask);
+                       ret = fanotify_remove_vfsmount_mark(group, mnt, mask, flags);
                else
-                       ret = fanotify_remove_inode_mark(group, inode, mask);
+                       ret = fanotify_remove_inode_mark(group, inode, mask, flags);
                break;
        default:
                ret = -EINVAL;
index 90e59b2..b8daa9f 100644 (file)
 #define FAN_MARK_DONT_FOLLOW   0x00000004
 #define FAN_MARK_ONLYDIR       0x00000008
 #define FAN_MARK_MOUNT         0x00000010
+#define FAN_MARK_IGNORED_MASK  0x00000020
 
 #define FAN_ALL_MARK_FLAGS     (FAN_MARK_ADD |\
                                 FAN_MARK_REMOVE |\
                                 FAN_MARK_DONT_FOLLOW |\
                                 FAN_MARK_ONLYDIR |\
-                                FAN_MARK_MOUNT)
+                                FAN_MARK_MOUNT |\
+                                FAN_MARK_IGNORED_MASK)
 
 /*
  * All of the events - we build the list by hand so that we can add flags in