Commit eb65405e authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'fsnotify_for_v5.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs

Pull fsnotify updates from Jan Kara:

 - fanotify fix for softlockups when there are many queued events

 - performance improvement to reduce fsnotify overhead when not used

 - Amir's implementation of fanotify events with names. With these you
   can now efficiently monitor whole filesystem, eg to mirror changes to
   another machine.

* tag 'fsnotify_for_v5.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs: (37 commits)
  fanotify: compare fsid when merging name event
  fsnotify: create method handle_inode_event() in fsnotify_operations
  fanotify: report parent fid + child fid
  fanotify: report parent fid + name + child fid
  fanotify: add support for FAN_REPORT_NAME
  fanotify: report events with parent dir fid to sb/mount/non-dir marks
  fanotify: add basic support for FAN_REPORT_DIR_FID
  fsnotify: remove check that source dentry is positive
  fsnotify: send event with parent/name info to sb/mount/non-dir marks
  audit: do not set FS_EVENT_ON_CHILD in audit marks mask
  inotify: do not set FS_EVENT_ON_CHILD in non-dir mark mask
  fsnotify: pass dir and inode arguments to fsnotify()
  fsnotify: create helper fsnotify_inode()
  fsnotify: send event to parent and child with single callback
  inotify: report both events on parent and child with single callback
  dnotify: report both events on parent and child with single callback
  fanotify: no external fh buffer in fanotify_name_event
  fanotify: use struct fanotify_info to parcel the variable size buffer
  fsnotify: add object type "child" to object type iterator
  fanotify: use FAN_EVENT_ON_CHILD as implicit flag on sb/mount/non-dir marks
  ...
