Skip to content

Commit

Permalink
fanotify: cache fsid in fsnotify_mark_connector
Browse files Browse the repository at this point in the history
For FAN_REPORT_FID, we need to encode fid with fsid of the filesystem on
every event. To avoid having to call vfs_statfs() on every event to get
fsid, we store the fsid in fsnotify_mark_connector on the first time we
add a mark and on handle event we use the cached fsid.

Subsequent calls to add mark on the same object are expected to pass the
same fsid, so the call will fail on cached fsid mismatch.

If an event is reported on several mark types (inode, mount, filesystem),
all connectors should already have the same fsid, so we use the cached
fsid from the first connector.

[JK: Simplify code flow around fanotify_get_fid()
     make fsid argument of fsnotify_add_mark_locked() unconditional]

Suggested-by: Jan Kara <jack@suse.cz>
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>
  • Loading branch information
Amir Goldstein authored and Jan Kara committed Feb 7, 2019
1 parent a8b13aa commit 7711522
Show file tree
Hide file tree
Showing 5 changed files with 128 additions and 58 deletions.
59 changes: 40 additions & 19 deletions fs/notify/fanotify/fanotify.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,26 +153,20 @@ static u32 fanotify_group_event_mask(struct fsnotify_iter_info *iter_info,
}

static int fanotify_encode_fid(struct fanotify_event *event,
const struct path *path, gfp_t gfp)
struct inode *inode, gfp_t gfp,
__kernel_fsid_t *fsid)
{
struct fanotify_fid *fid = &event->fid;
int dwords, bytes = 0;
struct kstatfs stat;
int err, type;

stat.f_fsid.val[0] = stat.f_fsid.val[1] = 0;
fid->ext_fh = NULL;
dwords = 0;
err = -ENOENT;
type = exportfs_encode_inode_fh(d_inode(path->dentry), NULL, &dwords,
NULL);
type = exportfs_encode_inode_fh(inode, NULL, &dwords, NULL);
if (!dwords)
goto out_err;

err = vfs_statfs(path, &stat);
if (err)
goto out_err;

bytes = dwords << 2;
if (bytes > FANOTIFY_INLINE_FH_LEN) {
/* Treat failure to allocate fh as failure to allocate event */
Expand All @@ -182,23 +176,21 @@ static int fanotify_encode_fid(struct fanotify_event *event,
goto out_err;
}

type = exportfs_encode_inode_fh(d_inode(path->dentry),
fanotify_fid_fh(fid, bytes), &dwords,
NULL);
type = exportfs_encode_inode_fh(inode, fanotify_fid_fh(fid, bytes),
&dwords, NULL);
err = -EINVAL;
if (!type || type == FILEID_INVALID || bytes != dwords << 2)
goto out_err;

fid->fsid = stat.f_fsid;
fid->fsid = *fsid;
event->fh_len = bytes;

return type;

out_err:
pr_warn_ratelimited("fanotify: failed to encode fid (fsid=%x.%x, "
"type=%d, bytes=%d, err=%i)\n",
stat.f_fsid.val[0], stat.f_fsid.val[1],
type, bytes, err);
fsid->val[0], fsid->val[1], type, bytes, err);
kfree(fid->ext_fh);
fid->ext_fh = NULL;
event->fh_len = 0;
Expand All @@ -207,8 +199,9 @@ static int fanotify_encode_fid(struct fanotify_event *event,
}

struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
struct inode *inode, u32 mask,
const struct path *path)
struct inode *inode, u32 mask,
const struct path *path,
__kernel_fsid_t *fsid)
{
struct fanotify_event *event = NULL;
gfp_t gfp = GFP_KERNEL_ACCOUNT;
Expand Down Expand Up @@ -247,7 +240,8 @@ init: __maybe_unused
event->fh_len = 0;
if (path && FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
/* Report the event without a file identifier on encode error */
event->fh_type = fanotify_encode_fid(event, path, gfp);
event->fh_type = fanotify_encode_fid(event,
d_inode(path->dentry), gfp, fsid);
} else if (path) {
event->fh_type = FILEID_ROOT;
event->path = *path;
Expand All @@ -262,6 +256,29 @@ init: __maybe_unused
return event;
}

/*
* Get cached fsid of the filesystem containing the object from any connector.
* All connectors are supposed to have the same fsid, but we do not verify that
* here.
*/
static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info)
{
int type;
__kernel_fsid_t fsid = {};

fsnotify_foreach_obj_type(type) {
if (!fsnotify_iter_should_report_type(iter_info, type))
continue;

fsid = iter_info->marks[type]->connector->fsid;
if (WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1]))
continue;
return fsid;
}

