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

fanotify: use struct fanotify_info to parcel the variable size buffer

An fanotify event name is always recorded relative to a dir fh.
Encapsulate the name_len member of fanotify_name_event in a new struct
fanotify_info, which describes the parceling of the variable size
buffer of an fanotify_name_event.

The dir_fh member of fanotify_name_event is renamed to _dir_fh and is not
accessed directly, but via the fanotify_info_dir_fh() accessor.
Although the dir_fh len information is already available in struct
fanotify_fh, we store it also in dif_fh_totlen member of fanotify_info,
including the size of fanotify_fh header, so we know the offset of the
name in the buffer without looking inside the dir_fh.

We also add a file_fh_totlen member to allow packing another file handle
in the variable size buffer after the dir_fh and before the name.
We are going to use that space to store the child fid.

Link: https://lore.kernel.org/r/20200716084230.30611-10-amir73il@gmail.comSigned-off-by: default avatarAmir Goldstein <amir73il@gmail.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
parent 6ba8d710
...@@ -49,22 +49,44 @@ static bool fanotify_fid_event_equal(struct fanotify_fid_event *ffe1, ...@@ -49,22 +49,44 @@ 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_info_equal(struct fanotify_info *info1,
struct fanotify_info *info2)
{
if (info1->dir_fh_totlen != info2->dir_fh_totlen ||
info1->file_fh_totlen != info2->file_fh_totlen ||
info1->name_len != info2->name_len)
return false;
if (info1->dir_fh_totlen &&
!fanotify_fh_equal(fanotify_info_dir_fh(info1),
fanotify_info_dir_fh(info2)))
return false;
if (info1->file_fh_totlen &&
!fanotify_fh_equal(fanotify_info_file_fh(info1),
fanotify_info_file_fh(info2)))
return false;
return !info1->name_len ||
!memcmp(fanotify_info_name(info1), fanotify_info_name(info2),
info1->name_len);
}
static bool fanotify_name_event_equal(struct fanotify_name_event *fne1, static bool fanotify_name_event_equal(struct fanotify_name_event *fne1,
struct fanotify_name_event *fne2) struct fanotify_name_event *fne2)
{ {
/* Do not merge name events without dir fh */ struct fanotify_info *info1 = &fne1->info;
if (!fne1->dir_fh.len) struct fanotify_info *info2 = &fne2->info;
return false;
if (fne1->name_len != fne2->name_len || /* Do not merge name events without dir fh */
!fanotify_fh_equal(&fne1->dir_fh, &fne2->dir_fh)) if (!info1->dir_fh_totlen)
return false; return false;
return !memcmp(fne1->name, fne2->name, fne1->name_len); return fanotify_info_equal(info1, info2);
} }
static bool fanotify_should_merge(struct fsnotify_event *old_fsn, static bool fanotify_should_merge(struct fsnotify_event *old_fsn,
struct fsnotify_event *new_fsn) struct fsnotify_event *new_fsn)
{ {
struct fanotify_event *old, *new; struct fanotify_event *old, *new;
...@@ -276,8 +298,14 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group, ...@@ -276,8 +298,14 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
return test_mask & user_mask; return test_mask & user_mask;
} }
static void fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode, /*
gfp_t gfp) * Encode fanotify_fh.
*
* Return total size of encoded fh including fanotify_fh header.
* Return 0 on failure to encode.
*/
static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
gfp_t gfp)
{ {
int dwords, type, bytes = 0; int dwords, type, bytes = 0;
char *ext_buf = NULL; char *ext_buf = NULL;
...@@ -287,7 +315,7 @@ static void fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode, ...@@ -287,7 +315,7 @@ static void fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
fh->type = FILEID_ROOT; fh->type = FILEID_ROOT;
fh->len = 0; fh->len = 0;
if (!inode) if (!inode)
return; return 0;
dwords = 0; dwords = 0;
err = -ENOENT; err = -ENOENT;
...@@ -315,7 +343,7 @@ static void fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode, ...@@ -315,7 +343,7 @@ static void fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
fh->type = type; fh->type = type;
fh->len = bytes; fh->len = bytes;
return; return FANOTIFY_FH_HDR_LEN + bytes;
out_err: out_err:
pr_warn_ratelimited("fanotify: failed to encode fid (type=%d, len=%d, err=%i)\n", pr_warn_ratelimited("fanotify: failed to encode fid (type=%d, len=%d, err=%i)\n",
...@@ -325,6 +353,7 @@ static void fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode, ...@@ -325,6 +353,7 @@ static void fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
/* 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;
return 0;
} }
/* /*
...@@ -401,6 +430,8 @@ static struct fanotify_event *fanotify_alloc_name_event(struct inode *id, ...@@ -401,6 +430,8 @@ static struct fanotify_event *fanotify_alloc_name_event(struct inode *id,
gfp_t gfp) gfp_t gfp)
{ {
struct fanotify_name_event *fne; struct fanotify_name_event *fne;
struct fanotify_info *info;
struct fanotify_fh *dfh;
fne = kmalloc(sizeof(*fne) + file_name->len + 1, gfp); fne = kmalloc(sizeof(*fne) + file_name->len + 1, gfp);
if (!fne) if (!fne)
...@@ -408,9 +439,11 @@ static struct fanotify_event *fanotify_alloc_name_event(struct inode *id, ...@@ -408,9 +439,11 @@ static struct fanotify_event *fanotify_alloc_name_event(struct inode *id,
fne->fae.type = FANOTIFY_EVENT_TYPE_FID_NAME; fne->fae.type = FANOTIFY_EVENT_TYPE_FID_NAME;
fne->fsid = *fsid; fne->fsid = *fsid;
fanotify_encode_fh(&fne->dir_fh, id, gfp); info = &fne->info;
fne->name_len = file_name->len; fanotify_info_init(info);
strcpy(fne->name, file_name->name); dfh = fanotify_info_dir_fh(info);
info->dir_fh_totlen = fanotify_encode_fh(dfh, id, gfp);
fanotify_info_copy_name(info, file_name);
return &fne->fae; return &fne->fae;
} }
...@@ -626,9 +659,10 @@ static void fanotify_free_fid_event(struct fanotify_event *event) ...@@ -626,9 +659,10 @@ static void fanotify_free_fid_event(struct fanotify_event *event)
static void fanotify_free_name_event(struct fanotify_event *event) static void fanotify_free_name_event(struct fanotify_event *event)
{ {
struct fanotify_name_event *fne = FANOTIFY_NE(event); struct fanotify_name_event *fne = FANOTIFY_NE(event);
struct fanotify_fh *dfh = fanotify_info_dir_fh(&fne->info);
if (fanotify_fh_has_ext_buf(&fne->dir_fh)) if (fanotify_fh_has_ext_buf(dfh))
kfree(fanotify_fh_ext_buf(&fne->dir_fh)); kfree(fanotify_fh_ext_buf(dfh));
kfree(fne); kfree(fne);
} }
......
...@@ -23,11 +23,29 @@ enum { ...@@ -23,11 +23,29 @@ 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;
u8 pad[2];
unsigned char buf[FANOTIFY_INLINE_FH_LEN];
} __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)
...@@ -37,6 +55,7 @@ static inline bool fanotify_fh_has_ext_buf(struct fanotify_fh *fh) ...@@ -37,6 +55,7 @@ static inline bool fanotify_fh_has_ext_buf(struct fanotify_fh *fh)
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 +71,56 @@ static inline void *fanotify_fh_buf(struct fanotify_fh *fh) ...@@ -52,6 +71,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
...@@ -96,9 +165,9 @@ FANOTIFY_FE(struct fanotify_event *event) ...@@ -96,9 +165,9 @@ 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; /* Reserve space in info.buf[] - access with fanotify_info_dir_fh() */
char name[]; struct fanotify_fh _dir_fh;
}; };
static inline struct fanotify_name_event * static inline struct fanotify_name_event *
...@@ -126,11 +195,11 @@ static inline struct fanotify_fh *fanotify_event_object_fh( ...@@ -126,11 +195,11 @@ static inline struct fanotify_fh *fanotify_event_object_fh(
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;
} }
...@@ -142,15 +211,11 @@ static inline int fanotify_event_object_fh_len(struct fanotify_event *event) ...@@ -142,15 +211,11 @@ 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) 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 {
......
...@@ -66,19 +66,17 @@ static int fanotify_fid_info_len(int fh_len, int name_len) ...@@ -66,19 +66,17 @@ static int fanotify_fid_info_len(int fh_len, int name_len)
static int fanotify_event_info_len(struct fanotify_event *event) static int fanotify_event_info_len(struct fanotify_event *event)
{ {
int info_len = 0; struct fanotify_info *info = fanotify_event_info(event);
int dir_fh_len = fanotify_event_dir_fh_len(event);
int fh_len = fanotify_event_object_fh_len(event); int fh_len = fanotify_event_object_fh_len(event);
int info_len = 0;
if (dir_fh_len)
info_len += fanotify_fid_info_len(dir_fh_len, info->name_len);
if (fh_len) if (fh_len)
info_len += fanotify_fid_info_len(fh_len, 0); info_len += fanotify_fid_info_len(fh_len, 0);
if (fanotify_event_name_len(event)) {
struct fanotify_name_event *fne = FANOTIFY_NE(event);
info_len += fanotify_fid_info_len(fne->dir_fh.len,
fne->name_len);
}
return info_len; return info_len;
} }
...@@ -305,6 +303,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, ...@@ -305,6 +303,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
{ {
struct fanotify_event_metadata metadata; struct fanotify_event_metadata metadata;
struct path *path = fanotify_event_path(event); struct path *path = fanotify_event_path(event);
struct fanotify_info *info = fanotify_event_info(event);
struct file *f = NULL; struct file *f = NULL;
int ret, fd = FAN_NOFD; int ret, fd = FAN_NOFD;
...@@ -346,13 +345,11 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, ...@@ -346,13 +345,11 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
fd_install(fd, f); fd_install(fd, f);
/* Event info records order is: dir fid + name, child fid */ /* Event info records order is: dir fid + name, child fid */
if (fanotify_event_name_len(event)) { if (fanotify_event_dir_fh_len(event)) {
struct fanotify_name_event *fne = FANOTIFY_NE(event);
ret = copy_info_to_user(fanotify_event_fsid(event), ret = copy_info_to_user(fanotify_event_fsid(event),
fanotify_event_dir_fh(event), fanotify_info_dir_fh(info),
fne->name, fne->name_len, fanotify_info_name(info),
buf, count); info->name_len, buf, count);
if (ret < 0) if (ret < 0)
return ret; return ret;
......
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