Merge branch 'for-linus' of git://git.infradead.org/users/eparis/notify
* 'for-linus' of git://git.infradead.org/users/eparis/notify: (22 commits) Ensure FMODE_NONOTIFY is not set by userspace make fanotify_read() restartable across signals fsnotify: remove alignment padding from fsnotify_mark on 64 bit builds fs/notify/fanotify/fanotify_user.c: fix warnings fanotify: Fix FAN_CLOSE comments fanotify: do not recalculate the mask if the ignored mask changed fanotify: ignore events on directories unless specifically requested fsnotify: rename FS_IN_ISDIR to FS_ISDIR fanotify: do not send events for irregular files fanotify: limit number of listeners per user fanotify: allow userspace to override max marks fanotify: limit the number of marks in a single fanotify group fanotify: allow userspace to override max queue depth fsnotify: implement a default maximum queue depth fanotify: ignore fanotify ignore marks if open writers fanotify: allow userspace to flush all marks fsnotify: call fsnotify_parent in perm events fsnotify: correctly handle return codes from listeners fanotify: use __aligned_u64 in fanotify userspace metadata fanotify: implement fanotify listener ordering ...
This commit is contained in:
@@ -118,6 +118,7 @@ header-y += eventpoll.h
|
||||
header-y += ext2_fs.h
|
||||
header-y += fadvise.h
|
||||
header-y += falloc.h
|
||||
header-y += fanotify.h
|
||||
header-y += fb.h
|
||||
header-y += fcntl.h
|
||||
header-y += fd.h
|
||||
|
||||
@@ -6,18 +6,19 @@
|
||||
/* the following events that user-space can register for */
|
||||
#define FAN_ACCESS 0x00000001 /* File was accessed */
|
||||
#define FAN_MODIFY 0x00000002 /* File was modified */
|
||||
#define FAN_CLOSE_WRITE 0x00000008 /* Unwrittable file closed */
|
||||
#define FAN_CLOSE_NOWRITE 0x00000010 /* Writtable file closed */
|
||||
#define FAN_CLOSE_WRITE 0x00000008 /* Writtable file closed */
|
||||
#define FAN_CLOSE_NOWRITE 0x00000010 /* Unwrittable file closed */
|
||||
#define FAN_OPEN 0x00000020 /* File was opened */
|
||||
|
||||
#define FAN_EVENT_ON_CHILD 0x08000000 /* interested in child events */
|
||||
|
||||
/* FIXME currently Q's have no limit.... */
|
||||
#define FAN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */
|
||||
|
||||
#define FAN_OPEN_PERM 0x00010000 /* File open in perm check */
|
||||
#define FAN_ACCESS_PERM 0x00020000 /* File accessed in perm check */
|
||||
|
||||
#define FAN_ONDIR 0x40000000 /* event occurred against dir */
|
||||
|
||||
#define FAN_EVENT_ON_CHILD 0x08000000 /* interested in child events */
|
||||
|
||||
/* helper events */
|
||||
#define FAN_CLOSE (FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE) /* close */
|
||||
|
||||
@@ -25,7 +26,19 @@
|
||||
#define FAN_CLOEXEC 0x00000001
|
||||
#define FAN_NONBLOCK 0x00000002
|
||||
|
||||
#define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK)
|
||||
/* These are NOT bitwise flags. Both bits are used togther. */
|
||||
#define FAN_CLASS_NOTIF 0x00000000
|
||||
#define FAN_CLASS_CONTENT 0x00000004
|
||||
#define FAN_CLASS_PRE_CONTENT 0x00000008
|
||||
#define FAN_ALL_CLASS_BITS (FAN_CLASS_NOTIF | FAN_CLASS_CONTENT | \
|
||||
FAN_CLASS_PRE_CONTENT)
|
||||
|
||||
#define FAN_UNLIMITED_QUEUE 0x00000010
|
||||
#define FAN_UNLIMITED_MARKS 0x00000020
|
||||
|
||||
#define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK | \
|
||||
FAN_ALL_CLASS_BITS | FAN_UNLIMITED_QUEUE |\
|
||||
FAN_UNLIMITED_MARKS)
|
||||
|
||||
/* flags used for fanotify_modify_mark() */
|
||||
#define FAN_MARK_ADD 0x00000001
|
||||
@@ -36,6 +49,10 @@
|
||||
#define FAN_MARK_IGNORED_MASK 0x00000020
|
||||
#define FAN_MARK_IGNORED_SURV_MODIFY 0x00000040
|
||||
#define FAN_MARK_FLUSH 0x00000080
|
||||
#ifdef __KERNEL__
|
||||
/* not valid from userspace, only kernel internal */
|
||||
#define FAN_MARK_ONDIR 0x00000100
|
||||
#endif
|
||||
|
||||
#define FAN_ALL_MARK_FLAGS (FAN_MARK_ADD |\
|
||||
FAN_MARK_REMOVE |\
|
||||
@@ -43,7 +60,8 @@
|
||||
FAN_MARK_ONLYDIR |\
|
||||
FAN_MARK_MOUNT |\
|
||||
FAN_MARK_IGNORED_MASK |\
|
||||
FAN_MARK_IGNORED_SURV_MODIFY)
|
||||
FAN_MARK_IGNORED_SURV_MODIFY |\
|
||||
FAN_MARK_FLUSH)
|
||||
|
||||
/*
|
||||
* All of the events - we build the list by hand so that we can add flags in
|
||||
@@ -70,10 +88,10 @@
|
||||
struct fanotify_event_metadata {
|
||||
__u32 event_len;
|
||||
__u32 vers;
|
||||
__u64 mask;
|
||||
__aligned_u64 mask;
|
||||
__s32 fd;
|
||||
__s32 pid;
|
||||
} __attribute__ ((packed));
|
||||
};
|
||||
|
||||
struct fanotify_response {
|
||||
__s32 fd;
|
||||
|
||||
+22
-16
@@ -26,12 +26,12 @@ static inline void fsnotify_d_instantiate(struct dentry *dentry,
|
||||
}
|
||||
|
||||
/* Notify this dentry's parent about a child's events. */
|
||||
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)
|
||||
{
|
||||
if (!dentry)
|
||||
dentry = path->dentry;
|
||||
|
||||
__fsnotify_parent(path, dentry, mask);
|
||||
return __fsnotify_parent(path, dentry, mask);
|
||||
}
|
||||
|
||||
/* simple call site for access decisions */
|
||||
@@ -40,6 +40,7 @@ static inline int fsnotify_perm(struct file *file, int mask)
|
||||
struct path *path = &file->f_path;
|
||||
struct inode *inode = path->dentry->d_inode;
|
||||
__u32 fsnotify_mask = 0;
|
||||
int ret;
|
||||
|
||||
if (file->f_mode & FMODE_NONOTIFY)
|
||||
return 0;
|
||||
@@ -52,6 +53,10 @@ static inline int fsnotify_perm(struct file *file, int mask)
|
||||
else
|
||||
BUG();
|
||||
|
||||
ret = fsnotify_parent(path, NULL, fsnotify_mask);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return fsnotify(inode, fsnotify_mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
|
||||
}
|
||||
|
||||
@@ -93,8 +98,8 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
|
||||
old_dir_mask |= FS_DN_RENAME;
|
||||
|
||||
if (isdir) {
|
||||
old_dir_mask |= FS_IN_ISDIR;
|
||||
new_dir_mask |= FS_IN_ISDIR;
|
||||
old_dir_mask |= FS_ISDIR;
|
||||
new_dir_mask |= FS_ISDIR;
|
||||
}
|
||||
|
||||
fsnotify(old_dir, old_dir_mask, old_dir, FSNOTIFY_EVENT_INODE, old_name, fs_cookie);
|
||||
@@ -132,7 +137,7 @@ static inline void fsnotify_nameremove(struct dentry *dentry, int isdir)
|
||||
__u32 mask = FS_DELETE;
|
||||
|
||||
if (isdir)
|
||||
mask |= FS_IN_ISDIR;
|
||||
mask |= FS_ISDIR;
|
||||
|
||||
fsnotify_parent(NULL, dentry, mask);
|
||||
}
|
||||
@@ -174,7 +179,7 @@ static inline void fsnotify_link(struct inode *dir, struct inode *inode, struct
|
||||
*/
|
||||
static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry)
|
||||
{
|
||||
__u32 mask = (FS_CREATE | FS_IN_ISDIR);
|
||||
__u32 mask = (FS_CREATE | FS_ISDIR);
|
||||
struct inode *d_inode = dentry->d_inode;
|
||||
|
||||
audit_inode_child(dentry, inode);
|
||||
@@ -192,7 +197,7 @@ static inline void fsnotify_access(struct file *file)
|
||||
__u32 mask = FS_ACCESS;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
mask |= FS_IN_ISDIR;
|
||||
mask |= FS_ISDIR;
|
||||
|
||||
if (!(file->f_mode & FMODE_NONOTIFY)) {
|
||||
fsnotify_parent(path, NULL, mask);
|
||||
@@ -210,7 +215,7 @@ static inline void fsnotify_modify(struct file *file)
|
||||
__u32 mask = FS_MODIFY;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
mask |= FS_IN_ISDIR;
|
||||
mask |= FS_ISDIR;
|
||||
|
||||
if (!(file->f_mode & FMODE_NONOTIFY)) {
|
||||
fsnotify_parent(path, NULL, mask);
|
||||
@@ -228,12 +233,13 @@ static inline void fsnotify_open(struct file *file)
|
||||
__u32 mask = FS_OPEN;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
mask |= FS_IN_ISDIR;
|
||||
mask |= FS_ISDIR;
|
||||
|
||||
if (!(file->f_mode & FMODE_NONOTIFY)) {
|
||||
fsnotify_parent(path, NULL, mask);
|
||||
fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
|
||||
}
|
||||
/* FMODE_NONOTIFY must never be set from user */
|
||||
file->f_mode &= ~FMODE_NONOTIFY;
|
||||
|
||||
fsnotify_parent(path, NULL, mask);
|
||||
fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -247,7 +253,7 @@ static inline void fsnotify_close(struct file *file)
|
||||
__u32 mask = (mode & FMODE_WRITE) ? FS_CLOSE_WRITE : FS_CLOSE_NOWRITE;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
mask |= FS_IN_ISDIR;
|
||||
mask |= FS_ISDIR;
|
||||
|
||||
if (!(file->f_mode & FMODE_NONOTIFY)) {
|
||||
fsnotify_parent(path, NULL, mask);
|
||||
@@ -264,7 +270,7 @@ static inline void fsnotify_xattr(struct dentry *dentry)
|
||||
__u32 mask = FS_ATTRIB;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
mask |= FS_IN_ISDIR;
|
||||
mask |= FS_ISDIR;
|
||||
|
||||
fsnotify_parent(NULL, dentry, mask);
|
||||
fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0);
|
||||
@@ -299,7 +305,7 @@ static inline void fsnotify_change(struct dentry *dentry, unsigned int ia_valid)
|
||||
|
||||
if (mask) {
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
mask |= FS_IN_ISDIR;
|
||||
mask |= FS_ISDIR;
|
||||
|
||||
fsnotify_parent(NULL, dentry, mask);
|
||||
fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0);
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
#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 */
|
||||
@@ -64,13 +64,15 @@
|
||||
|
||||
#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_IN_ISDIR | FS_IN_ONESHOT | FS_DN_RENAME | \
|
||||
FS_ISDIR | FS_IN_ONESHOT | FS_DN_RENAME | \
|
||||
FS_DN_MULTISHOT | FS_EVENT_ON_CHILD)
|
||||
|
||||
struct fsnotify_group;
|
||||
@@ -129,6 +131,14 @@ struct fsnotify_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 */
|
||||
@@ -159,6 +169,8 @@ struct fsnotify_group {
|
||||
bool bypass_perm; /* protected by access_mutex */
|
||||
#endif /* CONFIG_FANOTIFY_ACCESS_PERMISSIONS */
|
||||
int f_flags;
|
||||
unsigned int max_marks;
|
||||
struct user_struct *user;
|
||||
} fanotify_data;
|
||||
#endif /* CONFIG_FANOTIFY */
|
||||
};
|
||||
@@ -275,8 +287,8 @@ struct fsnotify_mark {
|
||||
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
|
||||
@@ -294,7 +306,7 @@ struct fsnotify_mark {
|
||||
/* 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);
|
||||
@@ -423,8 +435,10 @@ static inline int fsnotify(struct inode *to_tell, __u32 mask, void *data, int da
|
||||
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)
|
||||
{}
|
||||
|
||||
@@ -672,6 +672,9 @@ struct user_struct {
|
||||
atomic_t inotify_watches; /* How many inotify watches does this user have? */
|
||||
atomic_t inotify_devs; /* How many inotify devs does this user have opened? */
|
||||
#endif
|
||||
#ifdef CONFIG_FANOTIFY
|
||||
atomic_t fanotify_listeners;
|
||||
#endif
|
||||
#ifdef CONFIG_EPOLL
|
||||
atomic_t epoll_watches; /* The number of file descriptors currently watched */
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user