return fsid;
}

static int fanotify_handle_event(struct fsnotify_group *group,
struct inode *inode,
u32 mask, const void *data, int data_type,
Expand All @@ -271,6 +288,7 @@ static int fanotify_handle_event(struct fsnotify_group *group,
int ret = 0;
struct fanotify_event *event;
struct fsnotify_event *fsn_event;
__kernel_fsid_t fsid = {};

BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS);
BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY);
Expand Down Expand Up @@ -303,7 +321,10 @@ static int fanotify_handle_event(struct fsnotify_group *group,
return 0;
}

event = fanotify_alloc_event(group, inode, mask, data);
if (FAN_GROUP_FLAG(group, FAN_REPORT_FID))
fsid = fanotify_get_fsid(iter_info);

event = fanotify_alloc_event(group, inode, mask, data, &fsid);
ret = -ENOMEM;
if (unlikely(!event)) {
/*
Expand Down
5 changes: 3 additions & 2 deletions fs/notify/fanotify/fanotify.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,5 +131,6 @@ static inline struct fanotify_event *FANOTIFY_E(struct fsnotify_event *fse)
}

struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
struct inode *inode, u32 mask,
const struct path *path);
struct inode *inode, u32 mask,
const struct path *path,
__kernel_fsid_t *fsid);
62 changes: 37 additions & 25 deletions fs/notify/fanotify/fanotify_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -653,7 +653,8 @@ static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,

static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
fsnotify_connp_t *connp,
unsigned int type)
unsigned int type,
__kernel_fsid_t *fsid)
{
struct fsnotify_mark *mark;
int ret;
Expand All @@ -666,7 +667,7 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
return ERR_PTR(-ENOMEM);

fsnotify_init_mark(mark, group);
ret = fsnotify_add_mark_locked(mark, connp, type, 0);
ret = fsnotify_add_mark_locked(mark, connp, type, 0, fsid);
if (ret) {
fsnotify_put_mark(mark);
return ERR_PTR(ret);
Expand All @@ -678,15 +679,16 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,

static int fanotify_add_mark(struct fsnotify_group *group,
fsnotify_connp_t *connp, unsigned int type,
__u32 mask, unsigned int flags)
__u32 mask, unsigned int flags,
__kernel_fsid_t *fsid)
{
struct fsnotify_mark *fsn_mark;
__u32 added;

mutex_lock(&group->mark_mutex);
fsn_mark = fsnotify_find_mark(connp, group);
if (!fsn_mark) {
fsn_mark = fanotify_add_new_mark(group, connp, type);
fsn_mark = fanotify_add_new_mark(group, connp, type, fsid);
if (IS_ERR(fsn_mark)) {
mutex_unlock(&group->mark_mutex);
return PTR_ERR(fsn_mark);
Expand All @@ -703,23 +705,23 @@ static int fanotify_add_mark(struct fsnotify_group *group,

static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
struct vfsmount *mnt, __u32 mask,
unsigned int flags)
unsigned int flags, __kernel_fsid_t *fsid)
{
return fanotify_add_mark(group, &real_mount(mnt)->mnt_fsnotify_marks,
FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags);
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 super_block *sb, __u32 mask,
unsigned int flags, __kernel_fsid_t *fsid)
{
return fanotify_add_mark(group, &sb->s_fsnotify_marks,
FSNOTIFY_OBJ_TYPE_SB, mask, flags);
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)
unsigned int flags, __kernel_fsid_t *fsid)
{
pr_debug("%s: group=%p inode=%p\n", __func__, group, inode);

Expand All @@ -734,7 +736,7 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group,
return 0;

return fanotify_add_mark(group, &inode->i_fsnotify_marks,
FSNOTIFY_OBJ_TYPE_INODE, mask, flags);
FSNOTIFY_OBJ_TYPE_INODE, mask, flags, fsid);
}

/* fanotify syscalls */
Expand Down Expand Up @@ -798,7 +800,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
atomic_inc(&user->fanotify_listeners);
group->memcg = get_mem_cgroup_from_mm(current->mm);

oevent = fanotify_alloc_event(group, NULL, FS_Q_OVERFLOW, NULL);
oevent = fanotify_alloc_event(group, NULL, FS_Q_OVERFLOW, NULL, NULL);
if (unlikely(!oevent)) {
fd = -ENOMEM;
goto out_destroy_group;
Expand Down Expand Up @@ -861,9 +863,9 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
}

/* Check if filesystem can encode a unique fid */
static int fanotify_test_fid(struct path *path)
static int fanotify_test_fid(struct path *path, struct kstatfs *stat)
{
struct kstatfs stat, root_stat;
struct kstatfs root_stat;
struct path root = {
.mnt = path->mnt,
.dentry = path->dentry->d_sb->s_root,
Expand All @@ -873,11 +875,11 @@ static int fanotify_test_fid(struct path *path)
/*
* Make sure path is not in filesystem with zero fsid (e.g. tmpfs).
*/
err = vfs_statfs(path, &stat);
err = vfs_statfs(path, stat);
if (err)
return err;

if (!stat.f_fsid.val[0] && !stat.f_fsid.val[1])
if (!stat->f_fsid.val[0] && !stat->f_fsid.val[1])
return -ENODEV;

/*
Expand All @@ -888,8 +890,8 @@ static int fanotify_test_fid(struct path *path)
if (err)
return err;

if (root_stat.f_fsid.val[0] != stat.f_fsid.val[0] ||
root_stat.f_fsid.val[1] != stat.f_fsid.val[1])
if (root_stat.f_fsid.val[0] != stat->f_fsid.val[0] ||
root_stat.f_fsid.val[1] != stat->f_fsid.val[1])
return -EXDEV;

/*
Expand All @@ -914,6 +916,8 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
struct fsnotify_group *group;
struct fd f;
struct path path;
struct kstatfs stat;
__kernel_fsid_t *fsid = NULL;
u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS;
unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
int ret;
Expand Down Expand Up @@ -992,9 +996,11 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
goto fput_and_out;

if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
ret = fanotify_test_fid(&path);
ret = fanotify_test_fid(&path, &stat);
if (ret)
goto path_put_and_out;

fsid = &stat.f_fsid;
}

/* inode held in place by reference to path; group by fget on fd */
Expand All @@ -1007,19 +1013,25 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) {
case FAN_MARK_ADD:
if (mark_type == FAN_MARK_MOUNT)
ret = fanotify_add_vfsmount_mark(group, mnt, mask, flags);
ret = fanotify_add_vfsmount_mark(group, mnt, mask,
flags, fsid);
else if (mark_type == FAN_MARK_FILESYSTEM)
ret = fanotify_add_sb_mark(group, mnt->mnt_sb, mask, flags);
ret = fanotify_add_sb_mark(group, mnt->mnt_sb, mask,
flags, fsid);
else
ret = fanotify_add_inode_mark(group, inode, mask, flags);
ret = fanotify_add_inode_mark(group, inode, mask,
flags, fsid);
break;
case FAN_MARK_REMOVE:
if (mark_type == FAN_MARK_MOUNT)
ret = fanotify_remove_vfsmount_mark(group, mnt, mask, flags);
ret = fanotify_remove_vfsmount_mark(group, mnt, mask,
flags);
else if (mark_type == FAN_MARK_FILESYSTEM)
ret = fanotify_remove_sb_mark(group, mnt->mnt_sb, mask, flags);
ret = fanotify_remove_sb_mark(group, mnt->mnt_sb, mask,
flags);
else
ret = fanotify_remove_inode_mark(group, inode, mask, flags);
ret = fanotify_remove_inode_mark(group, inode, mask,
flags);
break;
default:
ret = -EINVAL;
Expand Down
Loading

0 comments on commit 7711522

Please sign in to comment.