Skip to content

Commit 83b7a59

Browse files
amir73iljankara
authored andcommitted
fanotify: add basic support for FAN_REPORT_DIR_FID
For now, the flag is mutually exclusive with FAN_REPORT_FID. Events include a single info record of type FAN_EVENT_INFO_TYPE_DFID with a directory file handle. For now, events are only reported for: - Directory modification events - Events on children of a watching directory - Events on directory objects Soon, we will add support for reporting the parent directory fid for events on non-directories with filesystem/mount mark and support for reporting both parent directory fid and 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 79cb299 commit 83b7a59

File tree

4 files changed

+101
-17
lines changed

4 files changed

+101
-17
lines changed

fs/notify/fanotify/fanotify.c

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ static int fanotify_get_response(struct fsnotify_group *group,
223223
static u32 fanotify_group_event_mask(struct fsnotify_group *group,
224224
struct fsnotify_iter_info *iter_info,
225225
u32 event_mask, const void *data,
226-
int data_type)
226+
int data_type, struct inode *dir)
227227
{
228228
__u32 marks_mask = 0, marks_ignored_mask = 0;
229229
__u32 test_mask, user_mask = FANOTIFY_OUTGOING_EVENTS |
@@ -243,6 +243,10 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
243243
/* Path type events are only relevant for files and dirs */
244244
if (!d_is_reg(path->dentry) && !d_can_lookup(path->dentry))
245245
return 0;
246+
} else if (!(fid_mode & FAN_REPORT_FID)) {
247+
/* Do we have a directory inode to report? */
248+
if (!dir && !(event_mask & FS_ISDIR))
249+
return 0;
246250
}
247251

