Commit cacfb956 authored by Amir Goldstein's avatar Amir Goldstein Committed by Jan Kara

fanotify: record name info for FAN_DIR_MODIFY event

For FAN_DIR_MODIFY event, allocate a variable size event struct to store
the dir entry name along side the directory file handle.

At this point, name info reporting is not yet implemented, so trying to
set FAN_DIR_MODIFY in mark mask will return -EINVAL.

Link: https://lore.kernel.org/r/20200319151022.31456-14-amir73il@gmail.comSigned-off-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent 01affd54
...@@ -53,6 +53,23 @@ static bool fanotify_fid_event_equal(struct fanotify_fid_event *ffe1, ...@@ -53,6 +53,23 @@ static bool fanotify_fid_event_equal(struct fanotify_fid_event *ffe1,
fanotify_fh_equal(&ffe1->object_fh, &ffe2->object_fh); fanotify_fh_equal(&ffe1->object_fh, &ffe2->object_fh);
} }
static bool fanotify_name_event_equal(struct fanotify_name_event *fne1,
struct fanotify_name_event *fne2)
{
/*
* Do not merge name events without dir fh.
* FAN_DIR_MODIFY does not encode object fh, so it may be empty.
*/
if (!fne1->dir_fh.len)
return false;
if (fne1->name_len != fne2->name_len ||
!fanotify_fh_equal(&fne1->dir_fh, &fne2->dir_fh))
return false;
return !memcmp(fne1->name, fne2->name, fne1->name_len);
}
static bool should_merge(struct fsnotify_event *old_fsn, static bool should_merge(struct fsnotify_event *old_fsn,
struct fsnotify_event *new_fsn) struct fsnotify_event *new_fsn)
{ {
...@@ -84,6 +101,9 @@ static bool should_merge(struct fsnotify_event *old_fsn, ...@@ -84,6 +101,9 @@ static bool should_merge(struct fsnotify_event *old_fsn,
return fanotify_fid_event_equal(FANOTIFY_FE(old), return fanotify_fid_event_equal(FANOTIFY_FE(old),
FANOTIFY_FE(new)); FANOTIFY_FE(new));
case FANOTIFY_EVENT_TYPE_FID_NAME:
return fanotify_name_event_equal(FANOTIFY_NE(old),
FANOTIFY_NE(new));
default: default:
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
} }
...@@ -262,6 +282,9 @@ static void fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode, ...@@ -262,6 +282,9 @@ static void fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
void *buf = fh->buf; void *buf = fh->buf;
int err; int err;
if (!inode)
goto out;
dwords = 0; dwords = 0;
err = -ENOENT; err = -ENOENT;
type = exportfs_encode_inode_fh(inode, NULL, &dwords, NULL); type = exportfs_encode_inode_fh(inode, NULL, &dwords, NULL);
...@@ -295,6 +318,7 @@ static void fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode, ...@@ -295,6 +318,7 @@ static void fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
type, bytes, err); type, bytes, err);
kfree(ext_buf); kfree(ext_buf);
*fanotify_fh_ext_buf_ptr(fh) = NULL; *fanotify_fh_ext_buf_ptr(fh) = NULL;
out:
/* Report the event without a file identifier on encode error */ /* Report the event without a file identifier on encode error */
fh->type = FILEID_INVALID; fh->type = FILEID_INVALID;
fh->len = 0; fh->len = 0;
...@@ -320,10 +344,12 @@ static struct inode *fanotify_fid_inode(struct inode *to_tell, u32 event_mask, ...@@ -320,10 +344,12 @@ static struct inode *fanotify_fid_inode(struct inode *to_tell, u32 event_mask,
struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group, struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
struct inode *inode, u32 mask, struct inode *inode, u32 mask,
const void *data, int data_type, const void *data, int data_type,
const struct qstr *file_name,
__kernel_fsid_t *fsid) __kernel_fsid_t *fsid)
{ {
struct fanotify_event *event = NULL; struct fanotify_event *event = NULL;
struct fanotify_fid_event *ffe = NULL; struct fanotify_fid_event *ffe = NULL;
struct fanotify_name_event *fne = NULL;
gfp_t gfp = GFP_KERNEL_ACCOUNT; gfp_t gfp = GFP_KERNEL_ACCOUNT;
struct inode *id = fanotify_fid_inode(inode, mask, data, data_type); struct inode *id = fanotify_fid_inode(inode, mask, data, data_type);
const struct path *path = fsnotify_data_path(data, data_type); const struct path *path = fsnotify_data_path(data, data_type);
...@@ -356,6 +382,23 @@ struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group, ...@@ -356,6 +382,23 @@ struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
goto init; goto init;
} }
/*
* For FAN_DIR_MODIFY event, we report the fid of the directory and
* the name of the modified entry.
* Allocate an fanotify_name_event struct and copy the name.
*/
if (mask & FAN_DIR_MODIFY && !(WARN_ON_ONCE(!file_name))) {
fne = kmalloc(sizeof(*fne) + file_name->len + 1, gfp);
if (!fne)
goto out;
event = &fne->fae;
event->type = FANOTIFY_EVENT_TYPE_FID_NAME;
fne->name_len = file_name->len;
strcpy(fne->name, file_name->name);
goto init;
}
if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) { if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
ffe = kmem_cache_alloc(fanotify_fid_event_cachep, gfp); ffe = kmem_cache_alloc(fanotify_fid_event_cachep, gfp);
if (!ffe) if (!ffe)
...@@ -374,7 +417,7 @@ struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group, ...@@ -374,7 +417,7 @@ struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
event->type = FANOTIFY_EVENT_TYPE_PATH; event->type = FANOTIFY_EVENT_TYPE_PATH;
} }
init: __maybe_unused init:
/* /*
* Use the victim inode instead of the watching inode as the id for * Use the victim inode instead of the watching inode as the id for
* event queue, so event reported on parent is merged with event * event queue, so event reported on parent is merged with event
...@@ -387,13 +430,16 @@ init: __maybe_unused ...@@ -387,13 +430,16 @@ init: __maybe_unused
else else
event->pid = get_pid(task_tgid(current)); event->pid = get_pid(task_tgid(current));
if (fanotify_event_object_fh(event)) { if (fsid && fanotify_event_fsid(event))
ffe->object_fh.len = 0; *fanotify_event_fsid(event) = *fsid;
if (fsid)
ffe->fsid = *fsid; if (fanotify_event_object_fh(event))
if (id) fanotify_encode_fh(fanotify_event_object_fh(event), id, gfp);
fanotify_encode_fh(&ffe->object_fh, id, gfp);
} else if (fanotify_event_has_path(event)) { if (fanotify_event_dir_fh(event))
fanotify_encode_fh(fanotify_event_dir_fh(event), id, gfp);
if (fanotify_event_has_path(event)) {
struct path *p = fanotify_event_path(event); struct path *p = fanotify_event_path(event);
if (path) { if (path) {
...@@ -501,7 +547,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, ...@@ -501,7 +547,7 @@ static int fanotify_handle_event(struct fsnotify_group *group,
} }
event = fanotify_alloc_event(group, inode, mask, data, data_type, event = fanotify_alloc_event(group, inode, mask, data, data_type,
&fsid); file_name, &fsid);
ret = -ENOMEM; ret = -ENOMEM;
if (unlikely(!event)) { if (unlikely(!event)) {
/* /*
...@@ -563,6 +609,15 @@ static void fanotify_free_fid_event(struct fanotify_event *event) ...@@ -563,6 +609,15 @@ static void fanotify_free_fid_event(struct fanotify_event *event)
kmem_cache_free(fanotify_fid_event_cachep, ffe); kmem_cache_free(fanotify_fid_event_cachep, ffe);
} }
static void fanotify_free_name_event(struct fanotify_event *event)
{
struct fanotify_name_event *fne = FANOTIFY_NE(event);
if (fanotify_fh_has_ext_buf(&fne->dir_fh))
kfree(fanotify_fh_ext_buf(&fne->dir_fh));
kfree(fne);
}
static void fanotify_free_event(struct fsnotify_event *fsn_event) static void fanotify_free_event(struct fsnotify_event *fsn_event)
{ {
struct fanotify_event *event; struct fanotify_event *event;
...@@ -579,6 +634,9 @@ static void fanotify_free_event(struct fsnotify_event *fsn_event) ...@@ -579,6 +634,9 @@ static void fanotify_free_event(struct fsnotify_event *fsn_event)
case FANOTIFY_EVENT_TYPE_FID: case FANOTIFY_EVENT_TYPE_FID:
fanotify_free_fid_event(event); fanotify_free_fid_event(event);
break; break;
case FANOTIFY_EVENT_TYPE_FID_NAME:
fanotify_free_name_event(event);
break;
default: default:
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
} }
......
...@@ -59,7 +59,8 @@ static inline void *fanotify_fh_buf(struct fanotify_fh *fh) ...@@ -59,7 +59,8 @@ static inline void *fanotify_fh_buf(struct fanotify_fh *fh)
* be freed and which concrete struct it may be cast to. * be freed and which concrete struct it may be cast to.
*/ */
enum fanotify_event_type { enum fanotify_event_type {
FANOTIFY_EVENT_TYPE_FID, FANOTIFY_EVENT_TYPE_FID, /* fixed 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,
}; };
...@@ -83,10 +84,26 @@ FANOTIFY_FE(struct fanotify_event *event) ...@@ -83,10 +84,26 @@ FANOTIFY_FE(struct fanotify_event *event)
return container_of(event, struct fanotify_fid_event, fae); return container_of(event, struct fanotify_fid_event, fae);
} }
struct fanotify_name_event {
struct fanotify_event fae;
__kernel_fsid_t fsid;
struct fanotify_fh dir_fh;
u8 name_len;
char name[0];
};
static inline struct fanotify_name_event *
FANOTIFY_NE(struct fanotify_event *event)
{
return container_of(event, struct fanotify_name_event, fae);
}
static inline __kernel_fsid_t *fanotify_event_fsid(struct fanotify_event *event) static inline __kernel_fsid_t *fanotify_event_fsid(struct fanotify_event *event)
{ {
if (event->type == FANOTIFY_EVENT_TYPE_FID) if (event->type == FANOTIFY_EVENT_TYPE_FID)
return &FANOTIFY_FE(event)->fsid; return &FANOTIFY_FE(event)->fsid;
else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME)
return &FANOTIFY_NE(event)->fsid;
else else
return NULL; return NULL;
} }
...@@ -100,6 +117,15 @@ static inline struct fanotify_fh *fanotify_event_object_fh( ...@@ -100,6 +117,15 @@ static inline struct fanotify_fh *fanotify_event_object_fh(
return NULL; return NULL;
} }
static inline struct fanotify_fh *fanotify_event_dir_fh(
struct fanotify_event *event)
{
if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME)
return &FANOTIFY_NE(event)->dir_fh;
else
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_fh *fh = fanotify_event_object_fh(event); struct fanotify_fh *fh = fanotify_event_object_fh(event);
...@@ -107,6 +133,17 @@ static inline int fanotify_event_object_fh_len(struct fanotify_event *event) ...@@ -107,6 +133,17 @@ static inline int fanotify_event_object_fh_len(struct fanotify_event *event)
return fh ? fh->len : 0; return fh ? fh->len : 0;
} }
static inline bool fanotify_event_has_name(struct fanotify_event *event)
{
return event->type == FANOTIFY_EVENT_TYPE_FID_NAME;
}
static inline int fanotify_event_name_len(struct fanotify_event *event)
{
return fanotify_event_has_name(event) ?
FANOTIFY_NE(event)->name_len : 0;
}
struct fanotify_path_event { struct fanotify_path_event {
struct fanotify_event fae; struct fanotify_event fae;
struct path path; struct path path;
...@@ -169,4 +206,5 @@ static inline struct path *fanotify_event_path(struct fanotify_event *event) ...@@ -169,4 +206,5 @@ static inline struct path *fanotify_event_path(struct fanotify_event *event)
struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group, struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
struct inode *inode, u32 mask, struct inode *inode, u32 mask,
const void *data, int data_type, const void *data, int data_type,
const struct qstr *file_name,
__kernel_fsid_t *fsid); __kernel_fsid_t *fsid);
...@@ -210,7 +210,7 @@ static int copy_fid_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh, ...@@ -210,7 +210,7 @@ static int copy_fid_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
struct fanotify_event_info_fid info = { }; struct fanotify_event_info_fid info = { };
struct file_handle handle = { }; struct file_handle handle = { };
unsigned char bounce[FANOTIFY_INLINE_FH_LEN], *fh_buf; unsigned char bounce[FANOTIFY_INLINE_FH_LEN], *fh_buf;
size_t fh_len = fh->len; size_t fh_len = fh ? fh->len : 0;
size_t len = fanotify_fid_info_len(fh_len); size_t len = fanotify_fid_info_len(fh_len);
if (!len) if (!len)
...@@ -828,7 +828,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) ...@@ -828,7 +828,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
group->memcg = get_mem_cgroup_from_mm(current->mm); 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,
FSNOTIFY_EVENT_NONE, NULL); FSNOTIFY_EVENT_NONE, NULL, NULL);
if (unlikely(!oevent)) { if (unlikely(!oevent)) {
fd = -ENOMEM; fd = -ENOMEM;
goto out_destroy_group; goto out_destroy_group;
......
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