Skip to content

Commit e9e0c89

Browse files
amir73iljankara
authored andcommitted
fanotify: encode file identifier for FAN_REPORT_FID
When user requests the flag FAN_REPORT_FID in fanotify_init(), a unique file identifier of the event target object will be reported with the event. The file identifier includes the filesystem's fsid (i.e. from statfs(2)) and an NFS file handle of the file (i.e. from name_to_handle_at(2)). The file identifier makes holding the path reference and passing a file descriptor to user redundant, so those are disabled in a group with FAN_REPORT_FID. Encode fid and store it in event for a group with FAN_REPORT_FID. Up to 12 bytes of file handle on 32bit arch (16 bytes on 64bit arch) are stored inline in fanotify_event struct. Larger file handles are stored in an external allocated buffer. On failure to encode fid, we print a warning and queue the event without the fid information. [JK: Fold part of later patched into this one to use exportfs_encode_inode_fh() right away] Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Jan Kara <jack@suse.cz>
1 parent bb2f7b4 commit e9e0c89

File tree

4 files changed

+156
-13
lines changed

4 files changed

+156
-13
lines changed

fs/notify/fanotify/fanotify.c

Lines changed: 78 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/wait.h>
1414
#include <linux/audit.h>
1515
#include <linux/sched/mm.h>
16+
#include <linux/statfs.h>
1617

1718
#include "fanotify.h"
1819

@@ -25,10 +26,18 @@ static bool should_merge(struct fsnotify_event *old_fsn,
2526
old = FANOTIFY_E(old_fsn);
2627
new = FANOTIFY_E(new_fsn);
2728

28-
if (old_fsn->inode == new_fsn->inode && old->pid == new->pid &&
29-
old->path.mnt == new->path.mnt &&
30-
old->path.dentry == new->path.dentry)
31-
return true;
29+
if (old_fsn->inode != new_fsn->inode || old->pid != new->pid ||
30+
old->fh_type != new->fh_type || old->fh_len != new->fh_len)
31+
return false;
32+
33+
if (fanotify_event_has_path(old)) {
34+
return old->path.mnt == new->path.mnt &&
35+
old->path.dentry == new->path.dentry;
36+
} else if (fanotify_event_has_fid(old)) {
37+
return fanotify_fid_equal(&old->fid, &new->fid, old->fh_len);
38+
}
39+
40+
/* Do not merge events if we failed to encode fid */
3241
return false;
3342
}
3443

@@ -143,6 +152,60 @@ static u32 fanotify_group_event_mask(struct fsnotify_iter_info *iter_info,
143152
~marks_ignored_mask;
144153
}
145154