248252
fsnotify_foreach_obj_type(type) {
@@ -396,6 +400,28 @@ static struct inode *fanotify_fid_inode(u32 event_mask, const void *data,
396400
return fsnotify_data_inode(data, data_type);
397401
}
398402

403+
/*
404+
* The inode to use as identifier when reporting dir fid depends on the event.
405+
* Report the modified directory inode on dirent modification events.
406+
* Report the "victim" inode if "victim" is a directory.
407+
* Report the parent inode if "victim" is not a directory and event is
408+
* reported to parent.
409+
* Otherwise, do not report dir fid.
410+
*/
411+
static struct inode *fanotify_dfid_inode(u32 event_mask, const void *data,
412+
int data_type, struct inode *dir)
413+
{
414+
struct inode *inode = fsnotify_data_inode(data, data_type);
415+
416+
if (event_mask & ALL_FSNOTIFY_DIRENT_EVENTS)
417+
return dir;
418+
419+
if (S_ISDIR(inode->i_mode))
420+
return inode;
421+
422+
return dir;
423+
}
424+
399425
static struct fanotify_event *fanotify_alloc_path_event(const struct path *path,
400426
gfp_t gfp)
401427
{
@@ -491,10 +517,14 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
491517
struct fanotify_event *event = NULL;
492518
gfp_t gfp = GFP_KERNEL_ACCOUNT;
493519
struct inode *id = fanotify_fid_inode(mask, data, data_type, dir);
520+
struct inode *dirid = fanotify_dfid_inode(mask, data, data_type, dir);
494521
const struct path *path = fsnotify_data_path(data, data_type);
495522
unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
496523
bool name_event = false;
497524

525+
if ((fid_mode & FAN_REPORT_DIR_FID) && dirid)
526+
id = dirid;
527+
498528
/*
499529
* For queues with unlimited length lost events are not expected and
500530
* can possibly have security implications. Avoid losing events when
@@ -605,7 +635,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
605635
BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 19);
606636

607637
mask = fanotify_group_event_mask(group, iter_info, mask, data,
608-
data_type);
638+
data_type, dir);
609639
if (!mask)
610640
return 0;
611641

fs/notify/fanotify/fanotify_user.c

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ static int process_access_response(struct fsnotify_group *group,
216216
}
217217

218218
static int copy_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
219-
const char *name, size_t name_len,
219+
int info_type, const char *name, size_t name_len,
220220
char __user *buf, size_t count)
221221
{
222222
struct fanotify_event_info_fid info = { };
@@ -229,7 +229,7 @@ static int copy_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
229229
pr_debug("%s: fh_len=%zu name_len=%zu, info_len=%zu, count=%zu\n",
230230
__func__, fh_len, name_len, info_len, count);
231231

232-
if (!fh_len || (name && !name_len))
232+
if (!fh_len)
233233
return 0;
234234

235235
if (WARN_ON_ONCE(len < sizeof(info) || len > count))
@@ -239,8 +239,21 @@ static int copy_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
239239
* Copy event info fid header followed by variable sized file handle
240240
* and optionally followed by variable sized filename.
241241
*/
242-
info.hdr.info_type = name_len ? FAN_EVENT_INFO_TYPE_DFID_NAME :
243-
FAN_EVENT_INFO_TYPE_FID;
242+
switch (info_type) {
243+
case FAN_EVENT_INFO_TYPE_FID:
244+
case FAN_EVENT_INFO_TYPE_DFID:
245+
if (WARN_ON_ONCE(name_len))
246+
return -EFAULT;
247+
break;
248+
case FAN_EVENT_INFO_TYPE_DFID_NAME:
249+
if (WARN_ON_ONCE(!name || !name_len))
250+
return -EFAULT;
251+
break;
252+
default:
253+
return -EFAULT;
254+
}
255+
256+
info.hdr.info_type = info_type;
244257
info.hdr.len = len;
245258
info.fsid = *fsid;
246259
if (copy_to_user(buf, &info, sizeof(info)))
@@ -304,8 +317,10 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
304317
struct fanotify_event_metadata metadata;
305318
struct path *path = fanotify_event_path(event);
306319
struct fanotify_info *info = fanotify_event_info(event);
320+
unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
307321
struct file *f = NULL;
308322
int ret, fd = FAN_NOFD;
323+
int info_type = 0;
309324

310325
pr_debug("%s: group=%p event=%p\n", __func__, group, event);
311326

@@ -346,9 +361,10 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
346361

347362
/* Event info records order is: dir fid + name, child fid */
348363
if (fanotify_event_dir_fh_len(event)) {
364+
info_type = FAN_EVENT_INFO_TYPE_DFID_NAME;
349365
ret = copy_info_to_user(fanotify_event_fsid(event),
350366
fanotify_info_dir_fh(info),
351-
fanotify_info_name(info),
367+
info_type, fanotify_info_name(info),
352368
info->name_len, buf, count);
353369
if (ret < 0)
354370
return ret;
@@ -358,9 +374,33 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
358374
}
359375

360376
if (fanotify_event_object_fh_len(event)) {
377+
if (fid_mode == FAN_REPORT_FID || info_type) {
378+
/*
379+
* With only group flag FAN_REPORT_FID only type FID is
380+
* reported. Second info record type is always FID.
381+
*/
382+
info_type = FAN_EVENT_INFO_TYPE_FID;
383+
} else if ((event->mask & ALL_FSNOTIFY_DIRENT_EVENTS) ||
384+
(event->mask & FAN_ONDIR)) {
385+
/*
386+
* With group flag FAN_REPORT_DIR_FID, a single info
387+
* record has type DFID for directory entry modification
388+
* event and for event on a directory.
389+
*/
390+
info_type = FAN_EVENT_INFO_TYPE_DFID;
391+
} else {
392+
/*
393+
* With group flags FAN_REPORT_DIR_FID|FAN_REPORT_FID,
394+
* a single info record has type FID for event on a
395+
* non-directory, when there is no directory to report.
396+
* For example, on FAN_DELETE_SELF event.
397+
*/
398+
info_type = FAN_EVENT_INFO_TYPE_FID;
399+
}
400+
361401
ret = copy_info_to_user(fanotify_event_fsid(event),
362402
fanotify_event_object_fh(event),
363-
NULL, 0, buf, count);
403+
info_type, NULL, 0, buf, count);
364404
if (ret < 0)
365405
return ret;
366406

@@ -861,6 +901,8 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
861901
struct fsnotify_group *group;
862902
int f_flags, fd;
863903
struct user_struct *user;
904+
unsigned int fid_mode = flags & FANOTIFY_FID_BITS;
905+
unsigned int class = flags & FANOTIFY_CLASS_BITS;
864906

865907
pr_debug("%s: flags=%x event_f_flags=%x\n",
866908
__func__, flags, event_f_flags);
@@ -887,10 +929,19 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
887929
return -EINVAL;
888930
}
889931

890-
if ((flags & FANOTIFY_FID_BITS) &&
891-
(flags & FANOTIFY_CLASS_BITS) != FAN_CLASS_NOTIF)
932+
if (fid_mode && class != FAN_CLASS_NOTIF)
892933
return -EINVAL;
893934

935+
/* Reporting either object fid or dir fid */
936+
switch (fid_mode) {
937+
case 0:
938+
case FAN_REPORT_FID:
939+
case FAN_REPORT_DIR_FID:
940+
break;
941+
default:
942+
return -EINVAL;
943+
}
944+
894945
user = get_current_user();
895946
if (atomic_read(&user->fanotify_listeners) > FANOTIFY_DEFAULT_MAX_LISTENERS) {
896947
free_uid(user);
@@ -926,7 +977,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
926977
group->fanotify_data.f_flags = event_f_flags;
927978
init_waitqueue_head(&group->fanotify_data.access_waitq);
928979
INIT_LIST_HEAD(&group->fanotify_data.access_list);
929-
switch (flags & FANOTIFY_CLASS_BITS) {
980+
switch (class) {
930981
case FAN_CLASS_NOTIF:
931982
group->priority = FS_PRIO_0;
932983
break;
@@ -1236,7 +1287,7 @@ COMPAT_SYSCALL_DEFINE6(fanotify_mark,
12361287
*/
12371288
static int __init fanotify_user_setup(void)
12381289
{
1239-
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 8);
1290+
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 9);
12401291
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 9);
12411292

12421293
fanotify_mark_cache = KMEM_CACHE(fsnotify_mark,

include/linux/fanotify.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
#define FANOTIFY_CLASS_BITS (FAN_CLASS_NOTIF | FAN_CLASS_CONTENT | \
1919
FAN_CLASS_PRE_CONTENT)
2020

21-
#define FANOTIFY_FID_BITS (FAN_REPORT_FID)
21+
#define FANOTIFY_FID_BITS (FAN_REPORT_FID | FAN_REPORT_DIR_FID)
2222

2323
#define FANOTIFY_INIT_FLAGS (FANOTIFY_CLASS_BITS | FANOTIFY_FID_BITS | \
2424
FAN_REPORT_TID | \

include/uapi/linux/fanotify.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
/* Flags to determine fanotify event format */
5454
#define FAN_REPORT_TID 0x00000100 /* event->pid is thread id */
5555
#define FAN_REPORT_FID 0x00000200 /* Report unique file id */
56+
#define FAN_REPORT_DIR_FID 0x00000400 /* Report unique directory id */
5657

5758
/* Deprecated - do not use this in programs and do not add new flags here! */
5859
#define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK | \
@@ -117,6 +118,7 @@ struct fanotify_event_metadata {
117118

118119
#define FAN_EVENT_INFO_TYPE_FID 1
119120
#define FAN_EVENT_INFO_TYPE_DFID_NAME 2
121+
#define FAN_EVENT_INFO_TYPE_DFID 3
120122

121123
/* Variable length info record following event metadata */
122124
struct fanotify_event_info_header {
@@ -126,10 +128,11 @@ struct fanotify_event_info_header {
126128
};
127129

128130
/*
129-
* Unique file identifier info record. This is used both for
130-
* FAN_EVENT_INFO_TYPE_FID records and for FAN_EVENT_INFO_TYPE_DFID_NAME
131-
* records. For FAN_EVENT_INFO_TYPE_DFID_NAME there is additionally a null
132-
* terminated name immediately after the file handle.
131+
* Unique file identifier info record.
132+
* This structure is used for records of types FAN_EVENT_INFO_TYPE_FID,
133+
* FAN_EVENT_INFO_TYPE_DFID and FAN_EVENT_INFO_TYPE_DFID_NAME.
134+
* For FAN_EVENT_INFO_TYPE_DFID_NAME there is additionally a null terminated
135+
* name immediately after the file handle.
133136
*/
134137
struct fanotify_event_info_fid {
135138
struct fanotify_event_info_header hdr;

0 commit comments

Comments
 (0)