Skip to content

Commit f454fa6

Browse files
amir73iljankara
authored andcommitted
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/[email protected] Signed-off-by: Amir Goldstein <[email protected]> Signed-off-by: Jan Kara <[email protected]>
1 parent 6ba8d71 commit f454fa6

File tree

3 files changed

+139
-43
lines changed

3 files changed

+139
-43
lines changed

fs/notify/fanotify/fanotify.c

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,22 +49,44 @@ static bool fanotify_fid_event_equal(struct fanotify_fid_event *ffe1,
4949
fanotify_fh_equal(&ffe1->object_fh, &ffe2->object_fh);
5050
}
5151

52+
static bool fanotify_info_equal(struct fanotify_info *info1,
53+
struct fanotify_info *info2)
54+
{
55+
if (info1->dir_fh_totlen != info2->dir_fh_totlen ||
56+
info1->file_fh_totlen != info2->file_fh_totlen ||
57+
info1->name_len != info2->name_len)
58+
return false;
59+
60+
if (info1->dir_fh_totlen &&
61+
!fanotify_fh_equal(fanotify_info_dir_fh(info1),
62+
fanotify_info_dir_fh(info2)))
63+
return false;
64+
65+
if (info1->file_fh_totlen &&
66+
!fanotify_fh_equal(fanotify_info_file_fh(info1),
67+
fanotify_info_file_fh(info2)))
68+
return false;
69+
70+
return !info1->name_len ||
71+
!memcmp(fanotify_info_name(info1), fanotify_info_name(info2),
72+
info1->name_len);
73+
}
74+
5275
static bool fanotify_name_event_equal(struct fanotify_name_event *fne1,
5376
struct fanotify_name_event *fne2)
5477
{
55-
/* Do not merge name events without dir fh */
56-
if (!fne1->dir_fh.len)
57-
return false;
78+
struct fanotify_info *info1 = &fne1->info;
79+
struct fanotify_info *info2 = &fne2->info;
5880

59-
if (fne1->name_len != fne2->name_len ||
60-
!fanotify_fh_equal(&fne1->dir_fh, &fne2->dir_fh))
81+
/* Do not merge name events without dir fh */
82+
if (!info1->dir_fh_totlen)
6183
return false;
6284

63-
return !memcmp(fne1->name, fne2->name, fne1->name_len);
85+
return fanotify_info_equal(info1, info2);
6486
}
6587

6688
static bool fanotify_should_merge(struct fsnotify_event *old_fsn,
67-
struct fsnotify_event *new_fsn)
89+
struct fsnotify_event *new_fsn)
6890
{
6991
struct fanotify_event *old, *new;
7092

@@ -276,8 +298,14 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
276298
return test_mask & user_mask;
277299
}
278300

279-
static void fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
280-
gfp_t gfp)
301+
/*
302+
* Encode fanotify_fh.
303+
*
304+
* Return total size of encoded fh including fanotify_fh header.
305+
* Return 0 on failure to encode.
306+
*/
307+
static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
308+
gfp_t gfp)
281309
{
282310
int dwords, type, bytes = 0;
283311
char *ext_buf = NULL;
@@ -287,7 +315,7 @@ static void fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
287315
fh->type = FILEID_ROOT;
288316
fh->len = 0;
289317
if (!inode)
290-
return;
318+
return 0;
291319

292320
dwords = 0;
293321
err = -ENOENT;
@@ -315,7 +343,7 @@ static void fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
315343
fh->type = type;
316344
fh->len = bytes;
317345

318-
return;
346+
return FANOTIFY_FH_HDR_LEN + bytes;
319347

320348
out_err:
321349
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,
325353
/* Report the event without a file identifier on encode error */
326354
fh->type = FILEID_INVALID;
327355
fh->len = 0;
356+
return 0;
328357
}
329358