155+
static int fanotify_encode_fid(struct fanotify_event *event,
156+
const struct path *path, gfp_t gfp)
157+
{
158+
struct fanotify_fid *fid = &event->fid;
159+
int dwords, bytes = 0;
160+
struct kstatfs stat;
161+
int err, type;
162+
163+
stat.f_fsid.val[0] = stat.f_fsid.val[1] = 0;
164+
fid->ext_fh = NULL;
165+
dwords = 0;
166+
err = -ENOENT;
167+
type = exportfs_encode_inode_fh(d_inode(path->dentry), NULL, &dwords,
168+
NULL);
169+
if (!dwords)
170+
goto out_err;
171+
172+
err = vfs_statfs(path, &stat);
173+
if (err)
174+
goto out_err;
175+
176+
bytes = dwords << 2;
177+
if (bytes > FANOTIFY_INLINE_FH_LEN) {
178+
/* Treat failure to allocate fh as failure to allocate event */
179+
err = -ENOMEM;
180+
fid->ext_fh = kmalloc(bytes, gfp);
181+
if (!fid->ext_fh)
182+
goto out_err;
183+
}
184+
185+
type = exportfs_encode_inode_fh(d_inode(path->dentry),
186+
fanotify_fid_fh(fid, bytes), &dwords,
187+
NULL);
188+
err = -EINVAL;
189+
if (!type || type == FILEID_INVALID || bytes != dwords << 2)
190+
goto out_err;
191+
192+
fid->fsid = stat.f_fsid;
193+
event->fh_len = bytes;
194+
195+
return type;
196+
197+
out_err:
198+
pr_warn_ratelimited("fanotify: failed to encode fid (fsid=%x.%x, "
199+
"type=%d, bytes=%d, err=%i)\n",
200+
stat.f_fsid.val[0], stat.f_fsid.val[1],
201+
type, bytes, err);
202+
kfree(fid->ext_fh);
203+
fid->ext_fh = NULL;
204+
event->fh_len = 0;
205+
206+
return FILEID_INVALID;
207+
}
208+
146209
struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
147210
struct inode *inode, u32 mask,
148211
const struct path *path)
@@ -181,10 +244,16 @@ init: __maybe_unused
181244
event->pid = get_pid(task_pid(current));
182245
else
183246
event->pid = get_pid(task_tgid(current));
184-
if (path) {
247+
event->fh_len = 0;
248+
if (path && FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
249+
/* Report the event without a file identifier on encode error */
250+
event->fh_type = fanotify_encode_fid(event, path, gfp);
251+
} else if (path) {
252+
event->fh_type = FILEID_ROOT;
185253
event->path = *path;
186254
path_get(&event->path);
187255
} else {
256+
event->fh_type = FILEID_INVALID;
188257
event->path.mnt = NULL;
189258
event->path.dentry = NULL;
190259
}
@@ -281,7 +350,10 @@ static void fanotify_free_event(struct fsnotify_event *fsn_event)
281350
struct fanotify_event *event;
282351

283352
event = FANOTIFY_E(fsn_event);
284-
path_put(&event->path);
353+
if (fanotify_event_has_path(event))
354+
path_put(&event->path);
355+
else if (fanotify_event_has_ext_fh(event))
356+
kfree(event->fid.ext_fh);
285357
put_pid(event->pid);
286358
if (fanotify_is_perm_event(event->mask)) {
287359
kmem_cache_free(fanotify_perm_event_cachep,

fs/notify/fanotify/fanotify.h

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,49 @@
22
#include <linux/fsnotify_backend.h>
33
#include <linux/path.h>
44
#include <linux/slab.h>
5+
#include <linux/exportfs.h>
56

67
extern struct kmem_cache *fanotify_mark_cache;
78
extern struct kmem_cache *fanotify_event_cachep;
89
extern struct kmem_cache *fanotify_perm_event_cachep;
910

11+
/*
12+
* 3 dwords are sufficient for most local fs (64bit ino, 32bit generation).
13+
* For 32bit arch, fid increases the size of fanotify_event by 12 bytes and
14+
* fh_* fields increase the size of fanotify_event by another 4 bytes.
15+
* For 64bit arch, fid increases the size of fanotify_fid by 8 bytes and
16+
* fh_* fields are packed in a hole after mask.
17+
*/
18+
#if BITS_PER_LONG == 32
19+
#define FANOTIFY_INLINE_FH_LEN (3 << 2)
20+
#else
21+
#define FANOTIFY_INLINE_FH_LEN (4 << 2)
22+
#endif
23+
24+
struct fanotify_fid {
25+
__kernel_fsid_t fsid;
26+
union {
27+
unsigned char fh[FANOTIFY_INLINE_FH_LEN];
28+
unsigned char *ext_fh;
29+
};
30+
};
31+
32+
static inline void *fanotify_fid_fh(struct fanotify_fid *fid,
33+
unsigned int fh_len)
34+
{
35+
return fh_len <= FANOTIFY_INLINE_FH_LEN ? fid->fh : fid->ext_fh;
36+
}
37+
38+
static inline bool fanotify_fid_equal(struct fanotify_fid *fid1,
39+
struct fanotify_fid *fid2,
40+
unsigned int fh_len)
41+
{
42+
return fid1->fsid.val[0] == fid2->fsid.val[0] &&
43+
fid1->fsid.val[1] == fid2->fsid.val[1] &&
44+
!memcmp(fanotify_fid_fh(fid1, fh_len),
45+
fanotify_fid_fh(fid2, fh_len), fh_len);
46+
}
47+
1048
/*
1149
* Structure for normal fanotify events. It gets allocated in
1250
* fanotify_handle_event() and freed when the information is retrieved by
@@ -16,13 +54,47 @@ struct fanotify_event {
1654
struct fsnotify_event fse;
1755
u32 mask;
1856
/*
19-
* We hold ref to this path so it may be dereferenced at any point
20-
* during this object's lifetime
57+
* Those fields are outside fanotify_fid to pack fanotify_event nicely
58+
* on 64bit arch and to use fh_type as an indication of whether path
59+
* or fid are used in the union:
60+
* FILEID_ROOT (0) for path, > 0 for fid, FILEID_INVALID for neither.
2161
*/
22-
struct path path;
62+
u8 fh_type;
63+
u8 fh_len;
64+
u16 pad;
65+
union {
66+
/*
67+
* We hold ref to this path so it may be dereferenced at any
68+
* point during this object's lifetime
69+
*/
70+
struct path path;
71+
/*
72+
* With FAN_REPORT_FID, we do not hold any reference on the
73+
* victim object. Instead we store its NFS file handle and its
74+
* filesystem's fsid as a unique identifier.
75+
*/
76+
struct fanotify_fid fid;
77+
};
2378
struct pid *pid;
2479
};
2580

81+
static inline bool fanotify_event_has_path(struct fanotify_event *event)
82+
{
83+
return event->fh_type == FILEID_ROOT;
84+
}
85+
86+
static inline bool fanotify_event_has_fid(struct fanotify_event *event)
87+
{
88+
return event->fh_type != FILEID_ROOT &&
89+
event->fh_type != FILEID_INVALID;
90+
}
91+
92+
static inline bool fanotify_event_has_ext_fh(struct fanotify_event *event)
93+
{
94+
return fanotify_event_has_fid(event) &&
95+
event->fh_len > FANOTIFY_INLINE_FH_LEN;
96+
}
97+
2698
/*
2799
* Structure for permission fanotify events. It gets allocated and freed in
28100
* fanotify_handle_event() since we wait there for user response. When the

fs/notify/fanotify/fanotify_user.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
181181
struct fanotify_event_metadata metadata;
182182
struct fanotify_event *event;
183183
struct file *f = NULL;
184-
int fd, ret;
184+
int ret, fd = FAN_NOFD;
185185

186186
pr_debug("%s: group=%p event=%p\n", __func__, group, fsn_event);
187187

@@ -193,9 +193,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
193193
metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
194194
metadata.pid = pid_vnr(event->pid);
195195

196-
if (unlikely(event->mask & FAN_Q_OVERFLOW)) {
197-
fd = FAN_NOFD;
198-
} else {
196+
if (fanotify_event_has_path(event)) {
199197
fd = create_fd(group, event, &f);
200198
if (fd < 0)
201199
return fd;

include/uapi/linux/fanotify.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444

4545
/* Flags to determine fanotify event format */
4646
#define FAN_REPORT_TID 0x00000100 /* event->pid is thread id */
47+
#define FAN_REPORT_FID 0x00000200 /* Report unique file id */
4748

4849
/* Deprecated - do not use this in programs and do not add new flags here! */
4950
#define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK | \

0 commit comments

Comments
 (0)