Skip to content

Commit 9b93f33

Browse files
amir73iljankara
authored andcommitted
fsnotify: send event with parent/name info to sb/mount/non-dir marks
Similar to events "on child" to watching directory, send event with parent/name info if sb/mount/non-dir marks are interested in parent/name info. The FS_EVENT_ON_CHILD flag can be set on sb/mount/non-dir marks to specify interest in parent/name info for events on non-directory inodes. Events on "orphan" children (disconnected dentries) are sent without parent/name info. Events on directories are sent with parent/name info only if the parent directory is watching. After this change, even groups that do not subscribe to events on children could get an event with mark iterator type TYPE_CHILD and without mark iterator type TYPE_INODE if fanotify has marks on the same objects. dnotify and inotify event handlers can already cope with that situation. audit does not subscribe to events that are possible on child, so won't get to this situation. nfsd does not access the marks iterator from its event handler at the moment, so it is not affected. This is a bit too fragile, so we should prepare all groups to cope with mark type TYPE_CHILD preferably using a generic helper. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Amir Goldstein <[email protected]> Signed-off-by: Jan Kara <[email protected]>
1 parent 7dbe608 commit 9b93f33

File tree

3 files changed

+97
-19
lines changed

3 files changed

+97
-19
lines changed

fs/notify/fsnotify.c

+61-13
Original file line numberDiff line numberDiff line change
@@ -142,38 +142,81 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode)
142142
spin_unlock(&inode->i_lock);
143143
}
144144

145+
/* Are inode/sb/mount interested in parent and name info with this event? */
146+
static bool fsnotify_event_needs_parent(struct inode *inode, struct mount *mnt,
147+
__u32 mask)
148+
{
149+
__u32 marks_mask = 0;
150+
151+
/* We only send parent/name to inode/sb/mount for events on non-dir */
152+
if (mask & FS_ISDIR)
153+
return false;
154+
155+
/* Did either inode/sb/mount subscribe for events with parent/name? */
156+
marks_mask |= fsnotify_parent_needed_mask(inode->i_fsnotify_mask);
157+
marks_mask |= fsnotify_parent_needed_mask(inode->i_sb->s_fsnotify_mask);
158+
if (mnt)
159+
marks_mask |= fsnotify_parent_needed_mask(mnt->mnt_fsnotify_mask);
160+
161+
/* Did they subscribe for this event with parent/name info? */
162+
return mask & marks_mask;
163+
}
164+
145165
/*
146166
* Notify this dentry's parent about a child's events with child name info
147-
* if parent is watching.
148-
* Notify only the child without name info if parent is not watching.
167+
* if parent is watching or if inode/sb/mount are interested in events with
168+
* parent and name info.
169+
*
170+
* Notify only the child without name info if parent is not watching and
171+
* inode/sb/mount are not interested in events with parent and name info.
149172
*/
150173
int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data,
151174
int data_type)
152175
{
176+
const struct path *path = fsnotify_data_path(data, data_type);
177+
struct mount *mnt = path ? real_mount(path->mnt) : NULL;
153178
struct inode *inode = d_inode(dentry);
154179
struct dentry *parent;
180+
bool parent_watched = dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED;
181+
__u32 p_mask;
155182
struct inode *p_inode = NULL;
156183
struct name_snapshot name;
157184
struct qstr *file_name = NULL;
158185
int ret = 0;
159186

187+
/*
188+
* Do inode/sb/mount care about parent and name info on non-dir?
189+
* Do they care about any event at all?
190+
*/
191+
if (!inode->i_fsnotify_marks && !inode->i_sb->s_fsnotify_marks &&
192+
(!mnt || !mnt->mnt_fsnotify_marks) && !parent_watched)
193+
return 0;
194+
160195
parent = NULL;
161-
if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED))
196+
if (!parent_watched && !fsnotify_event_needs_parent(inode, mnt, mask))
162197
goto notify;
163198