parents 09e70bb4 8aed8ceb
...@@ -883,6 +883,7 @@ static void kernfs_notify_workfn(struct work_struct *work) ...@@ -883,6 +883,7 @@ static void kernfs_notify_workfn(struct work_struct *work)
list_for_each_entry(info, &kernfs_root(kn)->supers, node) { list_for_each_entry(info, &kernfs_root(kn)->supers, node) {
struct kernfs_node *parent; struct kernfs_node *parent;
struct inode *p_inode = NULL;
struct inode *inode; struct inode *inode;
struct qstr name; struct qstr name;
...@@ -899,20 +900,20 @@ static void kernfs_notify_workfn(struct work_struct *work) ...@@ -899,20 +900,20 @@ static void kernfs_notify_workfn(struct work_struct *work)
name = (struct qstr)QSTR_INIT(kn->name, strlen(kn->name)); name = (struct qstr)QSTR_INIT(kn->name, strlen(kn->name));
parent = kernfs_get_parent(kn); parent = kernfs_get_parent(kn);
if (parent) { if (parent) {
struct inode *p_inode;
p_inode = ilookup(info->sb, kernfs_ino(parent)); p_inode = ilookup(info->sb, kernfs_ino(parent));
if (p_inode) { if (p_inode) {
fsnotify(p_inode, FS_MODIFY | FS_EVENT_ON_CHILD, fsnotify(FS_MODIFY | FS_EVENT_ON_CHILD,
inode, FSNOTIFY_EVENT_INODE, &name, 0); inode, FSNOTIFY_EVENT_INODE,
p_inode, &name, inode, 0);
iput(p_inode); iput(p_inode);
} }
kernfs_put(parent); kernfs_put(parent);
} }
fsnotify(inode, FS_MODIFY, inode, FSNOTIFY_EVENT_INODE, if (!p_inode)
&name, 0); fsnotify_inode(inode, FS_MODIFY);
iput(inode); iput(inode);
} }
......
...@@ -598,11 +598,9 @@ static struct notifier_block nfsd_file_lease_notifier = { ...@@ -598,11 +598,9 @@ static struct notifier_block nfsd_file_lease_notifier = {
}; };
static int static int
nfsd_file_fsnotify_handle_event(struct fsnotify_group *group, nfsd_file_fsnotify_handle_event(struct fsnotify_mark *mark, u32 mask,
struct inode *inode, struct inode *inode, struct inode *dir,
u32 mask, const void *data, int data_type, const struct qstr *name)
const struct qstr *file_name, u32 cookie,
struct fsnotify_iter_info *iter_info)
{ {
trace_nfsd_file_fsnotify_handle_event(inode, mask); trace_nfsd_file_fsnotify_handle_event(inode, mask);
...@@ -624,7 +622,7 @@ nfsd_file_fsnotify_handle_event(struct fsnotify_group *group, ...@@ -624,7 +622,7 @@ nfsd_file_fsnotify_handle_event(struct fsnotify_group *group,
static const struct fsnotify_ops nfsd_file_fsnotify_ops = { static const struct fsnotify_ops nfsd_file_fsnotify_ops = {
.handle_event = nfsd_file_fsnotify_handle_event, .handle_inode_event = nfsd_file_fsnotify_handle_event,
.free_mark = nfsd_file_mark_free, .free_mark = nfsd_file_mark_free,
}; };
......
...@@ -70,13 +70,10 @@ static void dnotify_recalc_inode_mask(struct fsnotify_mark *fsn_mark) ...@@ -70,13 +70,10 @@ static void dnotify_recalc_inode_mask(struct fsnotify_mark *fsn_mark)
* destroy the dnotify struct if it was not registered to receive multiple * destroy the dnotify struct if it was not registered to receive multiple
* events. * events.
*/ */
static int dnotify_handle_event(struct fsnotify_group *group, static int dnotify_handle_event(struct fsnotify_mark *inode_mark, u32 mask,
struct inode *inode, struct inode *inode, struct inode *dir,
u32 mask, const void *data, int data_type, const struct qstr *name)
const struct qstr *file_name, u32 cookie,
struct fsnotify_iter_info *iter_info)
{ {
struct fsnotify_mark *inode_mark = fsnotify_iter_inode_mark(iter_info);
struct dnotify_mark *dn_mark; struct dnotify_mark *dn_mark;
struct dnotify_struct *dn; struct dnotify_struct *dn;
struct dnotify_struct **prev; struct dnotify_struct **prev;
...@@ -84,10 +81,7 @@ static int dnotify_handle_event(struct fsnotify_group *group, ...@@ -84,10 +81,7 @@ static int dnotify_handle_event(struct fsnotify_group *group,
__u32 test_mask = mask & ~FS_EVENT_ON_CHILD; __u32 test_mask = mask & ~FS_EVENT_ON_CHILD;
/* not a dir, dnotify doesn't care */ /* not a dir, dnotify doesn't care */
if (!S_ISDIR(inode->i_mode)) if (!dir && !(mask & FS_ISDIR))
return 0;
if (WARN_ON(fsnotify_iter_vfsmount_mark(iter_info)))
return 0; return 0;
dn_mark = container_of(inode_mark, struct dnotify_mark, fsn_mark); dn_mark = container_of(inode_mark, struct dnotify_mark, fsn_mark);
...@@ -127,7 +121,7 @@ static void dnotify_free_mark(struct fsnotify_mark *fsn_mark) ...@@ -127,7 +121,7 @@ static void dnotify_free_mark(struct fsnotify_mark *fsn_mark)
} }
static const struct fsnotify_ops dnotify_fsnotify_ops = { static const struct fsnotify_ops dnotify_fsnotify_ops = {
.handle_event = dnotify_handle_event, .handle_inode_event = dnotify_handle_event,
.free_mark = dnotify_free_mark, .free_mark = dnotify_free_mark,
}; };
......
This diff is collapsed.
...@@ -23,20 +23,41 @@ enum { ...@@ -23,20 +23,41 @@ enum {
* stored in either the first or last 2 dwords. * stored in either the first or last 2 dwords.
*/ */
#define FANOTIFY_INLINE_FH_LEN (3 << 2) #define FANOTIFY_INLINE_FH_LEN (3 << 2)
#define FANOTIFY_FH_HDR_LEN offsetof(struct fanotify_fh, buf)
/* Fixed size struct for file handle */
struct fanotify_fh { struct fanotify_fh {
unsigned char buf[FANOTIFY_INLINE_FH_LEN];
u8 type; u8 type;
u8 len; u8 len;
#define FANOTIFY_FH_FLAG_EXT_BUF 1
u8 flags;
u8 pad;
unsigned char buf[];
} __aligned(4);
/* Variable size struct for dir file handle + child file handle + name */
struct fanotify_info {
/* size of dir_fh/file_fh including fanotify_fh hdr size */
u8 dir_fh_totlen;
u8 file_fh_totlen;
u8 name_len;
u8 pad;
unsigned char buf[];
/*
* (struct fanotify_fh) dir_fh starts at buf[0]
* (optional) file_fh starts at buf[dir_fh_totlen]
* name starts at buf[dir_fh_totlen + file_fh_totlen]
*/
} __aligned(4); } __aligned(4);
static inline bool fanotify_fh_has_ext_buf(struct fanotify_fh *fh) static inline bool fanotify_fh_has_ext_buf(struct fanotify_fh *fh)
{ {
return fh->len > FANOTIFY_INLINE_FH_LEN; return (fh->flags & FANOTIFY_FH_FLAG_EXT_BUF);
} }
static inline char **fanotify_fh_ext_buf_ptr(struct fanotify_fh *fh) static inline char **fanotify_fh_ext_buf_ptr(struct fanotify_fh *fh)
{ {
BUILD_BUG_ON(FANOTIFY_FH_HDR_LEN % 4);
BUILD_BUG_ON(__alignof__(char *) - 4 + sizeof(char *) > BUILD_BUG_ON(__alignof__(char *) - 4 + sizeof(char *) >
FANOTIFY_INLINE_FH_LEN); FANOTIFY_INLINE_FH_LEN);
return (char **)ALIGN((unsigned long)(fh->buf), __alignof__(char *)); return (char **)ALIGN((unsigned long)(fh->buf), __alignof__(char *));
...@@ -52,6 +73,56 @@ static inline void *fanotify_fh_buf(struct fanotify_fh *fh) ...@@ -52,6 +73,56 @@ static inline void *fanotify_fh_buf(struct fanotify_fh *fh)
return fanotify_fh_has_ext_buf(fh) ? fanotify_fh_ext_buf(fh) : fh->buf; return fanotify_fh_has_ext_buf(fh) ? fanotify_fh_ext_buf(fh) : fh->buf;
} }
static inline int fanotify_info_dir_fh_len(struct fanotify_info *info)
{
if (!info->dir_fh_totlen ||
WARN_ON_ONCE(info->dir_fh_totlen < FANOTIFY_FH_HDR_LEN))
return 0;
return info->dir_fh_totlen - FANOTIFY_FH_HDR_LEN;
}
static inline struct fanotify_fh *fanotify_info_dir_fh(struct fanotify_info *info)
{
BUILD_BUG_ON(offsetof(struct fanotify_info, buf) % 4);
return (struct fanotify_fh *)info->buf;
}
static inline int fanotify_info_file_fh_len(struct fanotify_info *info)
{
if (!info->file_fh_totlen ||
WARN_ON_ONCE(info->file_fh_totlen < FANOTIFY_FH_HDR_LEN))
return 0;
return info->file_fh_totlen - FANOTIFY_FH_HDR_LEN;
}
static inline struct fanotify_fh *fanotify_info_file_fh(struct fanotify_info *info)
{
return (struct fanotify_fh *)(info->buf + info->dir_fh_totlen);
}
static inline const char *fanotify_info_name(struct fanotify_info *info)
{
return info->buf + info->dir_fh_totlen + info->file_fh_totlen;
}
static inline void fanotify_info_init(struct fanotify_info *info)
{
info->dir_fh_totlen = 0;
info->file_fh_totlen = 0;
info->name_len = 0;
}
static inline void fanotify_info_copy_name(struct fanotify_info *info,
const struct qstr *name)
{
info->name_len = name->len;
strcpy(info->buf + info->dir_fh_totlen + info->file_fh_totlen,
name->name);
}
/* /*
* Common structure for fanotify events. Concrete structs are allocated in * Common structure for fanotify events. Concrete structs are allocated in
* fanotify_handle_event() and freed when the information is retrieved by * fanotify_handle_event() and freed when the information is retrieved by
...@@ -63,6 +134,7 @@ enum fanotify_event_type { ...@@ -63,6 +134,7 @@ enum fanotify_event_type {
FANOTIFY_EVENT_TYPE_FID_NAME, /* variable length */ FANOTIFY_EVENT_TYPE_FID_NAME, /* variable length */
FANOTIFY_EVENT_TYPE_PATH, FANOTIFY_EVENT_TYPE_PATH,
FANOTIFY_EVENT_TYPE_PATH_PERM, FANOTIFY_EVENT_TYPE_PATH_PERM,
FANOTIFY_EVENT_TYPE_OVERFLOW, /* struct fanotify_event */
}; };
struct fanotify_event { struct fanotify_event {
...@@ -72,10 +144,20 @@ struct fanotify_event { ...@@ -72,10 +144,20 @@ struct fanotify_event {
struct pid *pid; struct pid *pid;
}; };
static inline void fanotify_init_event(struct fanotify_event *event,
unsigned long id, u32 mask)
{
fsnotify_init_event(&event->fse, id);
event->mask = mask;
event->pid = NULL;
}
struct fanotify_fid_event { struct fanotify_fid_event {
struct fanotify_event fae; struct fanotify_event fae;
__kernel_fsid_t fsid; __kernel_fsid_t fsid;
struct fanotify_fh object_fh; struct fanotify_fh object_fh;
/* Reserve space in object_fh.buf[] - access with fanotify_fh_buf() */
unsigned char _inline_fh_buf[FANOTIFY_INLINE_FH_LEN];
}; };
static inline struct fanotify_fid_event * static inline struct fanotify_fid_event *
...@@ -87,9 +169,7 @@ FANOTIFY_FE(struct fanotify_event *event) ...@@ -87,9 +169,7 @@ FANOTIFY_FE(struct fanotify_event *event)
struct fanotify_name_event { struct fanotify_name_event {
struct fanotify_event fae; struct fanotify_event fae;
__kernel_fsid_t fsid; __kernel_fsid_t fsid;
struct fanotify_fh dir_fh; struct fanotify_info info;
u8 name_len;
char name[];
}; };
static inline struct fanotify_name_event * static inline struct fanotify_name_event *
...@@ -113,35 +193,37 @@ static inline struct fanotify_fh *fanotify_event_object_fh( ...@@ -113,35 +193,37 @@ static inline struct fanotify_fh *fanotify_event_object_fh(
{ {
if (event->type == FANOTIFY_EVENT_TYPE_FID) if (event->type == FANOTIFY_EVENT_TYPE_FID)
return &FANOTIFY_FE(event)->object_fh; return &FANOTIFY_FE(event)->object_fh;
else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME)
return fanotify_info_file_fh(&FANOTIFY_NE(event)->info);
else else
return NULL; return NULL;
} }
static inline struct fanotify_fh *fanotify_event_dir_fh( static inline struct fanotify_info *fanotify_event_info(
struct fanotify_event *event) struct fanotify_event *event)
{ {
if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME) if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME)
return &FANOTIFY_NE(event)->dir_fh; return &FANOTIFY_NE(event)->info;
else else
return NULL; return NULL;
} }
static inline int fanotify_event_object_fh_len(struct fanotify_event *event) static inline int fanotify_event_object_fh_len(struct fanotify_event *event)
{ {
struct fanotify_info *info = fanotify_event_info(event);
struct fanotify_fh *fh = fanotify_event_object_fh(event); struct fanotify_fh *fh = fanotify_event_object_fh(event);
return fh ? fh->len : 0; if (info)
return info->file_fh_totlen ? fh->len : 0;
else
return fh ? fh->len : 0;
} }
static inline bool fanotify_event_has_name(struct fanotify_event *event) static inline int fanotify_event_dir_fh_len(struct fanotify_event *event)
{ {
return event->type == FANOTIFY_EVENT_TYPE_FID_NAME; struct fanotify_info *info = fanotify_event_info(event);
}
static inline int fanotify_event_name_len(struct fanotify_event *event) return info ? fanotify_info_dir_fh_len(info) : 0;
{
return fanotify_event_has_name(event) ?
FANOTIFY_NE(event)->name_len : 0;
} }
struct fanotify_path_event { struct fanotify_path_event {
...@@ -202,9 +284,3 @@ static inline struct path *fanotify_event_path(struct fanotify_event *event) ...@@ -202,9 +284,3 @@ static inline struct path *fanotify_event_path(struct fanotify_event *event)
else else
return NULL; return NULL;
} }
struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
struct inode *inode, u32 mask,
const void *data, int data_type,
const struct qstr *file_name,
__kernel_fsid_t *fsid);
This diff is collapsed.
This diff is collapsed.
...@@ -24,9 +24,9 @@ static inline struct inotify_event_info *INOTIFY_E(struct fsnotify_event *fse) ...@@ -24,9 +24,9 @@ static inline struct inotify_event_info *INOTIFY_E(struct fsnotify_event *fse)
extern void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, extern void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark,
struct fsnotify_group *group); struct fsnotify_group *group);
extern int inotify_handle_event(struct fsnotify_group *group, extern int inotify_handle_event(struct fsnotify_group *group, u32 mask,
struct inode *inode, const void *data, int data_type,
u32 mask, const void *data, int data_type, struct inode *dir,
const struct qstr *file_name, u32 cookie, const struct qstr *file_name, u32 cookie,
struct fsnotify_iter_info *iter_info); struct fsnotify_iter_info *iter_info);
......
...@@ -39,7 +39,7 @@ static bool event_compare(struct fsnotify_event *old_fsn, ...@@ -39,7 +39,7 @@ static bool event_compare(struct fsnotify_event *old_fsn,
if (old->mask & FS_IN_IGNORED) if (old->mask & FS_IN_IGNORED)
return false; return false;
if ((old->mask == new->mask) && if ((old->mask == new->mask) &&
(old_fsn->objectid == new_fsn->objectid) && (old->wd == new->wd) &&
(old->name_len == new->name_len) && (old->name_len == new->name_len) &&
(!old->name_len || !strcmp(old->name, new->name))) (!old->name_len || !strcmp(old->name, new->name)))
return true; return true;
...@@ -55,14 +55,11 @@ static int inotify_merge(struct list_head *list, ...@@ -55,14 +55,11 @@ static int inotify_merge(struct list_head *list,
return event_compare(last_event, event); return event_compare(last_event, event);
} }
int inotify_handle_event(struct fsnotify_group *group, static int inotify_one_event(struct fsnotify_group *group, u32 mask,
struct inode *inode, struct fsnotify_mark *inode_mark,
u32 mask, const void *data, int data_type, const struct path *path,
const struct qstr *file_name, u32 cookie, const struct qstr *file_name, u32 cookie)
struct fsnotify_iter_info *iter_info)
{ {
const struct path *path = fsnotify_data_path(data, data_type);
struct fsnotify_mark *inode_mark = fsnotify_iter_inode_mark(iter_info);
struct inotify_inode_mark *i_mark; struct inotify_inode_mark *i_mark;
struct inotify_event_info *event; struct inotify_event_info *event;
struct fsnotify_event *fsn_event; struct fsnotify_event *fsn_event;
...@@ -70,9 +67,6 @@ int inotify_handle_event(struct fsnotify_group *group, ...@@ -70,9 +67,6 @@ int inotify_handle_event(struct fsnotify_group *group,
int len = 0; int len = 0;
int alloc_len = sizeof(struct inotify_event_info); int alloc_len = sizeof(struct inotify_event_info);
if (WARN_ON(fsnotify_iter_vfsmount_mark(iter_info)))
return 0;
if ((inode_mark->mask & FS_EXCL_UNLINK) && if ((inode_mark->mask & FS_EXCL_UNLINK) &&
path && d_unlinked(path->dentry)) path && d_unlinked(path->dentry))
return 0; return 0;
...@@ -82,7 +76,7 @@ int inotify_handle_event(struct fsnotify_group *group, ...@@ -82,7 +76,7 @@ int inotify_handle_event(struct fsnotify_group *group,
alloc_len += len + 1; alloc_len += len + 1;
} }
pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode, pr_debug("%s: group=%p mark=%p mask=%x\n", __func__, group, inode_mark,
mask); mask);
i_mark = container_of(inode_mark, struct inotify_inode_mark, i_mark = container_of(inode_mark, struct inotify_inode_mark,
...@@ -116,7 +110,7 @@ int inotify_handle_event(struct fsnotify_group *group, ...@@ -116,7 +110,7 @@ int inotify_handle_event(struct fsnotify_group *group,
mask &= ~IN_ISDIR; mask &= ~IN_ISDIR;
fsn_event = &event->fse; fsn_event = &event->fse;
fsnotify_init_event(fsn_event, (unsigned long)inode); fsnotify_init_event(fsn_event, 0);
event->mask = mask; event->mask = mask;
event->wd = i_mark->wd; event->wd = i_mark->wd;
event->sync_cookie = cookie; event->sync_cookie = cookie;
...@@ -136,6 +130,37 @@ int inotify_handle_event(struct fsnotify_group *group, ...@@ -136,6 +130,37 @@ int inotify_handle_event(struct fsnotify_group *group,
return 0; return 0;
} }
int inotify_handle_event(struct fsnotify_group *group, u32 mask,
const void *data, int data_type, struct inode *dir,
const struct qstr *file_name, u32 cookie,
struct fsnotify_iter_info *iter_info)
{
const struct path *path = fsnotify_data_path(data, data_type);
struct fsnotify_mark *inode_mark = fsnotify_iter_inode_mark(iter_info);
struct fsnotify_mark *child_mark = fsnotify_iter_child_mark(iter_info);
int ret = 0;
if (WARN_ON(fsnotify_iter_vfsmount_mark(iter_info)))
return 0;
/*
* Some events cannot be sent on both parent and child marks
* (e.g. IN_CREATE). Those events are always sent on inode_mark.
* For events that are possible on both parent and child (e.g. IN_OPEN),
* event is sent on inode_mark with name if the parent is watching and
* is sent on child_mark without name if child is watching.
* If both parent and child are watching, report the event with child's
* name here and report another event without child's name below.
*/
if (inode_mark)
ret = inotify_one_event(group, mask, inode_mark, path,
file_name, cookie);
if (ret || !child_mark)
return ret;
return inotify_one_event(group, mask, child_mark, path, NULL, 0);
}
static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group) static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group)
{ {
inotify_ignored_and_remove_idr(fsn_mark, group); inotify_ignored_and_remove_idr(fsn_mark, group);
......
...@@ -75,15 +75,17 @@ struct ctl_table inotify_table[] = { ...@@ -75,15 +75,17 @@ struct ctl_table inotify_table[] = {
}; };
#endif /* CONFIG_SYSCTL */ #endif /* CONFIG_SYSCTL */
static inline __u32 inotify_arg_to_mask(u32 arg) static inline __u32 inotify_arg_to_mask(struct inode *inode, u32 arg)
{ {
__u32 mask; __u32 mask;
/* /*
* everything should accept their own ignored, cares about children, * Everything should accept their own ignored and should receive events
* and should receive events when the inode is unmounted * when the inode is unmounted. All directories care about children.
*/ */
mask = (FS_IN_IGNORED | FS_EVENT_ON_CHILD | FS_UNMOUNT); mask = (FS_IN_IGNORED | FS_UNMOUNT);
if (S_ISDIR(inode->i_mode))
mask |= FS_EVENT_ON_CHILD;
/* mask off the flags used to open the fd */ /* mask off the flags used to open the fd */
mask |= (arg & (IN_ALL_EVENTS | IN_ONESHOT | IN_EXCL_UNLINK)); mask |= (arg & (IN_ALL_EVENTS | IN_ONESHOT | IN_EXCL_UNLINK));
...@@ -490,8 +492,8 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, ...@@ -490,8 +492,8 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark,
fsn_mark); fsn_mark);
/* Queue ignore event for the watch */ /* Queue ignore event for the watch */
inotify_handle_event(group, NULL, FS_IN_IGNORED, NULL, inotify_handle_event(group, FS_IN_IGNORED, NULL, FSNOTIFY_EVENT_NONE,
FSNOTIFY_EVENT_NONE, NULL, 0, &iter_info); NULL, NULL, 0, &iter_info);
i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
/* remove this mark from the idr */ /* remove this mark from the idr */
...@@ -512,7 +514,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, ...@@ -512,7 +514,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group,
int create = (arg & IN_MASK_CREATE); int create = (arg & IN_MASK_CREATE);
int ret; int ret;
mask = inotify_arg_to_mask(arg); mask = inotify_arg_to_mask(inode, arg);
fsn_mark = fsnotify_find_mark(&inode->i_fsnotify_marks, group); fsn_mark = fsnotify_find_mark(&inode->i_fsnotify_marks, group);
if (!fsn_mark) if (!fsn_mark)
...@@ -565,7 +567,7 @@ static int inotify_new_watch(struct fsnotify_group *group, ...@@ -565,7 +567,7 @@ static int inotify_new_watch(struct fsnotify_group *group,
struct idr *idr = &group->inotify_data.idr; struct idr *idr = &group->inotify_data.idr;
spinlock_t *idr_lock = &group->inotify_data.idr_lock; spinlock_t *idr_lock = &group->inotify_data.idr_lock;
mask = inotify_arg_to_mask(arg); mask = inotify_arg_to_mask(inode, arg);
tmp_i_mark = kmem_cache_alloc(inotify_inode_mark_cachep, GFP_KERNEL); tmp_i_mark = kmem_cache_alloc(inotify_inode_mark_cachep, GFP_KERNEL);
if (unlikely(!tmp_i_mark)) if (unlikely(!tmp_i_mark))
......
...@@ -18,8 +18,10 @@ ...@@ -18,8 +18,10 @@
#define FANOTIFY_CLASS_BITS (FAN_CLASS_NOTIF | FAN_CLASS_CONTENT | \ #define FANOTIFY_CLASS_BITS (FAN_CLASS_NOTIF | FAN_CLASS_CONTENT | \
FAN_CLASS_PRE_CONTENT) FAN_CLASS_PRE_CONTENT)
#define FANOTIFY_INIT_FLAGS (FANOTIFY_CLASS_BITS | \ #define FANOTIFY_FID_BITS (FAN_REPORT_FID | FAN_REPORT_DFID_NAME)
FAN_REPORT_TID | FAN_REPORT_FID | \
#define FANOTIFY_INIT_FLAGS (FANOTIFY_CLASS_BITS | FANOTIFY_FID_BITS | \
FAN_REPORT_TID | \
FAN_CLOEXEC | FAN_NONBLOCK | \ FAN_CLOEXEC | FAN_NONBLOCK | \
FAN_UNLIMITED_QUEUE | FAN_UNLIMITED_MARKS) FAN_UNLIMITED_QUEUE | FAN_UNLIMITED_MARKS)
......
...@@ -23,19 +23,14 @@ ...@@ -23,19 +23,14 @@
* have changed (i.e. renamed over). * have changed (i.e. renamed over).
* *
* Unlike fsnotify_parent(), the event will be reported regardless of the * Unlike fsnotify_parent(), the event will be reported regardless of the
* FS_EVENT_ON_CHILD mask on the parent inode. * FS_EVENT_ON_CHILD mask on the parent inode and will not be reported if only
* the child is interested and not the parent.
*/ */
static inline void fsnotify_name(struct inode *dir, __u32 mask, static inline void fsnotify_name(struct inode *dir, __u32 mask,
struct inode *child, struct inode *child,
const struct qstr *name, u32 cookie) const struct qstr *name, u32 cookie)
{ {
fsnotify(dir, mask, child, FSNOTIFY_EVENT_INODE, name, cookie); fsnotify(mask, child, FSNOTIFY_EVENT_INODE, dir, name, NULL, cookie);
/*
* Send another flavor of the event without child inode data and
* without the specific event type (e.g. FS_CREATE|FS_IS_DIR).
* The name is relative to the dir inode the event is reported to.
*/
fsnotify(dir, FS_DIR_MODIFY, dir, FSNOTIFY_EVENT_INODE, name, 0);
} }
static inline void fsnotify_dirent(struct inode *dir, struct dentry *dentry, static inline void fsnotify_dirent(struct inode *dir, struct dentry *dentry,
...@@ -44,38 +39,55 @@ static inline void fsnotify_dirent(struct inode *dir, struct dentry *dentry, ...@@ -44,38 +39,55 @@ static inline void fsnotify_dirent(struct inode *dir, struct dentry *dentry,
fsnotify_name(dir, mask, d_inode(dentry), &dentry->d_name, 0); fsnotify_name(dir, mask, d_inode(dentry), &dentry->d_name, 0);
} }
/* static inline void fsnotify_inode(struct inode *inode, __u32 mask)
* Simple wrappers to consolidate calls fsnotify_parent()/fsnotify() when {
* an event is on a file/dentry. if (S_ISDIR(inode->i_mode))
*/ mask |= FS_ISDIR;
static inline void fsnotify_dentry(struct dentry *dentry, __u32 mask)
fsnotify(mask, inode, FSNOTIFY_EVENT_INODE, NULL, NULL, inode, 0);
}
/* Notify this dentry's parent about a child's events. */
static inline int fsnotify_parent(struct dentry *dentry, __u32 mask,
const void *data, int data_type)
{ {
struct inode *inode = d_inode(dentry); struct inode *inode = d_inode(dentry);
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode)) {
mask |= FS_ISDIR; mask |= FS_ISDIR;
fsnotify_parent(dentry, mask, inode, FSNOTIFY_EVENT_INODE); /* sb/mount marks are not interested in name of directory */
fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0); if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED))
goto notify_child;
}
/* disconnected dentry cannot notify parent */
if (IS_ROOT(dentry))
goto notify_child;
return __fsnotify_parent(dentry, mask, data, data_type);
notify_child:
return fsnotify(mask, data, data_type, NULL, NULL, inode, 0);
}
/*
* Simple wrappers to consolidate calls to fsnotify_parent() when an event
* is on a file/dentry.
*/
static inline void fsnotify_dentry(struct dentry *dentry, __u32 mask)
{
fsnotify_parent(dentry, mask, d_inode(dentry), FSNOTIFY_EVENT_INODE);
} }
static inline int fsnotify_file(struct file *file, __u32 mask) static inline int fsnotify_file(struct file *file, __u32 mask)
{ {
const struct path *path = &file->f_path; const struct path *path = &file->f_path;
struct inode *inode = file_inode(file);
int ret;
if (file->f_mode & FMODE_NONOTIFY) if (file->f_mode & FMODE_NONOTIFY)
return 0; return 0;
if (S_ISDIR(inode->i_mode)) return fsnotify_parent(path->dentry, mask, path, FSNOTIFY_EVENT_PATH);
mask |= FS_ISDIR;
ret = fsnotify_parent(path->dentry, mask, path, FSNOTIFY_EVENT_PATH);
if (ret)
return ret;
return fsnotify(inode, mask, path, FSNOTIFY_EVENT_PATH, NULL, 0);
} }
/* Simple call site for access decisions */ /* Simple call site for access decisions */
...@@ -108,12 +120,7 @@ static inline int fsnotify_perm(struct file *file, int mask) ...@@ -108,12 +120,7 @@ static inline int fsnotify_perm(struct file *file, int mask)
*/ */
static inline void fsnotify_link_count(struct inode *inode) static inline void fsnotify_link_count(struct inode *inode)
{ {
__u32 mask = FS_ATTRIB; fsnotify_inode(inode, FS_ATTRIB);
if (S_ISDIR(inode->i_mode))
mask |= FS_ISDIR;
fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0);
} }
/* /*
...@@ -128,7 +135,6 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, ...@@ -128,7 +135,6 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
u32 fs_cookie = fsnotify_get_cookie(); u32 fs_cookie = fsnotify_get_cookie();
__u32 old_dir_mask = FS_MOVED_FROM; __u32 old_dir_mask = FS_MOVED_FROM;
__u32 new_dir_mask = FS_MOVED_TO; __u32 new_dir_mask = FS_MOVED_TO;
__u32 mask = FS_MOVE_SELF;
const struct qstr *new_name = &moved->d_name; const struct qstr *new_name = &moved->d_name;
if (old_dir == new_dir) if (old_dir == new_dir)
...@@ -137,7 +143,6 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, ...@@ -137,7 +143,6 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
if (isdir) { if (isdir) {
old_dir_mask |= FS_ISDIR; old_dir_mask |= FS_ISDIR;
new_dir_mask |= FS_ISDIR; new_dir_mask |= FS_ISDIR;
mask |= FS_ISDIR;
} }
fsnotify_name(old_dir, old_dir_mask, source, old_name, fs_cookie); fsnotify_name(old_dir, old_dir_mask, source, old_name, fs_cookie);
...@@ -145,9 +150,7 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir, ...@@ -145,9 +150,7 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
if (target) if (target)
fsnotify_link_count(target); fsnotify_link_count(target);
fsnotify_inode(source, FS_MOVE_SELF);
if (source)
fsnotify(source, mask, source, FSNOTIFY_EVENT_INODE, NULL, 0);
audit_inode_child(new_dir, moved, AUDIT_TYPE_CHILD_CREATE); audit_inode_child(new_dir, moved, AUDIT_TYPE_CHILD_CREATE);
} }
...@@ -172,12 +175,7 @@ static inline void fsnotify_vfsmount_delete(struct vfsmount *mnt) ...@@ -172,12 +175,7 @@ static inline void fsnotify_vfsmount_delete(struct vfsmount *mnt)
*/ */
static inline void fsnotify_inoderemove(struct inode *inode) static inline void fsnotify_inoderemove(struct inode *inode)
{ {
__u32 mask = FS_DELETE_SELF; fsnotify_inode(inode, FS_DELETE_SELF);
if (S_ISDIR(inode->i_mode))
mask |= FS_ISDIR;
fsnotify(inode, mask, inode, FSNOTIFY_EVENT_INODE, NULL, 0);
__fsnotify_inode_delete(inode); __fsnotify_inode_delete(inode);
} }
......
...@@ -47,11 +47,13 @@ ...@@ -47,11 +47,13 @@
#define FS_OPEN_PERM 0x00010000 /* open event in an permission hook */ #define FS_OPEN_PERM 0x00010000 /* open event in an permission hook */
#define FS_ACCESS_PERM 0x00020000 /* access event in a permissions hook */ #define FS_ACCESS_PERM 0x00020000 /* access event in a permissions hook */
#define FS_OPEN_EXEC_PERM 0x00040000 /* open/exec event in a permission hook */ #define FS_OPEN_EXEC_PERM 0x00040000 /* open/exec event in a permission hook */
#define FS_DIR_MODIFY 0x00080000 /* Directory entry was modified */
#define FS_EXCL_UNLINK 0x04000000 /* do not send events if object is unlinked */ #define FS_EXCL_UNLINK 0x04000000 /* do not send events if object is unlinked */
/* This inode cares about things that happen to its children. Always set for /*
* dnotify and inotify. */ * Set on inode mark that cares about things that happen to its children.
* Always set for dnotify and inotify.
* Set on inode/sb/mount marks that care about parent/name info.
*/
#define FS_EVENT_ON_CHILD 0x08000000 #define FS_EVENT_ON_CHILD 0x08000000
#define FS_DN_RENAME 0x10000000 /* file renamed */ #define FS_DN_RENAME 0x10000000 /* file renamed */
...@@ -67,21 +69,28 @@ ...@@ -67,21 +69,28 @@
* The watching parent may get an FS_ATTRIB|FS_EVENT_ON_CHILD event * The watching parent may get an FS_ATTRIB|FS_EVENT_ON_CHILD event
* when a directory entry inside a child subdir changes. * when a directory entry inside a child subdir changes.
*/ */
#define ALL_FSNOTIFY_DIRENT_EVENTS (FS_CREATE | FS_DELETE | FS_MOVE | \ #define ALL_FSNOTIFY_DIRENT_EVENTS (FS_CREATE | FS_DELETE | FS_MOVE)
FS_DIR_MODIFY)
#define ALL_FSNOTIFY_PERM_EVENTS (FS_OPEN_PERM | FS_ACCESS_PERM | \ #define ALL_FSNOTIFY_PERM_EVENTS (FS_OPEN_PERM | FS_ACCESS_PERM | \
FS_OPEN_EXEC_PERM) FS_OPEN_EXEC_PERM)
/* /*
* This is a list of all events that may get sent to a parent based on fs event * This is a list of all events that may get sent to a parent that is watching
* happening to inodes inside that directory. * with flag FS_EVENT_ON_CHILD based on fs event on a child of that directory.
*/ */
#define FS_EVENTS_POSS_ON_CHILD (ALL_FSNOTIFY_PERM_EVENTS | \ #define FS_EVENTS_POSS_ON_CHILD (ALL_FSNOTIFY_PERM_EVENTS | \
FS_ACCESS | FS_MODIFY | FS_ATTRIB | \ FS_ACCESS | FS_MODIFY | FS_ATTRIB | \
FS_CLOSE_WRITE | FS_CLOSE_NOWRITE | \ FS_CLOSE_WRITE | FS_CLOSE_NOWRITE | \
FS_OPEN | FS_OPEN_EXEC) FS_OPEN | FS_OPEN_EXEC)
/*
* This is a list of all events that may get sent with the parent inode as the
* @to_tell argument of fsnotify().
* It may include events that can be sent to an inode/sb/mount mark, but cannot
* be sent to a parent watching children.
*/
#define FS_EVENTS_POSS_TO_PARENT (FS_EVENTS_POSS_ON_CHILD)
/* Events that can be reported to backends */ /* Events that can be reported to backends */
#define ALL_FSNOTIFY_EVENTS (ALL_FSNOTIFY_DIRENT_EVENTS | \ #define ALL_FSNOTIFY_EVENTS (ALL_FSNOTIFY_DIRENT_EVENTS | \
FS_EVENTS_POSS_ON_CHILD | \ FS_EVENTS_POSS_ON_CHILD | \
...@@ -108,18 +117,41 @@ struct mem_cgroup; ...@@ -108,18 +117,41 @@ struct mem_cgroup;
* these operations for each relevant group. * these operations for each relevant group.
* *
* handle_event - main call for a group to handle an fs event * handle_event - main call for a group to handle an fs event
* @group: group to notify
* @mask: event type and flags
* @data: object that event happened on
* @data_type: type of object for fanotify_data_XXX() accessors
* @dir: optional directory associated with event -
* if @file_name is not NULL, this is the directory that
* @file_name is relative to
* @file_name: optional file name associated with event
* @cookie: inotify rename cookie
* @iter_info: array of marks from this group that are interested in the event
*
* handle_inode_event - simple variant of handle_event() for groups that only
* have inode marks and don't have ignore mask
* @mark: mark to notify
* @mask: event type and flags
* @inode: inode that event happened on
* @dir: optional directory associated with event -
* if @file_name is not NULL, this is the directory that
* @file_name is relative to.
* @file_name: optional file name associated with event
*
* free_group_priv - called when a group refcnt hits 0 to clean up the private union * free_group_priv - called when a group refcnt hits 0 to clean up the private union
* freeing_mark - called when a mark is being destroyed for some reason. The group * freeing_mark - called when a mark is being destroyed for some reason. The group
* MUST be holding a reference on each mark and that reference must be * MUST be holding a reference on each mark and that reference must be
* dropped in this function. inotify uses this function to send * dropped in this function. inotify uses this function to send
* userspace messages that marks have been removed. * userspace messages that marks have been removed.
*/ */
struct fsnotify_ops { struct fsnotify_ops {
int (*handle_event)(struct fsnotify_group *group, int (*handle_event)(struct fsnotify_group *group, u32 mask,
struct inode *inode, const void *data, int data_type, struct inode *dir,
u32 mask, const void *data, int data_type,
const struct qstr *file_name, u32 cookie, const struct qstr *file_name, u32 cookie,
struct fsnotify_iter_info *iter_info); struct fsnotify_iter_info *iter_info);
int (*handle_inode_event)(struct fsnotify_mark *mark, u32 mask,
struct inode *inode, struct inode *dir,
const struct qstr *file_name);
void (*free_group_priv)(struct fsnotify_group *group); void (*free_group_priv)(struct fsnotify_group *group);
void (*freeing_mark)(struct fsnotify_mark *mark, struct fsnotify_group *group); void (*freeing_mark)(struct fsnotify_mark *mark, struct fsnotify_group *group);
void (*free_event)(struct fsnotify_event *event); void (*free_event)(struct fsnotify_event *event);
...@@ -220,12 +252,11 @@ enum fsnotify_data_type { ...@@ -220,12 +252,11 @@ enum fsnotify_data_type {
FSNOTIFY_EVENT_INODE, FSNOTIFY_EVENT_INODE,
}; };
static inline const struct inode *fsnotify_data_inode(const void *data, static inline struct inode *fsnotify_data_inode(const void *data, int data_type)
int data_type)
{ {
switch (data_type) { switch (data_type) {
case FSNOTIFY_EVENT_INODE: case FSNOTIFY_EVENT_INODE:
return data; return (struct inode *)data;
case FSNOTIFY_EVENT_PATH: case FSNOTIFY_EVENT_PATH:
return d_inode(((const struct path *)data)->dentry); return d_inode(((const struct path *)data)->dentry);
default: default:
...@@ -246,6 +277,7 @@ static inline const struct path *fsnotify_data_path(const void *data, ...@@ -246,6 +277,7 @@ static inline const struct path *fsnotify_data_path(const void *data,
enum fsnotify_obj_type { enum fsnotify_obj_type {
FSNOTIFY_OBJ_TYPE_INODE, FSNOTIFY_OBJ_TYPE_INODE,
FSNOTIFY_OBJ_TYPE_CHILD,
FSNOTIFY_OBJ_TYPE_VFSMOUNT, FSNOTIFY_OBJ_TYPE_VFSMOUNT,
FSNOTIFY_OBJ_TYPE_SB, FSNOTIFY_OBJ_TYPE_SB,
FSNOTIFY_OBJ_TYPE_COUNT, FSNOTIFY_OBJ_TYPE_COUNT,
...@@ -253,6 +285,7 @@ enum fsnotify_obj_type { ...@@ -253,6 +285,7 @@ enum fsnotify_obj_type {
}; };
#define FSNOTIFY_OBJ_TYPE_INODE_FL (1U << FSNOTIFY_OBJ_TYPE_INODE) #define FSNOTIFY_OBJ_TYPE_INODE_FL (1U << FSNOTIFY_OBJ_TYPE_INODE)
#define FSNOTIFY_OBJ_TYPE_CHILD_FL (1U << FSNOTIFY_OBJ_TYPE_CHILD)
#define FSNOTIFY_OBJ_TYPE_VFSMOUNT_FL (1U << FSNOTIFY_OBJ_TYPE_VFSMOUNT) #define FSNOTIFY_OBJ_TYPE_VFSMOUNT_FL (1U << FSNOTIFY_OBJ_TYPE_VFSMOUNT)
#define FSNOTIFY_OBJ_TYPE_SB_FL (1U << FSNOTIFY_OBJ_TYPE_SB) #define FSNOTIFY_OBJ_TYPE_SB_FL (1U << FSNOTIFY_OBJ_TYPE_SB)
#define FSNOTIFY_OBJ_ALL_TYPES_MASK ((1U << FSNOTIFY_OBJ_TYPE_COUNT) - 1) #define FSNOTIFY_OBJ_ALL_TYPES_MASK ((1U << FSNOTIFY_OBJ_TYPE_COUNT) - 1)
...@@ -297,6 +330,7 @@ static inline struct fsnotify_mark *fsnotify_iter_##name##_mark( \ ...@@ -297,6 +330,7 @@ static inline struct fsnotify_mark *fsnotify_iter_##name##_mark( \
} }
FSNOTIFY_ITER_FUNCS(inode, INODE) FSNOTIFY_ITER_FUNCS(inode, INODE)
FSNOTIFY_ITER_FUNCS(child, CHILD)
FSNOTIFY_ITER_FUNCS(vfsmount, VFSMOUNT) FSNOTIFY_ITER_FUNCS(vfsmount, VFSMOUNT)
FSNOTIFY_ITER_FUNCS(sb, SB) FSNOTIFY_ITER_FUNCS(sb, SB)
...@@ -377,15 +411,29 @@ struct fsnotify_mark { ...@@ -377,15 +411,29 @@ struct fsnotify_mark {
/* called from the vfs helpers */ /* called from the vfs helpers */
/* main fsnotify call to send events */ /* main fsnotify call to send events */
extern int fsnotify(struct inode *to_tell, __u32 mask, const void *data, extern int fsnotify(__u32 mask, const void *data, int data_type,
int data_type, const struct qstr *name, u32 cookie); struct inode *dir, const struct qstr *name,
extern int fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data, struct inode *inode, u32 cookie);
extern int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data,
int data_type); int data_type);
extern void __fsnotify_inode_delete(struct inode *inode); extern void __fsnotify_inode_delete(struct inode *inode);
extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt); extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt);
extern void fsnotify_sb_delete(struct super_block *sb); extern void fsnotify_sb_delete(struct super_block *sb);
extern u32 fsnotify_get_cookie(void); extern u32 fsnotify_get_cookie(void);
static inline __u32 fsnotify_parent_needed_mask(__u32 mask)
{
/* FS_EVENT_ON_CHILD is set on marks that want parent/name info */
if (!(mask & FS_EVENT_ON_CHILD))
return 0;
/*
* This object might be watched by a mark that cares about parent/name
* info, does it care about the specific set of events that can be
* reported with parent/name info?
*/
return mask & FS_EVENTS_POSS_TO_PARENT;
}
static inline int fsnotify_inode_watches_children(struct inode *inode) static inline int fsnotify_inode_watches_children(struct inode *inode)
{ {
/* FS_EVENT_ON_CHILD is set if the inode may care */ /* FS_EVENT_ON_CHILD is set if the inode may care */
...@@ -535,13 +583,14 @@ static inline void fsnotify_init_event(struct fsnotify_event *event, ...@@ -535,13 +583,14 @@ static inline void fsnotify_init_event(struct fsnotify_event *event,
#else #else
static inline int fsnotify(struct inode *to_tell, __u32 mask, const void *data, static inline int fsnotify(__u32 mask, const void *data, int data_type,
int data_type, const struct qstr *name, u32 cookie) struct inode *dir, const struct qstr *name,
struct inode *inode, u32 cookie)
{ {
return 0; return 0;
} }
static inline int fsnotify_parent(struct dentry *dentry, __u32 mask, static inline int __fsnotify_parent(struct dentry *dentry, __u32 mask,
const void *data, int data_type) const void *data, int data_type)
{ {
return 0; return 0;
......
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
#define FAN_OPEN_PERM 0x00010000 /* File open in perm check */ #define FAN_OPEN_PERM 0x00010000 /* File open in perm check */
#define FAN_ACCESS_PERM 0x00020000 /* File accessed in perm check */ #define FAN_ACCESS_PERM 0x00020000 /* File accessed in perm check */
#define FAN_OPEN_EXEC_PERM 0x00040000 /* File open/exec in perm check */ #define FAN_OPEN_EXEC_PERM 0x00040000 /* File open/exec in perm check */
#define FAN_DIR_MODIFY 0x00080000 /* Directory entry was modified */
#define FAN_EVENT_ON_CHILD 0x08000000 /* Interested in child events */ #define FAN_EVENT_ON_CHILD 0x08000000 /* Interested in child events */
...@@ -54,6 +53,11 @@ ...@@ -54,6 +53,11 @@
/* Flags to determine fanotify event format */ /* Flags to determine fanotify event format */
#define FAN_REPORT_TID 0x00000100 /* event->pid is thread id */ #define FAN_REPORT_TID 0x00000100 /* event->pid is thread id */
#define FAN_REPORT_FID 0x00000200 /* Report unique file id */ #define FAN_REPORT_FID 0x00000200 /* Report unique file id */
#define FAN_REPORT_DIR_FID 0x00000400 /* Report unique directory id */
#define FAN_REPORT_NAME 0x00000800 /* Report events with name */
/* Convenience macro - FAN_REPORT_NAME requires FAN_REPORT_DIR_FID */
#define FAN_REPORT_DFID_NAME (FAN_REPORT_DIR_FID | FAN_REPORT_NAME)
/* Deprecated - do not use this in programs and do not add new flags here! */ /* Deprecated - do not use this in programs and do not add new flags here! */
#define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK | \ #define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK | \
...@@ -118,6 +122,7 @@ struct fanotify_event_metadata { ...@@ -118,6 +122,7 @@ struct fanotify_event_metadata {
#define FAN_EVENT_INFO_TYPE_FID 1 #define FAN_EVENT_INFO_TYPE_FID 1
#define FAN_EVENT_INFO_TYPE_DFID_NAME 2 #define FAN_EVENT_INFO_TYPE_DFID_NAME 2
#define FAN_EVENT_INFO_TYPE_DFID 3
/* Variable length info record following event metadata */ /* Variable length info record following event metadata */
struct fanotify_event_info_header { struct fanotify_event_info_header {
...@@ -127,10 +132,11 @@ struct fanotify_event_info_header { ...@@ -127,10 +132,11 @@ struct fanotify_event_info_header {
}; };
/* /*
* Unique file identifier info record. This is used both for * Unique file identifier info record.
* FAN_EVENT_INFO_TYPE_FID records and for FAN_EVENT_INFO_TYPE_DFID_NAME * This structure is used for records of types FAN_EVENT_INFO_TYPE_FID,
* records. For FAN_EVENT_INFO_TYPE_DFID_NAME there is additionally a null * FAN_EVENT_INFO_TYPE_DFID and FAN_EVENT_INFO_TYPE_DFID_NAME.
* terminated name immediately after the file handle. * For FAN_EVENT_INFO_TYPE_DFID_NAME there is additionally a null terminated
* name immediately after the file handle.
*/ */
struct fanotify_event_info_fid { struct fanotify_event_info_fid {
struct fanotify_event_info_header hdr; struct fanotify_event_info_header hdr;
......
...@@ -36,7 +36,7 @@ static struct fsnotify_group *audit_fsnotify_group; ...@@ -36,7 +36,7 @@ static struct fsnotify_group *audit_fsnotify_group;
/* fsnotify events we care about. */ /* fsnotify events we care about. */
#define AUDIT_FS_EVENTS (FS_MOVE | FS_CREATE | FS_DELETE | FS_DELETE_SELF |\ #define AUDIT_FS_EVENTS (FS_MOVE | FS_CREATE | FS_DELETE | FS_DELETE_SELF |\
FS_MOVE_SELF | FS_EVENT_ON_CHILD) FS_MOVE_SELF)
static void audit_fsnotify_mark_free(struct audit_fsnotify_mark *audit_mark) static void audit_fsnotify_mark_free(struct audit_fsnotify_mark *audit_mark)
{ {
...@@ -152,35 +152,31 @@ static void audit_autoremove_mark_rule(struct audit_fsnotify_mark *audit_mark) ...@@ -152,35 +152,31 @@ static void audit_autoremove_mark_rule(struct audit_fsnotify_mark *audit_mark)
} }
/* Update mark data in audit rules based on fsnotify events. */ /* Update mark data in audit rules based on fsnotify events. */
static int audit_mark_handle_event(struct fsnotify_group *group, static int audit_mark_handle_event(struct fsnotify_mark *inode_mark, u32 mask,
struct inode *to_tell, struct inode *inode, struct inode *dir,
u32 mask, const void *data, int data_type, const struct qstr *dname)
const struct qstr *dname, u32 cookie,
struct fsnotify_iter_info *iter_info)
{ {
struct fsnotify_mark *inode_mark = fsnotify_iter_inode_mark(iter_info);
struct audit_fsnotify_mark *audit_mark; struct audit_fsnotify_mark *audit_mark;
const struct inode *inode = fsnotify_data_inode(data, data_type);
audit_mark = container_of(inode_mark, struct audit_fsnotify_mark, mark); audit_mark = container_of(inode_mark, struct audit_fsnotify_mark, mark);
BUG_ON(group != audit_fsnotify_group); if (WARN_ON_ONCE(inode_mark->group != audit_fsnotify_group) ||
WARN_ON_ONCE(!inode))
if (WARN_ON(!inode))
return 0; return 0;
if (mask & (FS_CREATE|FS_MOVED_TO|FS_DELETE|FS_MOVED_FROM)) { if (mask & (FS_CREATE|FS_MOVED_TO|FS_DELETE|FS_MOVED_FROM)) {
if (audit_compare_dname_path(dname, audit_mark->path, AUDIT_NAME_FULL)) if (audit_compare_dname_path(dname, audit_mark->path, AUDIT_NAME_FULL))
return 0; return 0;
audit_update_mark(audit_mark, inode); audit_update_mark(audit_mark, inode);
} else if (mask & (FS_DELETE_SELF|FS_UNMOUNT|FS_MOVE_SELF)) } else if (mask & (FS_DELETE_SELF|FS_UNMOUNT|FS_MOVE_SELF)) {
audit_autoremove_mark_rule(audit_mark); audit_autoremove_mark_rule(audit_mark);
}
return 0; return 0;
} }
static const struct fsnotify_ops audit_mark_fsnotify_ops = { static const struct fsnotify_ops audit_mark_fsnotify_ops = {
.handle_event = audit_mark_handle_event, .handle_inode_event = audit_mark_handle_event,
.free_mark = audit_fsnotify_free_mark, .free_mark = audit_fsnotify_free_mark,
}; };
......
...@@ -1035,11 +1035,9 @@ static void evict_chunk(struct audit_chunk *chunk) ...@@ -1035,11 +1035,9 @@ static void evict_chunk(struct audit_chunk *chunk)
audit_schedule_prune(); audit_schedule_prune();
} }
static int audit_tree_handle_event(struct fsnotify_group *group, static int audit_tree_handle_event(struct fsnotify_mark *mark, u32 mask,
struct inode *to_tell, struct inode *inode, struct inode *dir,
u32 mask, const void *data, int data_type, const struct qstr *file_name)
const struct qstr *file_name, u32 cookie,
struct fsnotify_iter_info *iter_info)
{ {
return 0; return 0;
} }
...@@ -1068,7 +1066,7 @@ static void audit_tree_freeing_mark(struct fsnotify_mark *mark, ...@@ -1068,7 +1066,7 @@ static void audit_tree_freeing_mark(struct fsnotify_mark *mark,
} }
static const struct fsnotify_ops audit_tree_ops = { static const struct fsnotify_ops audit_tree_ops = {
.handle_event = audit_tree_handle_event, .handle_inode_event = audit_tree_handle_event,
.freeing_mark = audit_tree_freeing_mark, .freeing_mark = audit_tree_freeing_mark,
.free_mark = audit_tree_destroy_watch, .free_mark = audit_tree_destroy_watch,
}; };
......
...@@ -53,7 +53,7 @@ static struct fsnotify_group *audit_watch_group; ...@@ -53,7 +53,7 @@ static struct fsnotify_group *audit_watch_group;
/* fsnotify events we care about. */ /* fsnotify events we care about. */
#define AUDIT_FS_WATCH (FS_MOVE | FS_CREATE | FS_DELETE | FS_DELETE_SELF |\ #define AUDIT_FS_WATCH (FS_MOVE | FS_CREATE | FS_DELETE | FS_DELETE_SELF |\
FS_MOVE_SELF | FS_EVENT_ON_CHILD | FS_UNMOUNT) FS_MOVE_SELF | FS_UNMOUNT)
static void audit_free_parent(struct audit_parent *parent) static void audit_free_parent(struct audit_parent *parent)
{ {
...@@ -464,20 +464,17 @@ void audit_remove_watch_rule(struct audit_krule *krule) ...@@ -464,20 +464,17 @@ void audit_remove_watch_rule(struct audit_krule *krule)
} }
/* Update watch data in audit rules based on fsnotify events. */ /* Update watch data in audit rules based on fsnotify events. */
static int audit_watch_handle_event(struct fsnotify_group *group, static int audit_watch_handle_event(struct fsnotify_mark *inode_mark, u32 mask,
struct inode *to_tell, struct inode *inode, struct inode *dir,
u32 mask, const void *data, int data_type, const struct qstr *dname)
const struct qstr *dname, u32 cookie,
struct fsnotify_iter_info *iter_info)
{ {
struct fsnotify_mark *inode_mark = fsnotify_iter_inode_mark(iter_info);
const struct inode *inode = fsnotify_data_inode(data, data_type);
struct audit_parent *parent; struct audit_parent *parent;
parent = container_of(inode_mark, struct audit_parent, mark); parent = container_of(inode_mark, struct audit_parent, mark);
BUG_ON(group != audit_watch_group); if (WARN_ON_ONCE(inode_mark->group != audit_watch_group) ||
WARN_ON(!inode); WARN_ON_ONCE(!inode))
return 0;
if (mask & (FS_CREATE|FS_MOVED_TO) && inode) if (mask & (FS_CREATE|FS_MOVED_TO) && inode)
audit_update_watch(parent, dname, inode->i_sb->s_dev, inode->i_ino, 0); audit_update_watch(parent, dname, inode->i_sb->s_dev, inode->i_ino, 0);
...@@ -490,7 +487,7 @@ static int audit_watch_handle_event(struct fsnotify_group *group, ...@@ -490,7 +487,7 @@ static int audit_watch_handle_event(struct fsnotify_group *group,
} }
static const struct fsnotify_ops audit_watch_fsnotify_ops = { static const struct fsnotify_ops audit_watch_fsnotify_ops = {
.handle_event = audit_watch_handle_event, .handle_inode_event = audit_watch_handle_event,
.free_mark = audit_watch_free_mark, .free_mark = audit_watch_free_mark,
}; };
......
...@@ -1543,8 +1543,7 @@ static void latency_fsnotify_workfn(struct work_struct *work) ...@@ -1543,8 +1543,7 @@ static void latency_fsnotify_workfn(struct work_struct *work)
{ {
struct trace_array *tr = container_of(work, struct trace_array, struct trace_array *tr = container_of(work, struct trace_array,
fsnotify_work); fsnotify_work);
fsnotify(tr->d_max_latency->d_inode, FS_MODIFY, fsnotify_inode(tr->d_max_latency->d_inode, FS_MODIFY);
tr->d_max_latency->d_inode, FSNOTIFY_EVENT_INODE, NULL, 0);
} }
static void latency_fsnotify_workfn_irq(struct irq_work *iwork) static void latency_fsnotify_workfn_irq(struct irq_work *iwork)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment