Skip to content

Commit e54183f

Browse files
amir73iljankara
authored andcommitted
fsnotify: generate FS_RENAME event with rich information
The dnotify FS_DN_RENAME event is used to request notification about a move within the same parent directory and was always coupled with the FS_MOVED_FROM event. Rename the FS_DN_RENAME event flag to FS_RENAME, decouple it from FS_MOVED_FROM and report it with the moved dentry instead of the moved inode, so it has the information about both old and new parent and name. Generate the FS_RENAME event regardless of same parent dir and apply the "same parent" rule in the generic fsnotify_handle_event() helper that is used to call backends with ->handle_inode_event() method (i.e. dnotify). The ->handle_inode_event() method is not rich enough to report both old and new parent and name anyway. The enriched event is reported to fanotify over the ->handle_event() method with the old and new dir inode marks in marks array slots for ITER_TYPE_INODE and a new iter type slot ITER_TYPE_INODE2. The enriched event will be used for reporting old and new parent+name to fanotify groups with FAN_RENAME events. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Amir Goldstein <[email protected]> Signed-off-by: Jan Kara <[email protected]>
1 parent d61fd65 commit e54183f

File tree

5 files changed

+41
-16
lines changed

5 files changed

+41
-16
lines changed

fs/notify/dnotify/dnotify.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ static __u32 convert_arg(unsigned long arg)
196196
if (arg & DN_ATTRIB)
197197
new_mask |= FS_ATTRIB;
198198
if (arg & DN_RENAME)
199-
new_mask |= FS_DN_RENAME;
199+
new_mask |= FS_RENAME;
200200
if (arg & DN_CREATE)
201201
new_mask |= (FS_CREATE | FS_MOVED_TO);
202202

fs/notify/fsnotify.c

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,18 @@ static int fsnotify_handle_event(struct fsnotify_group *group, __u32 mask,
279279
WARN_ON_ONCE(fsnotify_iter_vfsmount_mark(iter_info)))
280280
return 0;
281281

282+
/*
283+
* For FS_RENAME, 'dir' is old dir and 'data' is new dentry.
284+
* The only ->handle_inode_event() backend that supports FS_RENAME is
285+
* dnotify, where it means file was renamed within same parent.
286+
*/
287+
if (mask & FS_RENAME) {
288+
struct dentry *moved = fsnotify_data_dentry(data, data_type);
289+
290+
if (dir != moved->d_parent->d_inode)
291+
return 0;
292+
}
293+
282294
if (parent_mark) {
283295
/*
284296
* parent_mark indicates that the parent inode is watching
@@ -469,7 +481,9 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
469481
struct super_block *sb = fsnotify_data_sb(data, data_type);
470482
struct fsnotify_iter_info iter_info = {};
471483
struct mount *mnt = NULL;
472-
struct inode *parent = NULL;
484+
struct inode *inode2 = NULL;
485+
struct dentry *moved;
486+
int inode2_type;
473487
int ret = 0;
474488
__u32 test_mask, marks_mask;
475489

@@ -479,12 +493,19 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
479493
if (!inode) {
480494
/* Dirent event - report on TYPE_INODE to dir */
481495
inode = dir;
496+
/* For FS_RENAME, inode is old_dir and inode2 is new_dir */
497+
if (mask & FS_RENAME) {
498+
moved = fsnotify_data_dentry(data, data_type);
499+
inode2 = moved->d_parent->d_inode;
500+
inode2_type = FSNOTIFY_ITER_TYPE_INODE2;
501+
}
482502
} else if (mask & FS_EVENT_ON_CHILD) {
483503
/*
484504
* Event on child - report on TYPE_PARENT to dir if it is
485505
* watching children and on TYPE_INODE to child.
486506
*/
487-
parent = dir;
507+
inode2 = dir;
508+
inode2_type = FSNOTIFY_ITER_TYPE_PARENT;
488509
}
489510

490511
/*
@@ -497,16 +518,16 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
497518
if (!sb->s_fsnotify_marks &&
498519
(!mnt || !mnt->mnt_fsnotify_marks) &&
499520
(!inode || !inode->i_fsnotify_marks) &&
500-
(!parent || !parent->i_fsnotify_marks))
521+
(!inode2 || !inode2->i_fsnotify_marks))
501522
return 0;
502523

503524
marks_mask = sb->s_fsnotify_mask;
504525
if (mnt)
505526
marks_mask |= mnt->mnt_fsnotify_mask;
506527
if (inode)
507528
marks_mask |= inode->i_fsnotify_mask;
508-
if (parent)
509-
marks_mask |= parent->i_fsnotify_mask;
529+
if (inode2)
530+
marks_mask |= inode2->i_fsnotify_mask;
510531

511532

512533
/*
@@ -529,9 +550,9 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
529550
iter_info.marks[FSNOTIFY_ITER_TYPE_INODE] =
530551
fsnotify_first_mark(&inode->i_fsnotify_marks);
531552
}
532-
if (parent) {
533-
iter_info.marks[FSNOTIFY_ITER_TYPE_PARENT] =
534-
fsnotify_first_mark(&parent->i_fsnotify_marks);
553+
if (inode2) {
554+
iter_info.marks[inode2_type] =
555+
fsnotify_first_mark(&inode2->i_fsnotify_marks);
535556
}
536557

537558
/*

include/linux/dnotify.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ struct dnotify_struct {
2626
FS_MODIFY | FS_MODIFY_CHILD |\
2727
FS_ACCESS | FS_ACCESS_CHILD |\
2828
FS_ATTRIB | FS_ATTRIB_CHILD |\
29-
FS_CREATE | FS_DN_RENAME |\
29+
FS_CREATE | FS_RENAME |\
3030
FS_MOVED_FROM | FS_MOVED_TO)
3131

3232
extern int dir_notify_enable;

include/linux/fsnotify.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -144,16 +144,19 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
144144
u32 fs_cookie = fsnotify_get_cookie();
145145
__u32 old_dir_mask = FS_MOVED_FROM;
146146
__u32 new_dir_mask = FS_MOVED_TO;
147+
__u32 rename_mask = FS_RENAME;
147148
const struct qstr *new_name = &moved->d_name;
148149

149-
if (old_dir == new_dir)
150-
old_dir_mask |= FS_DN_RENAME;
151-
152150
if (isdir) {
153151
old_dir_mask |= FS_ISDIR;
154152
new_dir_mask |= FS_ISDIR;
153+
rename_mask |= FS_ISDIR;
155154
}
156155

156+
/* Event with information about both old and new parent+name */
157+
fsnotify_name(rename_mask, moved, FSNOTIFY_EVENT_DENTRY,
158+
old_dir, old_name, 0);
159+
157160
fsnotify_name(old_dir_mask, source, FSNOTIFY_EVENT_INODE,
158161
old_dir, old_name, fs_cookie);
159162
fsnotify_name(new_dir_mask, source, FSNOTIFY_EVENT_INODE,

include/linux/fsnotify_backend.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
*/
6464
#define FS_EVENT_ON_CHILD 0x08000000
6565

66-
#define FS_DN_RENAME 0x10000000 /* file renamed */
66+
#define FS_RENAME 0x10000000 /* File was renamed */
6767
#define FS_DN_MULTISHOT 0x20000000 /* dnotify multishot */
6868
#define FS_ISDIR 0x40000000 /* event occurred against dir */
6969
#define FS_IN_ONESHOT 0x80000000 /* only send event once */
@@ -76,7 +76,7 @@
7676
* The watching parent may get an FS_ATTRIB|FS_EVENT_ON_CHILD event
7777
* when a directory entry inside a child subdir changes.
7878
*/
79-
#define ALL_FSNOTIFY_DIRENT_EVENTS (FS_CREATE | FS_DELETE | FS_MOVE)
79+
#define ALL_FSNOTIFY_DIRENT_EVENTS (FS_CREATE | FS_DELETE | FS_MOVE | FS_RENAME)
8080

8181
#define ALL_FSNOTIFY_PERM_EVENTS (FS_OPEN_PERM | FS_ACCESS_PERM | \
8282
FS_OPEN_EXEC_PERM)
@@ -101,7 +101,7 @@
101101
/* Events that can be reported to backends */
102102
#define ALL_FSNOTIFY_EVENTS (ALL_FSNOTIFY_DIRENT_EVENTS | \
103103
FS_EVENTS_POSS_ON_CHILD | \
104-
FS_DELETE_SELF | FS_MOVE_SELF | FS_DN_RENAME | \
104+
FS_DELETE_SELF | FS_MOVE_SELF | \
105105
FS_UNMOUNT | FS_Q_OVERFLOW | FS_IN_IGNORED | \
106106
FS_ERROR)
107107

@@ -349,6 +349,7 @@ enum fsnotify_iter_type {
349349
FSNOTIFY_ITER_TYPE_VFSMOUNT,
350350
FSNOTIFY_ITER_TYPE_SB,
351351
FSNOTIFY_ITER_TYPE_PARENT,
352+
FSNOTIFY_ITER_TYPE_INODE2,
352353
FSNOTIFY_ITER_TYPE_COUNT
353354
};
354355

0 commit comments

Comments
 (0)