diff options
Diffstat (limited to 'fs/notify/fanotify')
-rw-r--r-- | fs/notify/fanotify/fanotify.c | 59 | ||||
-rw-r--r-- | fs/notify/fanotify/fanotify.h | 5 | ||||
-rw-r--r-- | fs/notify/fanotify/fanotify_user.c | 62 |
3 files changed, 80 insertions, 46 deletions
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index dd33227e518a..555831603637 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -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 */ @@ -182,14 +176,13 @@ 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; @@ -197,8 +190,7 @@ static int fanotify_encode_fid(struct fanotify_event *event, 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; @@ -207,8 +199,9 @@ out_err: } 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; @@ -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; @@ -262,6 +256,29 @@ out: 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, @@ -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); @@ -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)) { /* diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index 4aafc7144c3d..5b072afa4e19 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -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); diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 1638c171ca82..603419ce096f 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -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; @@ -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); @@ -678,7 +679,8 @@ 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; @@ -686,7 +688,7 @@ static int fanotify_add_mark(struct fsnotify_group *group, 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); @@ -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); @@ -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 */ @@ -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; @@ -861,9 +863,9 @@ out_destroy_group: } /* 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, @@ -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; /* @@ -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; /* @@ -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; @@ -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 */ @@ -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; |