330359
/*
@@ -401,16 +430,20 @@ static struct fanotify_event *fanotify_alloc_name_event(struct inode *id,
401430
gfp_t gfp)
402431
{
403432
struct fanotify_name_event *fne;
433+
struct fanotify_info *info;
434+
struct fanotify_fh *dfh;
404435

405436
fne = kmalloc(sizeof(*fne) + file_name->len + 1, gfp);
406437
if (!fne)
407438
return NULL;
408439

409440
fne->fae.type = FANOTIFY_EVENT_TYPE_FID_NAME;
410441
fne->fsid = *fsid;
411-
fanotify_encode_fh(&fne->dir_fh, id, gfp);
412-
fne->name_len = file_name->len;
413-
strcpy(fne->name, file_name->name);
442+
info = &fne->info;
443+
fanotify_info_init(info);
444+
dfh = fanotify_info_dir_fh(info);
445+
info->dir_fh_totlen = fanotify_encode_fh(dfh, id, gfp);
446+
fanotify_info_copy_name(info, file_name);
414447

415448
return &fne->fae;
416449
}
@@ -626,9 +659,10 @@ static void fanotify_free_fid_event(struct fanotify_event *event)
626659
static void fanotify_free_name_event(struct fanotify_event *event)
627660
{
628661
struct fanotify_name_event *fne = FANOTIFY_NE(event);
662+
struct fanotify_fh *dfh = fanotify_info_dir_fh(&fne->info);
629663

630-
if (fanotify_fh_has_ext_buf(&fne->dir_fh))
631-
kfree(fanotify_fh_ext_buf(&fne->dir_fh));
664+
if (fanotify_fh_has_ext_buf(dfh))
665+
kfree(fanotify_fh_ext_buf(dfh));
632666
kfree(fne);
633667
}
634668

fs/notify/fanotify/fanotify.h

Lines changed: 78 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,29 @@ enum {
2323
* stored in either the first or last 2 dwords.
2424
*/
2525
#define FANOTIFY_INLINE_FH_LEN (3 << 2)
26+
#define FANOTIFY_FH_HDR_LEN offsetof(struct fanotify_fh, buf)
2627

28+
/* Fixed size struct for file handle */
2729
struct fanotify_fh {
28-
unsigned char buf[FANOTIFY_INLINE_FH_LEN];
2930
u8 type;
3031
u8 len;
32+
u8 pad[2];
33+
unsigned char buf[FANOTIFY_INLINE_FH_LEN];
34+
} __aligned(4);
35+
36+
/* Variable size struct for dir file handle + child file handle + name */
37+
struct fanotify_info {
38+
/* size of dir_fh/file_fh including fanotify_fh hdr size */
39+
u8 dir_fh_totlen;
40+
u8 file_fh_totlen;
41+
u8 name_len;
42+
u8 pad;
43+
unsigned char buf[];
44+
/*
45+
* (struct fanotify_fh) dir_fh starts at buf[0]
46+
* (optional) file_fh starts at buf[dir_fh_totlen]
47+
* name starts at buf[dir_fh_totlen + file_fh_totlen]
48+
*/
3149
} __aligned(4);
3250

3351
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)
3755

3856
static inline char **fanotify_fh_ext_buf_ptr(struct fanotify_fh *fh)
3957
{
58+
BUILD_BUG_ON(FANOTIFY_FH_HDR_LEN % 4);
4059
BUILD_BUG_ON(__alignof__(char *) - 4 + sizeof(char *) >
4160
FANOTIFY_INLINE_FH_LEN);
4261
return (char **)ALIGN((unsigned long)(fh->buf), __alignof__(char *));
@@ -52,6 +71,56 @@ static inline void *fanotify_fh_buf(struct fanotify_fh *fh)
5271
return fanotify_fh_has_ext_buf(fh) ? fanotify_fh_ext_buf(fh) : fh->buf;
5372
}
5473

74+
static inline int fanotify_info_dir_fh_len(struct fanotify_info *info)
75+
{
76+
if (!info->dir_fh_totlen ||
77+
WARN_ON_ONCE(info->dir_fh_totlen < FANOTIFY_FH_HDR_LEN))
78+
return 0;
79+
80+
return info->dir_fh_totlen - FANOTIFY_FH_HDR_LEN;
81+
}
82+
83+
static inline struct fanotify_fh *fanotify_info_dir_fh(struct fanotify_info *info)
84+
{
85+
BUILD_BUG_ON(offsetof(struct fanotify_info, buf) % 4);
86+
87+
return (struct fanotify_fh *)info->buf;
88+
}
89+
90+
static inline int fanotify_info_file_fh_len(struct fanotify_info *info)
91+
{
92+
if (!info->file_fh_totlen ||
93+
WARN_ON_ONCE(info->file_fh_totlen < FANOTIFY_FH_HDR_LEN))
94+
return 0;
95+
96+
return info->file_fh_totlen - FANOTIFY_FH_HDR_LEN;
97+
}
98+
99+
static inline struct fanotify_fh *fanotify_info_file_fh(struct fanotify_info *info)
100+
{
101+
return (struct fanotify_fh *)(info->buf + info->dir_fh_totlen);
102+
}
103+
104+
static inline const char *fanotify_info_name(struct fanotify_info *info)
105+
{
106+
return info->buf + info->dir_fh_totlen + info->file_fh_totlen;
107+
}
108+
109+
static inline void fanotify_info_init(struct fanotify_info *info)
110+
{
111+
info->dir_fh_totlen = 0;
112+
info->file_fh_totlen = 0;
113+
info->name_len = 0;
114+
}
115+
116+
static inline void fanotify_info_copy_name(struct fanotify_info *info,
117+
const struct qstr *name)
118+
{
119+
info->name_len = name->len;
120+
strcpy(info->buf + info->dir_fh_totlen + info->file_fh_totlen,
121+
name->name);
122+
}
123+
55124
/*
56125
* Common structure for fanotify events. Concrete structs are allocated in
57126
* fanotify_handle_event() and freed when the information is retrieved by
@@ -96,9 +165,9 @@ FANOTIFY_FE(struct fanotify_event *event)
96165
struct fanotify_name_event {
97166
struct fanotify_event fae;
98167
__kernel_fsid_t fsid;
99-
struct fanotify_fh dir_fh;
100-
u8 name_len;
101-
char name[];
168+
struct fanotify_info info;
169+
/* Reserve space in info.buf[] - access with fanotify_info_dir_fh() */
170+
struct fanotify_fh _dir_fh;
102171
};
103172

