Commit 5af9d1cf authored by Linus Torvalds's avatar Linus Torvalds

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

Pull fsnotify updates from Jan Kara:

 - reduce overhead of fsnotify infrastructure when no permission events
   are in use

 - a few small cleanups

* tag 'fsnotify_for_v6.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs:
  fsnotify: fix UAF from FS_ERROR event on a shutting down filesystem
  fsnotify: optimize the case of no permission event watchers
  fsnotify: use an enum for group priority constants
  fsnotify: move s_fsnotify_connectors into fsnotify_sb_info
  fsnotify: lazy attach fsnotify_sb_info state to sb
  fsnotify: create helper fsnotify_update_sb_watchers()
  fsnotify: pass object pointer and type to fsnotify mark helpers
  fanotify: merge two checks regarding add of ignore mark
  fsnotify: create a wrapper fsnotify_find_inode_mark()
  fsnotify: create helpers to get sb and connp from object
  fsnotify: rename fsnotify_{get,put}_sb_connectors()
  fsnotify: Avoid -Wflex-array-member-not-at-end warning
  fanotify: remove unneeded sub-zero check for unsigned value
parents daa12112 795bb82d
...@@ -159,8 +159,8 @@ nfsd_file_mark_find_or_create(struct nfsd_file *nf, struct inode *inode) ...@@ -159,8 +159,8 @@ nfsd_file_mark_find_or_create(struct nfsd_file *nf, struct inode *inode)
do { do {
fsnotify_group_lock(nfsd_file_fsnotify_group); fsnotify_group_lock(nfsd_file_fsnotify_group);
mark = fsnotify_find_mark(&inode->i_fsnotify_marks, mark = fsnotify_find_inode_mark(inode,
nfsd_file_fsnotify_group); nfsd_file_fsnotify_group);
if (mark) { if (mark) {
nfm = nfsd_file_mark_get(container_of(mark, nfm = nfsd_file_mark_get(container_of(mark,
struct nfsd_file_mark, struct nfsd_file_mark,
......
...@@ -162,7 +162,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id) ...@@ -162,7 +162,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id)
if (!S_ISDIR(inode->i_mode)) if (!S_ISDIR(inode->i_mode))
return; return;
fsn_mark = fsnotify_find_mark(&inode->i_fsnotify_marks, dnotify_group); fsn_mark = fsnotify_find_inode_mark(inode, dnotify_group);
if (!fsn_mark) if (!fsn_mark)
return; return;
dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark);
...@@ -326,7 +326,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned int arg) ...@@ -326,7 +326,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned int arg)
fsnotify_group_lock(dnotify_group); fsnotify_group_lock(dnotify_group);
/* add the new_fsn_mark or find an old one. */ /* add the new_fsn_mark or find an old one. */
fsn_mark = fsnotify_find_mark(&inode->i_fsnotify_marks, dnotify_group); fsn_mark = fsnotify_find_inode_mark(inode, dnotify_group);
if (fsn_mark) { if (fsn_mark) {
dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark);
spin_lock(&fsn_mark->lock); spin_lock(&fsn_mark->lock);
......
...@@ -502,7 +502,7 @@ static int copy_fid_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh, ...@@ -502,7 +502,7 @@ static int copy_fid_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
} }
/* Pad with 0's */ /* Pad with 0's */
WARN_ON_ONCE(len < 0 || len >= FANOTIFY_EVENT_ALIGN); WARN_ON_ONCE(len >= FANOTIFY_EVENT_ALIGN);
if (len > 0 && clear_user(buf, len)) if (len > 0 && clear_user(buf, len))
return -EFAULT; return -EFAULT;
...@@ -1076,7 +1076,7 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, ...@@ -1076,7 +1076,7 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark,
} }
static int fanotify_remove_mark(struct fsnotify_group *group, static int fanotify_remove_mark(struct fsnotify_group *group,
fsnotify_connp_t *connp, __u32 mask, void *obj, unsigned int obj_type, __u32 mask,
unsigned int flags, __u32 umask) unsigned int flags, __u32 umask)
{ {
struct fsnotify_mark *fsn_mark = NULL; struct fsnotify_mark *fsn_mark = NULL;
...@@ -1084,7 +1084,7 @@ static int fanotify_remove_mark(struct fsnotify_group *group, ...@@ -1084,7 +1084,7 @@ static int fanotify_remove_mark(struct fsnotify_group *group,
int destroy_mark; int destroy_mark;
fsnotify_group_lock(group); fsnotify_group_lock(group);
fsn_mark = fsnotify_find_mark(connp, group); fsn_mark = fsnotify_find_mark(obj, obj_type, group);
if (!fsn_mark) { if (!fsn_mark) {
fsnotify_group_unlock(group); fsnotify_group_unlock(group);
return -ENOENT; return -ENOENT;
...@@ -1105,30 +1105,6 @@ static int fanotify_remove_mark(struct fsnotify_group *group, ...@@ -1105,30 +1105,6 @@ static int fanotify_remove_mark(struct fsnotify_group *group,
return 0; return 0;
} }
static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group,
struct vfsmount *mnt, __u32 mask,
unsigned int flags, __u32 umask)
{
return fanotify_remove_mark(group, &real_mount(mnt)->mnt_fsnotify_marks,
mask, flags, umask);
}
static int fanotify_remove_sb_mark(struct fsnotify_group *group,
struct super_block *sb, __u32 mask,
unsigned int flags, __u32 umask)
{
return fanotify_remove_mark(group, &sb->s_fsnotify_marks, mask,
flags, umask);
}
static int fanotify_remove_inode_mark(struct fsnotify_group *group,
struct inode *inode, __u32 mask,
unsigned int flags, __u32 umask)
{
return fanotify_remove_mark(group, &inode->i_fsnotify_marks, mask,
flags, umask);
}
static bool fanotify_mark_update_flags(struct fsnotify_mark *fsn_mark, static bool fanotify_mark_update_flags(struct fsnotify_mark *fsn_mark,
unsigned int fan_flags) unsigned int fan_flags)
{ {
...@@ -1249,7 +1225,7 @@ static int fanotify_set_mark_fsid(struct fsnotify_group *group, ...@@ -1249,7 +1225,7 @@ static int fanotify_set_mark_fsid(struct fsnotify_group *group,
} }
static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group, static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
fsnotify_connp_t *connp, void *obj,
unsigned int obj_type, unsigned int obj_type,
unsigned int fan_flags, unsigned int fan_flags,
struct fan_fsid *fsid) struct fan_fsid *fsid)
...@@ -1288,7 +1264,7 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group, ...@@ -1288,7 +1264,7 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
fan_mark->fsid.val[0] = fan_mark->fsid.val[1] = 0; fan_mark->fsid.val[0] = fan_mark->fsid.val[1] = 0;
} }
ret = fsnotify_add_mark_locked(mark, connp, obj_type, 0); ret = fsnotify_add_mark_locked(mark, obj, obj_type, 0);
if (ret) if (ret)
goto out_put_mark; goto out_put_mark;
...@@ -1344,7 +1320,7 @@ static int fanotify_may_update_existing_mark(struct fsnotify_mark *fsn_mark, ...@@ -1344,7 +1320,7 @@ static int fanotify_may_update_existing_mark(struct fsnotify_mark *fsn_mark,
} }
static int fanotify_add_mark(struct fsnotify_group *group, static int fanotify_add_mark(struct fsnotify_group *group,
fsnotify_connp_t *connp, unsigned int obj_type, void *obj, unsigned int obj_type,
__u32 mask, unsigned int fan_flags, __u32 mask, unsigned int fan_flags,
struct fan_fsid *fsid) struct fan_fsid *fsid)
{ {
...@@ -1353,9 +1329,9 @@ static int fanotify_add_mark(struct fsnotify_group *group, ...@@ -1353,9 +1329,9 @@ static int fanotify_add_mark(struct fsnotify_group *group,
int ret = 0; int ret = 0;
fsnotify_group_lock(group); fsnotify_group_lock(group);
fsn_mark = fsnotify_find_mark(connp, group); fsn_mark = fsnotify_find_mark(obj, obj_type, group);
if (!fsn_mark) { if (!fsn_mark) {
fsn_mark = fanotify_add_new_mark(group, connp, obj_type, fsn_mark = fanotify_add_new_mark(group, obj, obj_type,
fan_flags, fsid); fan_flags, fsid);
if (IS_ERR(fsn_mark)) { if (IS_ERR(fsn_mark)) {
fsnotify_group_unlock(group); fsnotify_group_unlock(group);
...@@ -1392,42 +1368,6 @@ static int fanotify_add_mark(struct fsnotify_group *group, ...@@ -1392,42 +1368,6 @@ static int fanotify_add_mark(struct fsnotify_group *group,
return ret; return ret;
} }
static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
struct vfsmount *mnt, __u32 mask,
unsigned int flags, struct fan_fsid *fsid)
{
return fanotify_add_mark(group, &real_mount(mnt)->mnt_fsnotify_marks,
FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags, fsid);
}
static int fanotify_add_sb_mark(struct fsnotify_group *group,
struct super_block *sb, __u32 mask,
unsigned int flags, struct fan_fsid *fsid)
{
return fanotify_add_mark(group, &sb->s_fsnotify_marks,
FSNOTIFY_OBJ_TYPE_SB, mask, flags, fsid);
}
static int fanotify_add_inode_mark(struct fsnotify_group *group,
struct inode *inode, __u32 mask,
unsigned int flags, struct fan_fsid *fsid)
{
pr_debug("%s: group=%p inode=%p\n", __func__, group, inode);
/*
* If some other task has this inode open for write we should not add
* an ignore mask, unless that ignore mask is supposed to survive
* modification changes anyway.
*/
if ((flags & FANOTIFY_MARK_IGNORE_BITS) &&
!(flags & FAN_MARK_IGNORED_SURV_MODIFY) &&
inode_is_open_for_write(inode))
return 0;
return fanotify_add_mark(group, &inode->i_fsnotify_marks,
FSNOTIFY_OBJ_TYPE_INODE, mask, flags, fsid);
}
static struct fsnotify_event *fanotify_alloc_overflow_event(void) static struct fsnotify_event *fanotify_alloc_overflow_event(void)
{ {
struct fanotify_event *oevent; struct fanotify_event *oevent;
...@@ -1576,13 +1516,13 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) ...@@ -1576,13 +1516,13 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
INIT_LIST_HEAD(&group->fanotify_data.access_list); INIT_LIST_HEAD(&group->fanotify_data.access_list);
switch (class) { switch (class) {
case FAN_CLASS_NOTIF: case FAN_CLASS_NOTIF:
group->priority = FS_PRIO_0; group->priority = FSNOTIFY_PRIO_NORMAL;
break; break;
case FAN_CLASS_CONTENT: case FAN_CLASS_CONTENT:
group->priority = FS_PRIO_1; group->priority = FSNOTIFY_PRIO_CONTENT;
break; break;
case FAN_CLASS_PRE_CONTENT: case FAN_CLASS_PRE_CONTENT:
group->priority = FS_PRIO_2; group->priority = FSNOTIFY_PRIO_PRE_CONTENT;
break; break;
default: default:
fd = -EINVAL; fd = -EINVAL;
...@@ -1750,6 +1690,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, ...@@ -1750,6 +1690,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS; unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS;
unsigned int ignore = flags & FANOTIFY_MARK_IGNORE_BITS; unsigned int ignore = flags & FANOTIFY_MARK_IGNORE_BITS;
unsigned int obj_type, fid_mode; unsigned int obj_type, fid_mode;
void *obj;
u32 umask = 0; u32 umask = 0;
int ret; int ret;
...@@ -1833,12 +1774,11 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, ...@@ -1833,12 +1774,11 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
goto fput_and_out; goto fput_and_out;
/* /*
* group->priority == FS_PRIO_0 == FAN_CLASS_NOTIF. These are not * Permission events require minimum priority FAN_CLASS_CONTENT.
* allowed to set permissions events.
*/ */
ret = -EINVAL; ret = -EINVAL;
if (mask & FANOTIFY_PERM_EVENTS && if (mask & FANOTIFY_PERM_EVENTS &&
group->priority == FS_PRIO_0) group->priority < FSNOTIFY_PRIO_CONTENT)
goto fput_and_out; goto fput_and_out;
if (mask & FAN_FS_ERROR && if (mask & FAN_FS_ERROR &&
...@@ -1908,17 +1848,34 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, ...@@ -1908,17 +1848,34 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
} }
/* inode held in place by reference to path; group by fget on fd */ /* inode held in place by reference to path; group by fget on fd */
if (mark_type == FAN_MARK_INODE) if (mark_type == FAN_MARK_INODE) {
inode = path.dentry->d_inode; inode = path.dentry->d_inode;
else obj = inode;
} else {
mnt = path.mnt; mnt = path.mnt;
if (mark_type == FAN_MARK_MOUNT)
obj = mnt;
else
obj = mnt->mnt_sb;
}
ret = mnt ? -EINVAL : -EISDIR; /*
/* FAN_MARK_IGNORE requires SURV_MODIFY for sb/mount/dir marks */ * If some other task has this inode open for write we should not add
if (mark_cmd == FAN_MARK_ADD && ignore == FAN_MARK_IGNORE && * an ignore mask, unless that ignore mask is supposed to survive
(mnt || S_ISDIR(inode->i_mode)) && * modification changes anyway.
!(flags & FAN_MARK_IGNORED_SURV_MODIFY)) */
goto path_put_and_out; if (mark_cmd == FAN_MARK_ADD && (flags & FANOTIFY_MARK_IGNORE_BITS) &&
!(flags & FAN_MARK_IGNORED_SURV_MODIFY)) {
ret = mnt ? -EINVAL : -EISDIR;
/* FAN_MARK_IGNORE requires SURV_MODIFY for sb/mount/dir marks */
if (ignore == FAN_MARK_IGNORE &&
(mnt || S_ISDIR(inode->i_mode)))
goto path_put_and_out;
ret = 0;
if (inode && inode_is_open_for_write(inode))
goto path_put_and_out;
}
/* Mask out FAN_EVENT_ON_CHILD flag for sb/mount/non-dir marks */ /* Mask out FAN_EVENT_ON_CHILD flag for sb/mount/non-dir marks */
if (mnt || !S_ISDIR(inode->i_mode)) { if (mnt || !S_ISDIR(inode->i_mode)) {
...@@ -1936,26 +1893,12 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, ...@@ -1936,26 +1893,12 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
/* create/update an inode mark */ /* create/update an inode mark */
switch (mark_cmd) { switch (mark_cmd) {
case FAN_MARK_ADD: case FAN_MARK_ADD:
if (mark_type == FAN_MARK_MOUNT) ret = fanotify_add_mark(group, obj, obj_type, mask, flags,
ret = fanotify_add_vfsmount_mark(group, mnt, mask, fsid);
flags, fsid);
else if (mark_type == FAN_MARK_FILESYSTEM)
ret = fanotify_add_sb_mark(group, mnt->mnt_sb, mask,
flags, fsid);
else
ret = fanotify_add_inode_mark(group, inode, mask,
flags, fsid);
break; break;
case FAN_MARK_REMOVE: case FAN_MARK_REMOVE:
if (mark_type == FAN_MARK_MOUNT) ret = fanotify_remove_mark(group, obj, obj_type, mask, flags,
ret = fanotify_remove_vfsmount_mark(group, mnt, mask, umask);
flags, umask);
else if (mark_type == FAN_MARK_FILESYSTEM)
ret = fanotify_remove_sb_mark(group, mnt->mnt_sb, mask,
flags, umask);
else
ret = fanotify_remove_inode_mark(group, inode, mask,
flags, umask);
break; break;
default: default:
ret = -EINVAL; ret = -EINVAL;
......
...@@ -41,29 +41,25 @@ static void show_fdinfo(struct seq_file *m, struct file *f, ...@@ -41,29 +41,25 @@ static void show_fdinfo(struct seq_file *m, struct file *f,
#if defined(CONFIG_EXPORTFS) #if defined(CONFIG_EXPORTFS)
static void show_mark_fhandle(struct seq_file *m, struct inode *inode) static void show_mark_fhandle(struct seq_file *m, struct inode *inode)
{ {
struct { DEFINE_FLEX(struct file_handle, f, f_handle, handle_bytes, MAX_HANDLE_SZ);
struct file_handle handle;
u8 pad[MAX_HANDLE_SZ];
} f;
int size, ret, i; int size, ret, i;
f.handle.handle_bytes = sizeof(f.pad); size = f->handle_bytes >> 2;
size = f.handle.handle_bytes >> 2;
ret = exportfs_encode_fid(inode, (struct fid *)f.handle.f_handle, &size); ret = exportfs_encode_fid(inode, (struct fid *)f->f_handle, &size);
if ((ret == FILEID_INVALID) || (ret < 0)) { if ((ret == FILEID_INVALID) || (ret < 0)) {
WARN_ONCE(1, "Can't encode file handler for inotify: %d\n", ret); WARN_ONCE(1, "Can't encode file handler for inotify: %d\n", ret);
return; return;
} }
f.handle.handle_type = ret; f->handle_type = ret;
f.handle.handle_bytes = size * sizeof(u32); f->handle_bytes = size * sizeof(u32);
seq_printf(m, "fhandle-bytes:%x fhandle-type:%x f_handle:", seq_printf(m, "fhandle-bytes:%x fhandle-type:%x f_handle:",
f.handle.handle_bytes, f.handle.handle_type); f->handle_bytes, f->handle_type);
for (i = 0; i < f.handle.handle_bytes; i++) for (i = 0; i < f->handle_bytes; i++)
seq_printf(m, "%02x", (int)f.handle.f_handle[i]); seq_printf(m, "%02x", (int)f->f_handle[i]);
} }
#else #else
static void show_mark_fhandle(struct seq_file *m, struct inode *inode) static void show_mark_fhandle(struct seq_file *m, struct inode *inode)
......
...@@ -89,11 +89,25 @@ static void fsnotify_unmount_inodes(struct super_block *sb) ...@@ -89,11 +89,25 @@ static void fsnotify_unmount_inodes(struct super_block *sb)
void fsnotify_sb_delete(struct super_block *sb) void fsnotify_sb_delete(struct super_block *sb)
{ {
struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(sb);
/* Were any marks ever added to any object on this sb? */
if (!sbinfo)
return;
fsnotify_unmount_inodes(sb); fsnotify_unmount_inodes(sb);
fsnotify_clear_marks_by_sb(sb); fsnotify_clear_marks_by_sb(sb);
/* Wait for outstanding object references from connectors */ /* Wait for outstanding object references from connectors */
wait_var_event(&sb->s_fsnotify_connectors, wait_var_event(fsnotify_sb_watched_objects(sb),
!atomic_long_read(&sb->s_fsnotify_connectors)); !atomic_long_read(fsnotify_sb_watched_objects(sb)));
WARN_ON(fsnotify_sb_has_priority_watchers(sb, FSNOTIFY_PRIO_CONTENT));
WARN_ON(fsnotify_sb_has_priority_watchers(sb,
FSNOTIFY_PRIO_PRE_CONTENT));
}
void fsnotify_sb_free(struct super_block *sb)
{
kfree(sb->s_fsnotify_info);
} }
/* /*
...@@ -489,6 +503,7 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir, ...@@ -489,6 +503,7 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
{ {
const struct path *path = fsnotify_data_path(data, data_type); const struct path *path = fsnotify_data_path(data, data_type);
struct super_block *sb = fsnotify_data_sb(data, data_type); struct super_block *sb = fsnotify_data_sb(data, data_type);
struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(sb);
struct fsnotify_iter_info iter_info = {}; struct fsnotify_iter_info iter_info = {};
struct mount *mnt = NULL; struct mount *mnt = NULL;
struct inode *inode2 = NULL; struct inode *inode2 = NULL;
...@@ -525,7 +540,7 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir, ...@@ -525,7 +540,7 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
* SRCU because we have no references to any objects and do not * SRCU because we have no references to any objects and do not
* need SRCU to keep them "alive". * need SRCU to keep them "alive".
*/ */
if (!sb->s_fsnotify_marks && if ((!sbinfo || !sbinfo->sb_marks) &&
(!mnt || !mnt->mnt_fsnotify_marks) && (!mnt || !mnt->mnt_fsnotify_marks) &&
(!inode || !inode->i_fsnotify_marks) && (!inode || !inode->i_fsnotify_marks) &&
(!inode2 || !inode2->i_fsnotify_marks)) (!inode2 || !inode2->i_fsnotify_marks))
...@@ -552,8 +567,10 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir, ...@@ -552,8 +567,10 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
iter_info.srcu_idx = srcu_read_lock(&fsnotify_mark_srcu); iter_info.srcu_idx = srcu_read_lock(&fsnotify_mark_srcu);
iter_info.marks[FSNOTIFY_ITER_TYPE_SB] = if (sbinfo) {
fsnotify_first_mark(&sb->s_fsnotify_marks); iter_info.marks[FSNOTIFY_ITER_TYPE_SB] =
fsnotify_first_mark(&sbinfo->sb_marks);
}
if (mnt) { if (mnt) {
iter_info.marks[FSNOTIFY_ITER_TYPE_VFSMOUNT] = iter_info.marks[FSNOTIFY_ITER_TYPE_VFSMOUNT] =
fsnotify_first_mark(&mnt->mnt_fsnotify_marks); fsnotify_first_mark(&mnt->mnt_fsnotify_marks);
......
...@@ -9,39 +9,58 @@ ...@@ -9,39 +9,58 @@
#include "../mount.h" #include "../mount.h"
/*
* fsnotify_connp_t is what we embed in objects which connector can be attached
* to.
*/
typedef struct fsnotify_mark_connector __rcu *fsnotify_connp_t;
static inline struct inode *fsnotify_conn_inode( static inline struct inode *fsnotify_conn_inode(
struct fsnotify_mark_connector *conn) struct fsnotify_mark_connector *conn)
{ {
return container_of(conn->obj, struct inode, i_fsnotify_marks); return conn->obj;
} }
static inline struct mount *fsnotify_conn_mount( static inline struct mount *fsnotify_conn_mount(
struct fsnotify_mark_connector *conn) struct fsnotify_mark_connector *conn)
{ {
return container_of(conn->obj, struct mount, mnt_fsnotify_marks); return real_mount(conn->obj);
} }
static inline struct super_block *fsnotify_conn_sb( static inline struct super_block *fsnotify_conn_sb(
struct fsnotify_mark_connector *conn) struct fsnotify_mark_connector *conn)
{ {
return container_of(conn->obj, struct super_block, s_fsnotify_marks); return conn->obj;
} }
static inline struct super_block *fsnotify_connector_sb( static inline struct super_block *fsnotify_object_sb(void *obj,
struct fsnotify_mark_connector *conn) enum fsnotify_obj_type obj_type)
{ {
switch (conn->type) { switch (obj_type) {
case FSNOTIFY_OBJ_TYPE_INODE: case FSNOTIFY_OBJ_TYPE_INODE:
return fsnotify_conn_inode(conn)->i_sb; return ((struct inode *)obj)->i_sb;
case FSNOTIFY_OBJ_TYPE_VFSMOUNT: case FSNOTIFY_OBJ_TYPE_VFSMOUNT:
return fsnotify_conn_mount(conn)->mnt.mnt_sb; return ((struct vfsmount *)obj)->mnt_sb;
case FSNOTIFY_OBJ_TYPE_SB: case FSNOTIFY_OBJ_TYPE_SB:
return fsnotify_conn_sb(conn); return (struct super_block *)obj;
default: default:
return NULL; return NULL;
} }
} }
static inline struct super_block *fsnotify_connector_sb(
struct fsnotify_mark_connector *conn)
{
return fsnotify_object_sb(conn->obj, conn->type);
}
static inline fsnotify_connp_t *fsnotify_sb_marks(struct super_block *sb)
{
struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(sb);
return sbinfo ? &sbinfo->sb_marks : NULL;
}
/* destroy all events sitting in this groups notification queue */ /* destroy all events sitting in this groups notification queue */
extern void fsnotify_flush_notify(struct fsnotify_group *group); extern void fsnotify_flush_notify(struct fsnotify_group *group);
...@@ -67,7 +86,7 @@ static inline void fsnotify_clear_marks_by_mount(struct vfsmount *mnt) ...@@ -67,7 +86,7 @@ static inline void fsnotify_clear_marks_by_mount(struct vfsmount *mnt)
/* run the list of all marks associated with sb and destroy them */ /* run the list of all marks associated with sb and destroy them */
static inline void fsnotify_clear_marks_by_sb(struct super_block *sb) static inline void fsnotify_clear_marks_by_sb(struct super_block *sb)
{ {
fsnotify_destroy_marks(&sb->s_fsnotify_marks); fsnotify_destroy_marks(fsnotify_sb_marks(sb));
} }
/* /*
......
...@@ -544,7 +544,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, ...@@ -544,7 +544,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;
fsn_mark = fsnotify_find_mark(&inode->i_fsnotify_marks, group); fsn_mark = fsnotify_find_inode_mark(inode, group);
if (!fsn_mark) if (!fsn_mark)
return -ENOENT; return -ENOENT;
else if (create) { else if (create) {
......
...@@ -97,6 +97,21 @@ void fsnotify_get_mark(struct fsnotify_mark *mark) ...@@ -97,6 +97,21 @@ void fsnotify_get_mark(struct fsnotify_mark *mark)
refcount_inc(&mark->refcnt); refcount_inc(&mark->refcnt);
} }
static fsnotify_connp_t *fsnotify_object_connp(void *obj,
enum fsnotify_obj_type obj_type)
{
switch (obj_type) {
case FSNOTIFY_OBJ_TYPE_INODE:
return &((struct inode *)obj)->i_fsnotify_marks;
case FSNOTIFY_OBJ_TYPE_VFSMOUNT:
return &real_mount(obj)->mnt_fsnotify_marks;
case FSNOTIFY_OBJ_TYPE_SB:
return fsnotify_sb_marks(obj);
default:
return NULL;
}
}
static __u32 *fsnotify_conn_mask_p(struct fsnotify_mark_connector *conn) static __u32 *fsnotify_conn_mask_p(struct fsnotify_mark_connector *conn)
{ {
if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) if (conn->type == FSNOTIFY_OBJ_TYPE_INODE)
...@@ -116,10 +131,69 @@ __u32 fsnotify_conn_mask(struct fsnotify_mark_connector *conn) ...@@ -116,10 +131,69 @@ __u32 fsnotify_conn_mask(struct fsnotify_mark_connector *conn)
return *fsnotify_conn_mask_p(conn); return *fsnotify_conn_mask_p(conn);
} }
static void fsnotify_get_sb_watched_objects(struct super_block *sb)
{
atomic_long_inc(fsnotify_sb_watched_objects(sb));
}
static void fsnotify_put_sb_watched_objects(struct super_block *sb)
{
if (atomic_long_dec_and_test(fsnotify_sb_watched_objects(sb)))
wake_up_var(fsnotify_sb_watched_objects(sb));
}
static void fsnotify_get_inode_ref(struct inode *inode) static void fsnotify_get_inode_ref(struct inode *inode)
{ {
ihold(inode); ihold(inode);
atomic_long_inc(&inode->i_sb->s_fsnotify_connectors); fsnotify_get_sb_watched_objects(inode->i_sb);
}
static void fsnotify_put_inode_ref(struct inode *inode)
{
fsnotify_put_sb_watched_objects(inode->i_sb);
iput(inode);
}
/*
* Grab or drop watched objects reference depending on whether the connector
* is attached and has any marks attached.
*/
static void fsnotify_update_sb_watchers(struct super_block *sb,
struct fsnotify_mark_connector *conn)
{
struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(sb);
bool is_watched = conn->flags & FSNOTIFY_CONN_FLAG_IS_WATCHED;
struct fsnotify_mark *first_mark = NULL;
unsigned int highest_prio = 0;
if (conn->obj)
first_mark = hlist_entry_safe(conn->list.first,
struct fsnotify_mark, obj_list);
if (first_mark)
highest_prio = first_mark->group->priority;
if (WARN_ON(highest_prio >= __FSNOTIFY_PRIO_NUM))
highest_prio = 0;
/*
* If the highest priority of group watching this object is prio,
* then watched object has a reference on counters [0..prio].
* Update priority >= 1 watched objects counters.
*/
for (unsigned int p = conn->prio + 1; p <= highest_prio; p++)
atomic_long_inc(&sbinfo->watched_objects[p]);
for (unsigned int p = conn->prio; p > highest_prio; p--)
atomic_long_dec(&sbinfo->watched_objects[p]);
conn->prio = highest_prio;
/* Update priority >= 0 (a.k.a total) watched objects counter */
BUILD_BUG_ON(FSNOTIFY_PRIO_NORMAL != 0);
if (first_mark && !is_watched) {
conn->flags |= FSNOTIFY_CONN_FLAG_IS_WATCHED;
fsnotify_get_sb_watched_objects(sb);
} else if (!first_mark && is_watched) {
conn->flags &= ~FSNOTIFY_CONN_FLAG_IS_WATCHED;
fsnotify_put_sb_watched_objects(sb);
}
} }
/* /*
...@@ -213,35 +287,12 @@ static void fsnotify_connector_destroy_workfn(struct work_struct *work) ...@@ -213,35 +287,12 @@ static void fsnotify_connector_destroy_workfn(struct work_struct *work)
} }
} }
static void fsnotify_put_inode_ref(struct inode *inode)
{
struct super_block *sb = inode->i_sb;
iput(inode);
if (atomic_long_dec_and_test(&sb->s_fsnotify_connectors))
wake_up_var(&sb->s_fsnotify_connectors);
}
static void fsnotify_get_sb_connectors(struct fsnotify_mark_connector *conn)
{
struct super_block *sb = fsnotify_connector_sb(conn);
if (sb)
atomic_long_inc(&sb->s_fsnotify_connectors);
}
static void fsnotify_put_sb_connectors(struct fsnotify_mark_connector *conn)
{
struct super_block *sb = fsnotify_connector_sb(conn);
if (sb && atomic_long_dec_and_test(&sb->s_fsnotify_connectors))
wake_up_var(&sb->s_fsnotify_connectors);
}
static void *fsnotify_detach_connector_from_object( static void *fsnotify_detach_connector_from_object(
struct fsnotify_mark_connector *conn, struct fsnotify_mark_connector *conn,
unsigned int *type) unsigned int *type)
{ {
fsnotify_connp_t *connp = fsnotify_object_connp(conn->obj, conn->type);
struct super_block *sb = fsnotify_connector_sb(conn);
struct inode *inode = NULL; struct inode *inode = NULL;
*type = conn->type; *type = conn->type;
...@@ -261,10 +312,10 @@ static void *fsnotify_detach_connector_from_object( ...@@ -261,10 +312,10 @@ static void *fsnotify_detach_connector_from_object(
fsnotify_conn_sb(conn)->s_fsnotify_mask = 0; fsnotify_conn_sb(conn)->s_fsnotify_mask = 0;
} }
fsnotify_put_sb_connectors(conn); rcu_assign_pointer(*connp, NULL);
rcu_assign_pointer(*(conn->obj), NULL);
conn->obj = NULL; conn->obj = NULL;
conn->type = FSNOTIFY_OBJ_TYPE_DETACHED; conn->type = FSNOTIFY_OBJ_TYPE_DETACHED;
fsnotify_update_sb_watchers(sb, conn);
return inode; return inode;
} }
...@@ -316,6 +367,11 @@ void fsnotify_put_mark(struct fsnotify_mark *mark) ...@@ -316,6 +367,11 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
objp = fsnotify_detach_connector_from_object(conn, &type); objp = fsnotify_detach_connector_from_object(conn, &type);
free_conn = true; free_conn = true;
} else { } else {
struct super_block *sb = fsnotify_connector_sb(conn);
/* Update watched objects after detaching mark */
if (sb)
fsnotify_update_sb_watchers(sb, conn);
objp = __fsnotify_recalc_mask(conn); objp = __fsnotify_recalc_mask(conn);
type = conn->type; type = conn->type;
} }
...@@ -536,8 +592,28 @@ int fsnotify_compare_groups(struct fsnotify_group *a, struct fsnotify_group *b) ...@@ -536,8 +592,28 @@ int fsnotify_compare_groups(struct fsnotify_group *a, struct fsnotify_group *b)
return -1; return -1;
} }
static int fsnotify_attach_info_to_sb(struct super_block *sb)
{
struct fsnotify_sb_info *sbinfo;
/* sb info is freed on fsnotify_sb_delete() */
sbinfo = kzalloc(sizeof(*sbinfo), GFP_KERNEL);
if (!sbinfo)
return -ENOMEM;
/*
* cmpxchg() provides the barrier so that callers of fsnotify_sb_info()
* will observe an initialized structure
*/
if (cmpxchg(&sb->s_fsnotify_info, NULL, sbinfo)) {
/* Someone else created sbinfo for us */
kfree(sbinfo);
}
return 0;
}
static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp, static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
unsigned int obj_type) void *obj, unsigned int obj_type)
{ {
struct fsnotify_mark_connector *conn; struct fsnotify_mark_connector *conn;
...@@ -547,10 +623,9 @@ static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp, ...@@ -547,10 +623,9 @@ static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
spin_lock_init(&conn->lock); spin_lock_init(&conn->lock);
INIT_HLIST_HEAD(&conn->list); INIT_HLIST_HEAD(&conn->list);
conn->flags = 0; conn->flags = 0;
conn->prio = 0;
conn->type = obj_type; conn->type = obj_type;
conn->obj = connp; conn->obj = obj;
conn->flags = 0;
fsnotify_get_sb_connectors(conn);
/* /*
* cmpxchg() provides the barrier so that readers of *connp can see * cmpxchg() provides the barrier so that readers of *connp can see
...@@ -558,10 +633,8 @@ static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp, ...@@ -558,10 +633,8 @@ static int fsnotify_attach_connector_to_object(fsnotify_connp_t *connp,
*/ */
if (cmpxchg(connp, NULL, conn)) { if (cmpxchg(connp, NULL, conn)) {
/* Someone else created list structure for us */ /* Someone else created list structure for us */
fsnotify_put_sb_connectors(conn);
kmem_cache_free(fsnotify_mark_connector_cachep, conn); kmem_cache_free(fsnotify_mark_connector_cachep, conn);
} }
return 0; return 0;
} }
...@@ -598,24 +671,36 @@ static struct fsnotify_mark_connector *fsnotify_grab_connector( ...@@ -598,24 +671,36 @@ static struct fsnotify_mark_connector *fsnotify_grab_connector(
* to which group and for which inodes. These marks are ordered according to * to which group and for which inodes. These marks are ordered according to
* priority, highest number first, and then by the group's location in memory. * priority, highest number first, and then by the group's location in memory.
*/ */
static int fsnotify_add_mark_list(struct fsnotify_mark *mark, static int fsnotify_add_mark_list(struct fsnotify_mark *mark, void *obj,
fsnotify_connp_t *connp,
unsigned int obj_type, int add_flags) unsigned int obj_type, int add_flags)
{ {
struct super_block *sb = fsnotify_object_sb(obj, obj_type);
struct fsnotify_mark *lmark, *last = NULL; struct fsnotify_mark *lmark, *last = NULL;
struct fsnotify_mark_connector *conn; struct fsnotify_mark_connector *conn;
fsnotify_connp_t *connp;
int cmp; int cmp;
int err = 0; int err = 0;
if (WARN_ON(!fsnotify_valid_obj_type(obj_type))) if (WARN_ON(!fsnotify_valid_obj_type(obj_type)))
return -EINVAL; return -EINVAL;
/*
* Attach the sb info before attaching a connector to any object on sb.
* The sb info will remain attached as long as sb lives.
*/
if (!fsnotify_sb_info(sb)) {
err = fsnotify_attach_info_to_sb(sb);
if (err)
return err;
}
connp = fsnotify_object_connp(obj, obj_type);
restart: restart:
spin_lock(&mark->lock); spin_lock(&mark->lock);
conn = fsnotify_grab_connector(connp); conn = fsnotify_grab_connector(connp);
if (!conn) { if (!conn) {
spin_unlock(&mark->lock); spin_unlock(&mark->lock);
err = fsnotify_attach_connector_to_object(connp, obj_type); err = fsnotify_attach_connector_to_object(connp, obj, obj_type);
if (err) if (err)
return err; return err;
goto restart; goto restart;
...@@ -649,6 +734,7 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark, ...@@ -649,6 +734,7 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
/* mark should be the last entry. last is the current last entry */ /* mark should be the last entry. last is the current last entry */
hlist_add_behind_rcu(&mark->obj_list, &last->obj_list); hlist_add_behind_rcu(&mark->obj_list, &last->obj_list);
added: added:
fsnotify_update_sb_watchers(sb, conn);
/* /*
* Since connector is attached to object using cmpxchg() we are * Since connector is attached to object using cmpxchg() we are
* guaranteed that connector initialization is fully visible by anyone * guaranteed that connector initialization is fully visible by anyone
...@@ -667,7 +753,7 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark, ...@@ -667,7 +753,7 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark,
* event types should be delivered to which group. * event types should be delivered to which group.
*/ */
int fsnotify_add_mark_locked(struct fsnotify_mark *mark, int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
fsnotify_connp_t *connp, unsigned int obj_type, void *obj, unsigned int obj_type,
int add_flags) int add_flags)
{ {
struct fsnotify_group *group = mark->group; struct fsnotify_group *group = mark->group;
...@@ -688,7 +774,7 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark, ...@@ -688,7 +774,7 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
fsnotify_get_mark(mark); /* for g_list */ fsnotify_get_mark(mark); /* for g_list */
spin_unlock(&mark->lock); spin_unlock(&mark->lock);
ret = fsnotify_add_mark_list(mark, connp, obj_type, add_flags); ret = fsnotify_add_mark_list(mark, obj, obj_type, add_flags);
if (ret) if (ret)
goto err; goto err;
...@@ -706,14 +792,14 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark, ...@@ -706,14 +792,14 @@ int fsnotify_add_mark_locked(struct fsnotify_mark *mark,
return ret; return ret;
} }
int fsnotify_add_mark(struct fsnotify_mark *mark, fsnotify_connp_t *connp, int fsnotify_add_mark(struct fsnotify_mark *mark, void *obj,
unsigned int obj_type, int add_flags) unsigned int obj_type, int add_flags)
{ {
int ret; int ret;
struct fsnotify_group *group = mark->group; struct fsnotify_group *group = mark->group;
fsnotify_group_lock(group); fsnotify_group_lock(group);
ret = fsnotify_add_mark_locked(mark, connp, obj_type, add_flags); ret = fsnotify_add_mark_locked(mark, obj, obj_type, add_flags);
fsnotify_group_unlock(group); fsnotify_group_unlock(group);
return ret; return ret;
} }
...@@ -723,12 +809,16 @@ EXPORT_SYMBOL_GPL(fsnotify_add_mark); ...@@ -723,12 +809,16 @@ EXPORT_SYMBOL_GPL(fsnotify_add_mark);
* Given a list of marks, find the mark associated with given group. If found * Given a list of marks, find the mark associated with given group. If found
* take a reference to that mark and return it, else return NULL. * take a reference to that mark and return it, else return NULL.
*/ */
struct fsnotify_mark *fsnotify_find_mark(fsnotify_connp_t *connp, struct fsnotify_mark *fsnotify_find_mark(void *obj, unsigned int obj_type,
struct fsnotify_group *group) struct fsnotify_group *group)
{ {
fsnotify_connp_t *connp = fsnotify_object_connp(obj, obj_type);
struct fsnotify_mark_connector *conn; struct fsnotify_mark_connector *conn;
struct fsnotify_mark *mark; struct fsnotify_mark *mark;
if (!connp)
return NULL;
conn = fsnotify_grab_connector(connp); conn = fsnotify_grab_connector(connp);
if (!conn) if (!conn)
return NULL; return NULL;
......
...@@ -274,6 +274,7 @@ static void destroy_super_work(struct work_struct *work) ...@@ -274,6 +274,7 @@ static void destroy_super_work(struct work_struct *work)
{ {
struct super_block *s = container_of(work, struct super_block, struct super_block *s = container_of(work, struct super_block,
destroy_work); destroy_work);
fsnotify_sb_free(s);
security_sb_free(s); security_sb_free(s);
put_user_ns(s->s_user_ns); put_user_ns(s->s_user_ns);
kfree(s->s_subtype); kfree(s->s_subtype);
......
...@@ -73,6 +73,8 @@ struct fscrypt_inode_info; ...@@ -73,6 +73,8 @@ struct fscrypt_inode_info;
struct fscrypt_operations; struct fscrypt_operations;
struct fsverity_info; struct fsverity_info;
struct fsverity_operations; struct fsverity_operations;
struct fsnotify_mark_connector;
struct fsnotify_sb_info;
struct fs_context; struct fs_context;
struct fs_parameter_spec; struct fs_parameter_spec;
struct fileattr; struct fileattr;
...@@ -618,8 +620,6 @@ is_uncached_acl(struct posix_acl *acl) ...@@ -618,8 +620,6 @@ is_uncached_acl(struct posix_acl *acl)
#define IOP_XATTR 0x0008 #define IOP_XATTR 0x0008
#define IOP_DEFAULT_READLINK 0x0010 #define IOP_DEFAULT_READLINK 0x0010
struct fsnotify_mark_connector;
/* /*
* Keep mostly read-only and often accessed (especially for * Keep mostly read-only and often accessed (especially for
* the RCU path lookup and 'stat' data) fields at the beginning * the RCU path lookup and 'stat' data) fields at the beginning
...@@ -1248,7 +1248,7 @@ struct super_block { ...@@ -1248,7 +1248,7 @@ struct super_block {
/* /*
* Keep s_fs_info, s_time_gran, s_fsnotify_mask, and * Keep s_fs_info, s_time_gran, s_fsnotify_mask, and
* s_fsnotify_marks together for cache efficiency. They are frequently * s_fsnotify_info together for cache efficiency. They are frequently
* accessed and rarely modified. * accessed and rarely modified.
*/ */
void *s_fs_info; /* Filesystem private info */ void *s_fs_info; /* Filesystem private info */
...@@ -1260,7 +1260,7 @@ struct super_block { ...@@ -1260,7 +1260,7 @@ struct super_block {
time64_t s_time_max; time64_t s_time_max;
#ifdef CONFIG_FSNOTIFY #ifdef CONFIG_FSNOTIFY
__u32 s_fsnotify_mask; __u32 s_fsnotify_mask;
struct fsnotify_mark_connector __rcu *s_fsnotify_marks; struct fsnotify_sb_info *s_fsnotify_info;
#endif #endif
/* /*
...@@ -1301,12 +1301,6 @@ struct super_block { ...@@ -1301,12 +1301,6 @@ struct super_block {
/* Number of inodes with nlink == 0 but still referenced */ /* Number of inodes with nlink == 0 but still referenced */
atomic_long_t s_remove_count; atomic_long_t s_remove_count;
/*
* Number of inode/mount/sb objects that are being watched, note that
* inodes objects are currently double-accounted.
*/
atomic_long_t s_fsnotify_connectors;
/* Read-only state of the superblock is being changed */ /* Read-only state of the superblock is being changed */
int s_readonly_remount; int s_readonly_remount;
......
...@@ -17,10 +17,23 @@ ...@@ -17,10 +17,23 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/bug.h> #include <linux/bug.h>
/* Are there any inode/mount/sb objects watched with priority prio or above? */
static inline bool fsnotify_sb_has_priority_watchers(struct super_block *sb,
int prio)
{
struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(sb);
/* Were any marks ever added to any object on this sb? */
if (!sbinfo)
return false;
return atomic_long_read(&sbinfo->watched_objects[prio]);
}
/* Are there any inode/mount/sb objects that are being watched at all? */ /* Are there any inode/mount/sb objects that are being watched at all? */
static inline bool fsnotify_sb_has_watchers(struct super_block *sb) static inline bool fsnotify_sb_has_watchers(struct super_block *sb)
{ {
return atomic_long_read(&sb->s_fsnotify_connectors); return fsnotify_sb_has_priority_watchers(sb, 0);
} }
/* /*
...@@ -103,6 +116,12 @@ static inline int fsnotify_file(struct file *file, __u32 mask) ...@@ -103,6 +116,12 @@ static inline int fsnotify_file(struct file *file, __u32 mask)
return 0; return 0;
path = &file->f_path; path = &file->f_path;
/* Permission events require group prio >= FSNOTIFY_PRIO_CONTENT */
if (mask & ALL_FSNOTIFY_PERM_EVENTS &&
!fsnotify_sb_has_priority_watchers(path->dentry->d_sb,
FSNOTIFY_PRIO_CONTENT))
return 0;
return fsnotify_parent(path->dentry, mask, path, FSNOTIFY_EVENT_PATH); return fsnotify_parent(path->dentry, mask, path, FSNOTIFY_EVENT_PATH);
} }
......
...@@ -176,6 +176,17 @@ struct fsnotify_event { ...@@ -176,6 +176,17 @@ struct fsnotify_event {
struct list_head list; struct list_head list;
}; };
/*
* fsnotify group priorities.
* Events are sent in order from highest priority to lowest priority.
*/
enum fsnotify_group_prio {
FSNOTIFY_PRIO_NORMAL = 0, /* normal notifiers, no permissions */
FSNOTIFY_PRIO_CONTENT, /* fanotify permission events */
FSNOTIFY_PRIO_PRE_CONTENT, /* fanotify pre-content events */
__FSNOTIFY_PRIO_NUM
};
/* /*
* A group is a "thing" that wants to receive notification about filesystem * A group is a "thing" that wants to receive notification about filesystem
* events. The mask holds the subset of event types this group cares about. * events. The mask holds the subset of event types this group cares about.
...@@ -201,14 +212,7 @@ struct fsnotify_group { ...@@ -201,14 +212,7 @@ struct fsnotify_group {
wait_queue_head_t notification_waitq; /* read() on the notification file blocks on this waitq */ 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 q_len; /* events on the queue */
unsigned int max_events; /* maximum events allowed on the list */ unsigned int max_events; /* maximum events allowed on the list */
/* enum fsnotify_group_prio priority; /* priority for sending events */
* 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;
bool shutdown; /* group is being shut down, don't queue more events */ bool shutdown; /* group is being shut down, don't queue more events */
#define FSNOTIFY_GROUP_USER 0x01 /* user allocated group */ #define FSNOTIFY_GROUP_USER 0x01 /* user allocated group */
...@@ -456,13 +460,6 @@ FSNOTIFY_ITER_FUNCS(sb, SB) ...@@ -456,13 +460,6 @@ FSNOTIFY_ITER_FUNCS(sb, SB)
type < FSNOTIFY_ITER_TYPE_COUNT; \ type < FSNOTIFY_ITER_TYPE_COUNT; \
type++) type++)
/*
* fsnotify_connp_t is what we embed in objects which connector can be attached
* to. fsnotify_connp_t * is how we refer from connector back to object.
*/
struct fsnotify_mark_connector;
typedef struct fsnotify_mark_connector __rcu *fsnotify_connp_t;
/* /*
* Inode/vfsmount/sb point to this structure which tracks all marks attached to * Inode/vfsmount/sb point to this structure which tracks all marks attached to
* the inode/vfsmount/sb. The reference to inode/vfsmount/sb is held by this * the inode/vfsmount/sb. The reference to inode/vfsmount/sb is held by this
...@@ -471,18 +468,51 @@ typedef struct fsnotify_mark_connector __rcu *fsnotify_connp_t; ...@@ -471,18 +468,51 @@ typedef struct fsnotify_mark_connector __rcu *fsnotify_connp_t;
*/ */
struct fsnotify_mark_connector { struct fsnotify_mark_connector {
spinlock_t lock; spinlock_t lock;
unsigned short type; /* Type of object [lock] */ unsigned char type; /* Type of object [lock] */
unsigned char prio; /* Highest priority group */
#define FSNOTIFY_CONN_FLAG_IS_WATCHED 0x01
#define FSNOTIFY_CONN_FLAG_HAS_IREF 0x02 #define FSNOTIFY_CONN_FLAG_HAS_IREF 0x02
unsigned short flags; /* flags [lock] */ unsigned short flags; /* flags [lock] */
union { union {
/* Object pointer [lock] */ /* Object pointer [lock] */
fsnotify_connp_t *obj; void *obj;
/* Used listing heads to free after srcu period expires */ /* Used listing heads to free after srcu period expires */
struct fsnotify_mark_connector *destroy_next; struct fsnotify_mark_connector *destroy_next;
}; };
struct hlist_head list; struct hlist_head list;
}; };
/*
* Container for per-sb fsnotify state (sb marks and more).
* Attached lazily on first marked object on the sb and freed when killing sb.
*/
struct fsnotify_sb_info {
struct fsnotify_mark_connector __rcu *sb_marks;
/*
* Number of inode/mount/sb objects that are being watched in this sb.
* Note that inodes objects are currently double-accounted.
*
* The value in watched_objects[prio] is the number of objects that are
* watched by groups of priority >= prio, so watched_objects[0] is the
* total number of watched objects in this sb.
*/
atomic_long_t watched_objects[__FSNOTIFY_PRIO_NUM];
};
static inline struct fsnotify_sb_info *fsnotify_sb_info(struct super_block *sb)
{
#ifdef CONFIG_FSNOTIFY
return READ_ONCE(sb->s_fsnotify_info);
#else
return NULL;
#endif
}
static inline atomic_long_t *fsnotify_sb_watched_objects(struct super_block *sb)
{
return &fsnotify_sb_info(sb)->watched_objects[0];
}
/* /*
* A mark is simply an object attached to an in core inode which allows an * A mark is simply an object attached to an in core inode which allows an
* fsnotify listener to indicate they are either no longer interested in events * fsnotify listener to indicate they are either no longer interested in events
...@@ -546,6 +576,7 @@ extern int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data ...@@ -546,6 +576,7 @@ extern int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data
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 void fsnotify_sb_free(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) static inline __u32 fsnotify_parent_needed_mask(__u32 mask)
...@@ -758,30 +789,35 @@ extern void fsnotify_recalc_mask(struct fsnotify_mark_connector *conn); ...@@ -758,30 +789,35 @@ extern void fsnotify_recalc_mask(struct fsnotify_mark_connector *conn);
extern void fsnotify_init_mark(struct fsnotify_mark *mark, extern void fsnotify_init_mark(struct fsnotify_mark *mark,
struct fsnotify_group *group); struct fsnotify_group *group);
/* Find mark belonging to given group in the list of marks */ /* Find mark belonging to given group in the list of marks */
extern struct fsnotify_mark *fsnotify_find_mark(fsnotify_connp_t *connp, struct fsnotify_mark *fsnotify_find_mark(void *obj, unsigned int obj_type,
struct fsnotify_group *group); struct fsnotify_group *group);
/* attach the mark to the object */ /* attach the mark to the object */
extern int fsnotify_add_mark(struct fsnotify_mark *mark, int fsnotify_add_mark(struct fsnotify_mark *mark, void *obj,
fsnotify_connp_t *connp, unsigned int obj_type, unsigned int obj_type, int add_flags);
int add_flags); int fsnotify_add_mark_locked(struct fsnotify_mark *mark, void *obj,
extern int fsnotify_add_mark_locked(struct fsnotify_mark *mark, unsigned int obj_type, int add_flags);
fsnotify_connp_t *connp,
unsigned int obj_type, int add_flags);
/* attach the mark to the inode */ /* attach the mark to the inode */
static inline int fsnotify_add_inode_mark(struct fsnotify_mark *mark, static inline int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
struct inode *inode, struct inode *inode,
int add_flags) int add_flags)
{ {
return fsnotify_add_mark(mark, &inode->i_fsnotify_marks, return fsnotify_add_mark(mark, inode, FSNOTIFY_OBJ_TYPE_INODE,
FSNOTIFY_OBJ_TYPE_INODE, add_flags); add_flags);
} }
static inline int fsnotify_add_inode_mark_locked(struct fsnotify_mark *mark, static inline int fsnotify_add_inode_mark_locked(struct fsnotify_mark *mark,
struct inode *inode, struct inode *inode,
int add_flags) int add_flags)
{ {
return fsnotify_add_mark_locked(mark, &inode->i_fsnotify_marks, return fsnotify_add_mark_locked(mark, inode, FSNOTIFY_OBJ_TYPE_INODE,
FSNOTIFY_OBJ_TYPE_INODE, add_flags); add_flags);
}
static inline struct fsnotify_mark *fsnotify_find_inode_mark(
struct inode *inode,
struct fsnotify_group *group)
{
return fsnotify_find_mark(inode, FSNOTIFY_OBJ_TYPE_INODE, group);
} }
/* given a group and a mark, flag mark to be freed when all references are dropped */ /* given a group and a mark, flag mark to be freed when all references are dropped */
...@@ -845,6 +881,9 @@ static inline void __fsnotify_vfsmount_delete(struct vfsmount *mnt) ...@@ -845,6 +881,9 @@ static inline void __fsnotify_vfsmount_delete(struct vfsmount *mnt)
static inline void fsnotify_sb_delete(struct super_block *sb) static inline void fsnotify_sb_delete(struct super_block *sb)
{} {}
static inline void fsnotify_sb_free(struct super_block *sb)
{}
static inline void fsnotify_update_flags(struct dentry *dentry) static inline void fsnotify_update_flags(struct dentry *dentry)
{} {}
......
...@@ -463,7 +463,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) ...@@ -463,7 +463,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree)
int n; int n;
fsnotify_group_lock(audit_tree_group); fsnotify_group_lock(audit_tree_group);
mark = fsnotify_find_mark(&inode->i_fsnotify_marks, audit_tree_group); mark = fsnotify_find_inode_mark(inode, audit_tree_group);
if (!mark) if (!mark)
return create_chunk(inode, tree); return create_chunk(inode, tree);
......
...@@ -90,7 +90,7 @@ static inline struct audit_parent *audit_find_parent(struct inode *inode) ...@@ -90,7 +90,7 @@ static inline struct audit_parent *audit_find_parent(struct inode *inode)
struct audit_parent *parent = NULL; struct audit_parent *parent = NULL;
struct fsnotify_mark *entry; struct fsnotify_mark *entry;
entry = fsnotify_find_mark(&inode->i_fsnotify_marks, audit_watch_group); entry = fsnotify_find_inode_mark(inode, audit_watch_group);
if (entry) if (entry)
parent = container_of(entry, struct audit_parent, mark); parent = container_of(entry, struct audit_parent, mark);
......
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