199+
/* Does parent inode care about events on children? */
164200
parent = dget_parent(dentry);
165201
p_inode = parent->d_inode;
166-
167-
if (unlikely(!fsnotify_inode_watches_children(p_inode))) {
202+
p_mask = fsnotify_inode_watches_children(p_inode);
203+
if (unlikely(parent_watched && !p_mask))
168204
__fsnotify_update_child_dentry_flags(p_inode);
169-
} else if (p_inode->i_fsnotify_mask & mask & ALL_FSNOTIFY_EVENTS) {
205+
206+
/*
207+
* Include parent/name in notification either if some notification
208+
* groups require parent info (!parent_watched case) or the parent is
209+
* interested in this event.
210+
*/
211+
if (!parent_watched || (mask & p_mask & ALL_FSNOTIFY_EVENTS)) {
170212
/* When notifying parent, child should be passed as data */
171213
WARN_ON_ONCE(inode != fsnotify_data_inode(data, data_type));
172214

173215
/* Notify both parent and child with child name info */
174216
take_dentry_name_snapshot(&name, dentry);
175217
file_name = &name.name;
176-
mask |= FS_EVENT_ON_CHILD;
218+
if (parent_watched)
219+
mask |= FS_EVENT_ON_CHILD;
177220
}
178221

179222
notify:
@@ -349,8 +392,8 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
349392
inode = dir;
350393
} else if (mask & FS_EVENT_ON_CHILD) {
351394
/*
352-
* Event on child - report on TYPE_INODE to dir
353-
* and on TYPE_CHILD to child.
395+
* Event on child - report on TYPE_INODE to dir if it is
396+
* watching children and on TYPE_CHILD to child.
354397
*/
355398
child = inode;
356399
inode = dir;
@@ -364,14 +407,17 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
364407
* SRCU because we have no references to any objects and do not
365408
* need SRCU to keep them "alive".
366409
*/
367-
if (!inode->i_fsnotify_marks && !sb->s_fsnotify_marks &&
410+
if (!sb->s_fsnotify_marks &&
368411
(!mnt || !mnt->mnt_fsnotify_marks) &&
412+
(!inode || !inode->i_fsnotify_marks) &&
369413
(!child || !child->i_fsnotify_marks))
370414
return 0;
371415

372-
marks_mask = inode->i_fsnotify_mask | sb->s_fsnotify_mask;
416+
marks_mask = sb->s_fsnotify_mask;
373417
if (mnt)
374418
marks_mask |= mnt->mnt_fsnotify_mask;
419+
if (inode)
420+
marks_mask |= inode->i_fsnotify_mask;
375421
if (child)
376422
marks_mask |= child->i_fsnotify_mask;
377423

@@ -386,14 +432,16 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
386432

387433
iter_info.srcu_idx = srcu_read_lock(&fsnotify_mark_srcu);
388434

389-
iter_info.marks[FSNOTIFY_OBJ_TYPE_INODE] =
390-
fsnotify_first_mark(&inode->i_fsnotify_marks);
391435
iter_info.marks[FSNOTIFY_OBJ_TYPE_SB] =
392436
fsnotify_first_mark(&sb->s_fsnotify_marks);
393437
if (mnt) {
394438
iter_info.marks[FSNOTIFY_OBJ_TYPE_VFSMOUNT] =
395439
fsnotify_first_mark(&mnt->mnt_fsnotify_marks);
396440
}
441+
if (inode) {
442+
iter_info.marks[FSNOTIFY_OBJ_TYPE_INODE] =
443+
fsnotify_first_mark(&inode->i_fsnotify_marks);
444+
}
397445
if (child) {
398446
iter_info.marks[FSNOTIFY_OBJ_TYPE_CHILD] =
399447
fsnotify_first_mark(&child->i_fsnotify_marks);

include/linux/fsnotify.h

+8-2
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,16 @@ static inline int fsnotify_parent(struct dentry *dentry, __u32 mask,
5353
{
5454
struct inode *inode = d_inode(dentry);
5555

56-
if (S_ISDIR(inode->i_mode))
56+
if (S_ISDIR(inode->i_mode)) {
5757
mask |= FS_ISDIR;
5858

59-
if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED))
59+
/* sb/mount marks are not interested in name of directory */
60+
if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED))
61+
goto notify_child;
62+
}
63+
64+
/* disconnected dentry cannot notify parent */
65+
if (IS_ROOT(dentry))
6066
goto notify_child;
6167

6268
return __fsnotify_parent(dentry, mask, data, data_type);

include/linux/fsnotify_backend.h

+28-4
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,11 @@
4949
#define FS_OPEN_EXEC_PERM 0x00040000 /* open/exec event in a permission hook */
5050

5151
#define FS_EXCL_UNLINK 0x04000000 /* do not send events if object is unlinked */
52-
/* This inode cares about things that happen to its children. Always set for
53-
* dnotify and inotify. */
52+
/*
53+
* Set on inode mark that cares about things that happen to its children.
54+
* Always set for dnotify and inotify.
55+
* Set on inode/sb/mount marks that care about parent/name info.
56+
*/
5457
#define FS_EVENT_ON_CHILD 0x08000000
5558

5659
#define FS_DN_RENAME 0x10000000 /* file renamed */
@@ -72,14 +75,22 @@
7275
FS_OPEN_EXEC_PERM)
7376

7477
/*
75-
* This is a list of all events that may get sent to a parent based on fs event
76-
* happening to inodes inside that directory.
78+
* This is a list of all events that may get sent to a parent that is watching
79+
* with flag FS_EVENT_ON_CHILD based on fs event on a child of that directory.
7780
*/
7881
#define FS_EVENTS_POSS_ON_CHILD (ALL_FSNOTIFY_PERM_EVENTS | \
7982
FS_ACCESS | FS_MODIFY | FS_ATTRIB | \
8083
FS_CLOSE_WRITE | FS_CLOSE_NOWRITE | \
8184
FS_OPEN | FS_OPEN_EXEC)
8285

86+
/*
87+
* This is a list of all events that may get sent with the parent inode as the
88+
* @to_tell argument of fsnotify().
89+
* It may include events that can be sent to an inode/sb/mount mark, but cannot
90+
* be sent to a parent watching children.
91+
*/
92+
#define FS_EVENTS_POSS_TO_PARENT (FS_EVENTS_POSS_ON_CHILD)
93+
8394
/* Events that can be reported to backends */
8495
#define ALL_FSNOTIFY_EVENTS (ALL_FSNOTIFY_DIRENT_EVENTS | \
8596
FS_EVENTS_POSS_ON_CHILD | \
@@ -397,6 +408,19 @@ extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt);
397408
extern void fsnotify_sb_delete(struct super_block *sb);
398409
extern u32 fsnotify_get_cookie(void);
399410

411+
static inline __u32 fsnotify_parent_needed_mask(__u32 mask)
412+
{
413+
/* FS_EVENT_ON_CHILD is set on marks that want parent/name info */
414+
if (!(mask & FS_EVENT_ON_CHILD))
415+
return 0;
416+
/*
417+
* This object might be watched by a mark that cares about parent/name
418+
* info, does it care about the specific set of events that can be
419+
* reported with parent/name info?
420+
*/
421+
return mask & FS_EVENTS_POSS_TO_PARENT;
422+
}
423+
400424
static inline int fsnotify_inode_watches_children(struct inode *inode)
401425
{
402426
/* FS_EVENT_ON_CHILD is set if the inode may care */

0 commit comments

Comments
 (0)