104173
static inline struct fanotify_name_event *
@@ -126,11 +195,11 @@ static inline struct fanotify_fh *fanotify_event_object_fh(
126195
return NULL;
127196
}
128197

129-
static inline struct fanotify_fh *fanotify_event_dir_fh(
198+
static inline struct fanotify_info *fanotify_event_info(
130199
struct fanotify_event *event)
131200
{
132201
if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME)
133-
return &FANOTIFY_NE(event)->dir_fh;
202+
return &FANOTIFY_NE(event)->info;
134203
else
135204
return NULL;
136205
}
@@ -142,15 +211,11 @@ static inline int fanotify_event_object_fh_len(struct fanotify_event *event)
142211
return fh ? fh->len : 0;
143212
}
144213

145-
static inline bool fanotify_event_has_name(struct fanotify_event *event)
214+
static inline int fanotify_event_dir_fh_len(struct fanotify_event *event)
146215
{
147-
return event->type == FANOTIFY_EVENT_TYPE_FID_NAME;
148-
}
216+
struct fanotify_info *info = fanotify_event_info(event);
149217

150-
static inline int fanotify_event_name_len(struct fanotify_event *event)
151-
{
152-
return fanotify_event_has_name(event) ?
153-
FANOTIFY_NE(event)->name_len : 0;
218+
return info ? fanotify_info_dir_fh_len(info) : 0;
154219
}
155220

156221
struct fanotify_path_event {

fs/notify/fanotify/fanotify_user.c

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,17 @@ static int fanotify_fid_info_len(int fh_len, int name_len)
6666

6767
static int fanotify_event_info_len(struct fanotify_event *event)
6868
{
69-
int info_len = 0;
69+
struct fanotify_info *info = fanotify_event_info(event);
70+
int dir_fh_len = fanotify_event_dir_fh_len(event);
7071
int fh_len = fanotify_event_object_fh_len(event);
72+
int info_len = 0;
73+
74+
if (dir_fh_len)
75+
info_len += fanotify_fid_info_len(dir_fh_len, info->name_len);
7176

7277
if (fh_len)
7378
info_len += fanotify_fid_info_len(fh_len, 0);
7479

75-
if (fanotify_event_name_len(event)) {
76-
struct fanotify_name_event *fne = FANOTIFY_NE(event);
77-
78-
info_len += fanotify_fid_info_len(fne->dir_fh.len,
79-
fne->name_len);
80-
}
81-
8280
return info_len;
8381
}
8482

@@ -305,6 +303,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
305303
{
306304
struct fanotify_event_metadata metadata;
307305
struct path *path = fanotify_event_path(event);
306+
struct fanotify_info *info = fanotify_event_info(event);
308307
struct file *f = NULL;
309308
int ret, fd = FAN_NOFD;
310309

@@ -346,13 +345,11 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
346345
fd_install(fd, f);
347346

348347
/* Event info records order is: dir fid + name, child fid */
349-
if (fanotify_event_name_len(event)) {
350-
struct fanotify_name_event *fne = FANOTIFY_NE(event);
351-
348+
if (fanotify_event_dir_fh_len(event)) {
352349
ret = copy_info_to_user(fanotify_event_fsid(event),
353-
fanotify_event_dir_fh(event),
354-
fne->name, fne->name_len,
355-
buf, count);
350+
fanotify_info_dir_fh(info),
351+
fanotify_info_name(info),
352+
info->name_len, buf, count);
356353
if (ret < 0)
357354
return ret;
358355

0 commit comments

Comments
 (0)