diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c
index bdafc76cc258..e91a8cc1b83c 100644
--- a/fs/notify/fanotify/fanotify.c
+++ b/fs/notify/fanotify/fanotify.c
@@ -64,7 +64,8 @@ static bool fanotify_name_event_equal(struct fanotify_name_event *fne1,
return false;
if (fne1->name_len != fne2->name_len ||
- !fanotify_fh_equal(&fne1->dir_fh, &fne2->dir_fh))
+ !fanotify_fh_equal(&fne1->dir_fh, &fne2->dir_fh) ||
+ !fanotify_fh_equal(&fne1->object_fh, &fne2->object_fh))
return false;
return !memcmp(fne1->name, fne2->name, fne1->name_len);
@@ -211,7 +212,8 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
int data_type)
{
__u32 marks_mask = 0, marks_ignored_mask = 0;
- __u32 test_mask, user_mask = FANOTIFY_OUTGOING_EVENTS;
+ __u32 test_mask, user_mask = FANOTIFY_OUTGOING_EVENTS |
+ FANOTIFY_EVENT_FLAGS;
const struct path *path = fsnotify_data_path(data, data_type);
struct fsnotify_mark *mark;
int type;@@ -262,11 +264,17 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
* For backward compatibility and consistency, do not report FAN_ONDIR
* to user in legacy fanotify mode (reporting fd) and report FAN_ONDIR
* to user in FAN_REPORT_FID mode for all event types.
+ *
+ * We never report FAN_EVENT_ON_CHILD to user, but we do pass it in to
+ * fanotify_alloc_event() for group with FAN_REPORT_NAME as indication
+ * of how the event should be reported.
*/
if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
/* Do not report event flags without any event */
if (!(test_mask & ~FANOTIFY_EVENT_FLAGS))
return 0;
+ if (!FAN_GROUP_FLAG(group, FAN_REPORT_NAME))
+ user_mask &= ~FAN_EVENT_ON_CHILD;
} else {
user_mask &= ~FANOTIFY_EVENT_FLAGS;
}@@ -394,10 +402,10 @@ struct fanotify_event *fanotify_alloc_fid_event(struct inode *id,
return &ffe->fae;
}
-struct fanotify_event *fanotify_alloc_name_event(struct inode *id,
+struct fanotify_event *fanotify_alloc_name_event(struct inode *dir,
__kernel_fsid_t *fsid,
const struct qstr *file_name,
- gfp_t gfp)
+ struct inode *id, gfp_t gfp)
{
struct fanotify_name_event *fne;
@@ -407,7 +415,8 @@ struct fanotify_event *fanotify_alloc_name_event(struct inode *id,
fne->fae.type = FANOTIFY_EVENT_TYPE_FID_NAME;
fne->fsid = *fsid;
- fanotify_encode_fh(&fne->dir_fh, id, gfp);
+ fanotify_encode_fh(&fne->dir_fh, dir, gfp);
+ fanotify_encode_fh(&fne->object_fh, id, gfp);
fne->name_len = file_name->len;
strcpy(fne->name, file_name->name);
@@ -447,7 +456,17 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
* and the name of the modified entry.
* Allocate an fanotify_name_event struct and copy the name.
*/
- event = fanotify_alloc_name_event(id, fsid, file_name, gfp);
+ event = fanotify_alloc_name_event(id, fsid, file_name, NULL,
+ gfp);
+ } else if (FAN_GROUP_FLAG(group, FAN_REPORT_NAME) &&
+ (mask & FAN_EVENT_ON_CHILD) && likely(inode != id)) {
+ /*
+ * With flag FAN_REPORT_NAME, we report the parent fid and name
+ * for events reported "on child" in addition to reporting the
+ * child fid.
+ */
+ event = fanotify_alloc_name_event(inode, fsid, file_name, id,
+ gfp);
} else if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
event = fanotify_alloc_fid_event(id, fsid, gfp);
} else {@@ -631,6 +650,8 @@ 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->object_fh))
+ kfree(fanotify_fh_ext_buf(&fne->object_fh));
if (fanotify_fh_has_ext_buf(&fne->dir_fh))
kfree(fanotify_fh_ext_buf(&fne->dir_fh));
kfree(fne);diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index 3adbb9f7d1a8..9aafccc23c75 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -96,6 +96,7 @@ FANOTIFY_FE(struct fanotify_event *event)
struct fanotify_name_event {
struct fanotify_event fae;
__kernel_fsid_t fsid;
+ struct fanotify_fh object_fh;
struct fanotify_fh dir_fh;
u8 name_len;
char name[0];@@ -122,6 +123,8 @@ static inline struct fanotify_fh *fanotify_event_object_fh(
{
if (event->type == FANOTIFY_EVENT_TYPE_FID)
return &FANOTIFY_FE(event)->object_fh;
+ else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME)
+ return &FANOTIFY_NE(event)->object_fh;
else
return NULL;
}diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 8c6d22ec7b41..030534da49e2 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -879,6 +879,10 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
(flags & FANOTIFY_CLASS_BITS) != FAN_CLASS_NOTIF)
return -EINVAL;
+ /* Child name is reported with parent fid */
+ if ((flags & FAN_REPORT_NAME) && !(flags & FAN_REPORT_FID))
+ return -EINVAL;
+
user = get_current_user();
if (atomic_read(&user->fanotify_listeners) > FANOTIFY_DEFAULT_MAX_LISTENERS) {
free_uid(user);@@ -1212,7 +1216,7 @@ COMPAT_SYSCALL_DEFINE6(fanotify_mark,
*/
static int __init fanotify_user_setup(void)
{
- BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 8);
+ BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 9);
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 9);
fanotify_mark_cache = KMEM_CACHE(fsnotify_mark,diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index 3049a6c06d9e..5412a25c54c0 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -19,7 +19,7 @@
FAN_CLASS_PRE_CONTENT)
#define FANOTIFY_INIT_FLAGS (FANOTIFY_CLASS_BITS | \
- FAN_REPORT_TID | FAN_REPORT_FID | \
+ FAN_REPORT_TID | FAN_REPORT_FID_NAME | \
FAN_CLOEXEC | FAN_NONBLOCK | \
FAN_UNLIMITED_QUEUE | FAN_UNLIMITED_MARKS)
diff --git a/include/uapi/linux/fanotify.h b/include/uapi/linux/fanotify.h
index a88c7c6d0692..41f54a0f360f 100644
--- a/include/uapi/linux/fanotify.h
+++ b/include/uapi/linux/fanotify.h
@@ -54,6 +54,10 @@
/* Flags to determine fanotify event format */
#define FAN_REPORT_TID 0x00000100 /* event->pid is thread id */
#define FAN_REPORT_FID 0x00000200 /* Report unique file id */
+#define FAN_REPORT_NAME 0x00000400 /* Report events with name */
+
+/* Convenience macro - FAN_REPORT_NAME requires FAN_REPORT_FID */
+#define FAN_REPORT_FID_NAME (FAN_REPORT_FID | FAN_REPORT_NAME)
/* Deprecated - do not use this in programs and do not add new flags here! */
#define FAN_ALL_INIT_FLAGS (FAN_CLOEXEC | FAN_NONBLOCK | \
--
2.17.1