#include <linux/spinlock.h>
#include <linux/types.h>
-#include <asm/atomic.h>
+#include <linux/atomic.h>
/*
* IN_* from inotfy.h lines up EXACTLY with FS_*, this is so we can easily
#define FS_ACCESS_PERM 0x00020000 /* access event in a permissions hook */
#define FS_EXCL_UNLINK 0x04000000 /* do not send events if object is unlinked */
-#define FS_IN_ISDIR 0x40000000 /* event occurred against dir */
+#define FS_ISDIR 0x40000000 /* event occurred against dir */
#define FS_IN_ONESHOT 0x80000000 /* only send event once */
#define FS_DN_RENAME 0x10000000 /* file renamed */
#define FS_MOVE (FS_MOVED_FROM | FS_MOVED_TO)
+#define ALL_FSNOTIFY_PERM_EVENTS (FS_OPEN_PERM | FS_ACCESS_PERM)
+
+#define ALL_FSNOTIFY_EVENTS (FS_ACCESS | FS_MODIFY | FS_ATTRIB | \
+ FS_CLOSE_WRITE | FS_CLOSE_NOWRITE | FS_OPEN | \
+ FS_MOVED_FROM | FS_MOVED_TO | FS_CREATE | \
+ FS_DELETE | FS_DELETE_SELF | FS_MOVE_SELF | \
+ FS_UNMOUNT | FS_Q_OVERFLOW | FS_IN_IGNORED | \
+ FS_OPEN_PERM | FS_ACCESS_PERM | FS_EXCL_UNLINK | \
+ FS_ISDIR | FS_IN_ONESHOT | FS_DN_RENAME | \
+ FS_DN_MULTISHOT | FS_EVENT_ON_CHILD)
+
struct fsnotify_group;
struct fsnotify_event;
struct fsnotify_mark;
*/
struct fsnotify_ops {
bool (*should_send_event)(struct fsnotify_group *group, struct inode *inode,
- struct vfsmount *mnt, __u32 mask, void *data,
- int data_type);
- int (*handle_event)(struct fsnotify_group *group, struct fsnotify_event *event);
+ struct fsnotify_mark *inode_mark,
+ struct fsnotify_mark *vfsmount_mark,
+ __u32 mask, void *data, int data_type);
+ int (*handle_event)(struct fsnotify_group *group,
+ struct fsnotify_mark *inode_mark,
+ struct fsnotify_mark *vfsmount_mark,
+ struct fsnotify_event *event);
void (*free_group_priv)(struct fsnotify_group *group);
void (*freeing_mark)(struct fsnotify_mark *mark, struct fsnotify_group *group);
void (*free_event_priv)(struct fsnotify_event_private_data *priv);
* everything will be cleaned up.
*/
struct fsnotify_group {
- /*
- * global list of all groups receiving events from fsnotify.
- * anchored by fsnotify_inode_groups and protected by either fsnotify_grp_mutex
- * or fsnotify_grp_srcu depending on write vs read.
- */
- struct list_head inode_group_list;
- /*
- * same as above except anchored by fsnotify_vfsmount_groups
- */
- struct list_head vfsmount_group_list;
-
- /*
- * Defines all of the event types in which this group is interested.
- * This mask is a bitwise OR of the FS_* events from above. Each time
- * this mask changes for a group (if it changes) the correct functions
- * must be called to update the global structures which indicate global
- * interest in event types.
- */
- __u32 mask;
-
/*
* How the refcnt is used is up to each group. When the refcnt hits 0
* fsnotify will clean up all of the resources associated with this group.
wait_queue_head_t notification_waitq; /* read() on the notification file blocks on this waitq */
unsigned int q_len; /* events on the queue */
unsigned int max_events; /* maximum events allowed on the list */
+ /*
+ * Valid fsnotify group priorities. Events are send in order from highest
+ * priority to lowest priority. We default to the lowest priority.
+ */
+ #define FS_PRIO_0 0 /* normal notifiers, no permissions */
+ #define FS_PRIO_1 1 /* fanotify content based access control */
+ #define FS_PRIO_2 2 /* fanotify pre-content access */
+ unsigned int priority;
/* stores all fastpath marks assoc with this group so they can be cleaned on unregister */
spinlock_t mark_lock; /* protect marks_list */
* a group */
struct list_head marks_list; /* all inode marks for this group */
- /* prevents double list_del of group_list. protected by global fsnotify_grp_mutex */
- bool on_inode_group_list;
- bool on_vfsmount_group_list;
-
/* groups can define private fields here or use the void *private */
union {
void *private;
struct user_struct *user;
} inotify_data;
#endif
-#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
+#ifdef CONFIG_FANOTIFY
struct fanotify_group_private_data {
+#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
/* allows a group to block waiting for a userspace response */
struct mutex access_mutex;
struct list_head access_list;
wait_queue_head_t access_waitq;
+ atomic_t bypass_perm;
+#endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */
+ int f_flags;
+ unsigned int max_marks;
+ struct user_struct *user;
} fanotify_data;
-#endif
+#endif /* CONFIG_FANOTIFY */
};
};
struct fsnotify_inode_mark i;
struct fsnotify_vfsmount_mark m;
};
- __u32 ignored_mask; /* events types to ignore */
struct list_head free_g_list; /* tmp list used when freeing this mark */
+ __u32 ignored_mask; /* events types to ignore */
#define FSNOTIFY_MARK_FLAG_INODE 0x01
#define FSNOTIFY_MARK_FLAG_VFSMOUNT 0x02
#define FSNOTIFY_MARK_FLAG_OBJECT_PINNED 0x04
#define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY 0x08
+#define FSNOTIFY_MARK_FLAG_ALIVE 0x10
unsigned int flags; /* vfsmount or inode mark? */
+ struct list_head destroy_list;
void (*free_mark)(struct fsnotify_mark *mark); /* called on final put+free */
};
/* main fsnotify call to send events */
extern int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is,
const unsigned char *name, u32 cookie);
-extern void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask);
+extern int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask);
extern void __fsnotify_inode_delete(struct inode *inode);
extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt);
extern u32 fsnotify_get_cookie(void);
{
struct dentry *parent;
- assert_spin_locked(&dcache_lock);
assert_spin_locked(&dentry->d_lock);
+ /*
+ * Serialisation of setting PARENT_WATCHED on the dentries is provided
+ * by d_lock. If inotify_inode_watched changes after we have taken
+ * d_lock, the following __fsnotify_update_child_dentry_flags call will
+ * find our entry, so it will spin until we complete here, and update
+ * us with the new state.
+ */
parent = dentry->d_parent;
if (parent->d_inode && fsnotify_inode_watches_children(parent->d_inode))
dentry->d_flags |= DCACHE_FSNOTIFY_PARENT_WATCHED;
/*
* fsnotify_d_instantiate - instantiate a dentry for inode
- * Called with dcache_lock held.
*/
static inline void __fsnotify_d_instantiate(struct dentry *dentry, struct inode *inode)
{
if (!inode)
return;
- assert_spin_locked(&dcache_lock);
-
spin_lock(&dentry->d_lock);
__fsnotify_update_dcache_flags(dentry);
spin_unlock(&dentry->d_lock);
/* called from fsnotify listeners, such as fanotify or dnotify */
-/* must call when a group changes its ->mask */
-extern void fsnotify_recalc_global_mask(void);
/* get a reference to an existing or create a new group */
extern struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops);
-/* run all marks associated with this group and update group->mask */
-extern void fsnotify_recalc_group_mask(struct fsnotify_group *group);
/* drop reference on a group from fsnotify_alloc_group */
extern void fsnotify_put_group(struct fsnotify_group *group);
struct fsnotify_event *event);
/* attach the event to the group notification queue */
-extern int fsnotify_add_notify_event(struct fsnotify_group *group,
- struct fsnotify_event *event,
- struct fsnotify_event_private_data *priv,
- int (*merge)(struct list_head *,
- struct fsnotify_event *,
- void **),
- void **arg);
+extern struct fsnotify_event *fsnotify_add_notify_event(struct fsnotify_group *group,
+ struct fsnotify_event *event,
+ struct fsnotify_event_private_data *priv,
+ struct fsnotify_event *(*merge)(struct list_head *,
+ struct fsnotify_event *));
/* true if the group notification queue is empty */
extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group);
/* return, but do not dequeue the first event on the notification queue */
return 0;
}
-static inline void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
-{}
+static inline int __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask)
+{
+ return 0;
+}
static inline void __fsnotify_inode_delete(struct inode *inode)
{}