Skip to content

Commit 3d3d673

Browse files
committed
Merge tag 'fsnotify_for_v5.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs
Pull fanotify updates from Jan Kara: "Support for new FAN_RENAME fanotify event and support for reporting child info in directory fanotify events (FAN_REPORT_TARGET_FID)" * tag 'fsnotify_for_v5.17-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs: fanotify: wire up FAN_RENAME event fanotify: report old and/or new parent+name in FAN_RENAME event fanotify: record either old name new name or both for FAN_RENAME fanotify: record old and new parent and name in FAN_RENAME event fanotify: support secondary dir fh and name in fanotify_info fanotify: use helpers to parcel fanotify_info buffer fanotify: use macros to get the offset to fanotify_info buffer fsnotify: generate FS_RENAME event with rich information fanotify: introduce group flag FAN_REPORT_TARGET_FID fsnotify: separate mark iterator type from object type enum fsnotify: clarify object type argument
2 parents f079ab0 + 8cc3b1c commit 3d3d673

File tree

12 files changed

+483
-144
lines changed

12 files changed

+483
-144
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/fanotify/fanotify.c

Lines changed: 161 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -76,23 +76,35 @@ static bool fanotify_info_equal(struct fanotify_info *info1,
7676
struct fanotify_info *info2)
7777
{
7878
if (info1->dir_fh_totlen != info2->dir_fh_totlen ||
79+
info1->dir2_fh_totlen != info2->dir2_fh_totlen ||
7980
info1->file_fh_totlen != info2->file_fh_totlen ||
80-
info1->name_len != info2->name_len)
81+
info1->name_len != info2->name_len ||
82+
info1->name2_len != info2->name2_len)
8183
return false;
8284

8385
if (info1->dir_fh_totlen &&
8486
!fanotify_fh_equal(fanotify_info_dir_fh(info1),
8587
fanotify_info_dir_fh(info2)))
8688
return false;
8789

90+
if (info1->dir2_fh_totlen &&
91+
!fanotify_fh_equal(fanotify_info_dir2_fh(info1),
92+
fanotify_info_dir2_fh(info2)))
93+
return false;
94+
8895
if (info1->file_fh_totlen &&
8996
!fanotify_fh_equal(fanotify_info_file_fh(info1),
9097
fanotify_info_file_fh(info2)))
9198
return false;
9299

93-
return !info1->name_len ||
94-
!memcmp(fanotify_info_name(info1), fanotify_info_name(info2),
95-
info1->name_len);
100+
if (info1->name_len &&
101+
memcmp(fanotify_info_name(info1), fanotify_info_name(info2),
102+
info1->name_len))
103+
return false;
104+
105+
return !info1->name2_len ||
106+
!memcmp(fanotify_info_name2(info1), fanotify_info_name2(info2),
107+
info1->name2_len);
96108
}
97109

98110
static bool fanotify_name_event_equal(struct fanotify_name_event *fne1,
@@ -141,6 +153,13 @@ static bool fanotify_should_merge(struct fanotify_event *old,
141153
if ((old->mask & FS_ISDIR) != (new->mask & FS_ISDIR))
142154
return false;
143155

156+
/*
157+
* FAN_RENAME event is reported with special info record types,
158+
* so we cannot merge it with other events.
159+
*/
160+
if ((old->mask & FAN_RENAME) != (new->mask & FAN_RENAME))
161+
return false;
162+
144163
switch (old->type) {
145164
case FANOTIFY_EVENT_TYPE_PATH:
146165
return fanotify_path_equal(fanotify_event_path(old),
@@ -272,8 +291,9 @@ static int fanotify_get_response(struct fsnotify_group *group,
272291
*/
273292
static u32 fanotify_group_event_mask(struct fsnotify_group *group,
274293
struct fsnotify_iter_info *iter_info,
275-
u32 event_mask, const void *data,
276-
int data_type, struct inode *dir)
294+
u32 *match_mask, u32 event_mask,
295+
const void *data, int data_type,
296+
struct inode *dir)
277297
{
278298
__u32 marks_mask = 0, marks_ignored_mask = 0;
279299
__u32 test_mask, user_mask = FANOTIFY_OUTGOING_EVENTS |
@@ -299,7 +319,7 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
299319
return 0;
300320
}
301321

302-
fsnotify_foreach_obj_type(type) {
322+
fsnotify_foreach_iter_type(type) {
303323
if (!fsnotify_iter_should_report_type(iter_info, type))
304324
continue;
305325
mark = iter_info->marks[type];
@@ -318,11 +338,14 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
318338
* If the event is on a child and this mark is on a parent not
319339
* watching children, don't send it!
320340
*/
321-
if (type == FSNOTIFY_OBJ_TYPE_PARENT &&
341+
if (type == FSNOTIFY_ITER_TYPE_PARENT &&
322342
!(mark->mask & FS_EVENT_ON_CHILD))
323343
continue;
324344

325345
marks_mask |= mark->mask;
346+
347+
/* Record the mark types of this group that matched the event */
348+
*match_mask |= 1U << type;
326349
}
327350

328351
test_mask = event_mask & marks_mask & ~marks_ignored_mask;
@@ -411,7 +434,7 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
411434
* be zero in that case if encoding fh len failed.
412435
*/
413436
err = -ENOENT;
414-
if (fh_len < 4 || WARN_ON_ONCE(fh_len % 4))
437+
if (fh_len < 4 || WARN_ON_ONCE(fh_len % 4) || fh_len > MAX_HANDLE_SZ)
415438
goto out_err;
416439

417440
/* No external buffer in a variable size allocated fh */
@@ -458,17 +481,41 @@ static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode,
458481
}
459482

460483
/*
461-
* The inode to use as identifier when reporting fid depends on the event.
462-
* Report the modified directory inode on dirent modification events.
463-
* Report the "victim" inode otherwise.
484+
* FAN_REPORT_FID is ambiguous in that it reports the fid of the child for
485+
* some events and the fid of the parent for create/delete/move events.
486+
*
487+
* With the FAN_REPORT_TARGET_FID flag, the fid of the child is reported
488+
* also in create/delete/move events in addition to the fid of the parent
489+
* and the name of the child.
490+
*/
491+
static inline bool fanotify_report_child_fid(unsigned int fid_mode, u32 mask)
492+
{
493+
if (mask & ALL_FSNOTIFY_DIRENT_EVENTS)
494+
return (fid_mode & FAN_REPORT_TARGET_FID);
495+
496+
return (fid_mode & FAN_REPORT_FID) && !(mask & FAN_ONDIR);
497+
}
498+
499+
/*
500+
* The inode to use as identifier when reporting fid depends on the event
501+
* and the group flags.
502+
*
503+
* With the group flag FAN_REPORT_TARGET_FID, always report the child fid.
504+
*
505+
* Without the group flag FAN_REPORT_TARGET_FID, report the modified directory
506+
* fid on dirent events and the child fid otherwise.
507+
*
464508
* For example:
465-
* FS_ATTRIB reports the child inode even if reported on a watched parent.
466-
* FS_CREATE reports the modified dir inode and not the created inode.
509+
* FS_ATTRIB reports the child fid even if reported on a watched parent.
510+
* FS_CREATE reports the modified dir fid without FAN_REPORT_TARGET_FID.
511+
* and reports the created child fid with FAN_REPORT_TARGET_FID.
467512
*/
468513
static struct inode *fanotify_fid_inode(u32 event_mask, const void *data,
469-
int data_type, struct inode *dir)
514+
int data_type, struct inode *dir,
515+
unsigned int fid_mode)
470516
{
471-
if (event_mask & ALL_FSNOTIFY_DIRENT_EVENTS)
517+
if ((event_mask & ALL_FSNOTIFY_DIRENT_EVENTS) &&
518+
!(fid_mode & FAN_REPORT_TARGET_FID))
472519
return dir;
473520

474521
return fsnotify_data_inode(data, data_type);
@@ -552,25 +599,34 @@ static struct fanotify_event *fanotify_alloc_fid_event(struct inode *id,
552599
return &ffe->fae;
553600
}
554601

555-
static struct fanotify_event *fanotify_alloc_name_event(struct inode *id,
602+
static struct fanotify_event *fanotify_alloc_name_event(struct inode *dir,
556603
__kernel_fsid_t *fsid,
557604
const struct qstr *name,
558605
struct inode *child,
606+
struct dentry *moved,
559607
unsigned int *hash,
560608
gfp_t gfp)
561609
{
562610
struct fanotify_name_event *fne;
563611
struct fanotify_info *info;
564612
struct fanotify_fh *dfh, *ffh;
565-
unsigned int dir_fh_len = fanotify_encode_fh_len(id);
613+
struct inode *dir2 = moved ? d_inode(moved->d_parent) : NULL;
614+
const struct qstr *name2 = moved ? &moved->d_name : NULL;
615+
unsigned int dir_fh_len = fanotify_encode_fh_len(dir);
616+
unsigned int dir2_fh_len = fanotify_encode_fh_len(dir2);
566617
unsigned int child_fh_len = fanotify_encode_fh_len(child);
567-
unsigned int size;
568-
569-
size = sizeof(*fne) + FANOTIFY_FH_HDR_LEN + dir_fh_len;
618+
unsigned long name_len = name ? name->len : 0;
619+
unsigned long name2_len = name2 ? name2->len : 0;
620+
unsigned int len, size;
621+
622+
/* Reserve terminating null byte even for empty name */
623+
size = sizeof(*fne) + name_len + name2_len + 2;
624+
if (dir_fh_len)
625+
size += FANOTIFY_FH_HDR_LEN + dir_fh_len;
626+
if (dir2_fh_len)
627+
size += FANOTIFY_FH_HDR_LEN + dir2_fh_len;
570628
if (child_fh_len)
571629
size += FANOTIFY_FH_HDR_LEN + child_fh_len;
572-
if (name)
573-
size += name->len + 1;
574630
fne = kmalloc(size, gfp);
575631
if (!fne)
576632
return NULL;
@@ -580,24 +636,41 @@ static struct fanotify_event *fanotify_alloc_name_event(struct inode *id,
580636
*hash ^= fanotify_hash_fsid(fsid);
581637
info = &fne->info;
582638
fanotify_info_init(info);
583-
dfh = fanotify_info_dir_fh(info);
584-
info->dir_fh_totlen = fanotify_encode_fh(dfh, id, dir_fh_len, hash, 0);
639+
if (dir_fh_len) {
640+
dfh = fanotify_info_dir_fh(info);
641+
len = fanotify_encode_fh(dfh, dir, dir_fh_len, hash, 0);
642+
fanotify_info_set_dir_fh(info, len);
643+
}
644+
if (dir2_fh_len) {
645+
dfh = fanotify_info_dir2_fh(info);
646+
len = fanotify_encode_fh(dfh, dir2, dir2_fh_len, hash, 0);
647+
fanotify_info_set_dir2_fh(info, len);
648+
}
585649
if (child_fh_len) {
586650
ffh = fanotify_info_file_fh(info);
587-
info->file_fh_totlen = fanotify_encode_fh(ffh, child,
588-
child_fh_len, hash, 0);
651+
len = fanotify_encode_fh(ffh, child, child_fh_len, hash, 0);
652+
fanotify_info_set_file_fh(info, len);
589653
}
590-
if (name) {
591-
long salt = name->len;
592-
654+
if (name_len) {
593655
fanotify_info_copy_name(info, name);
594-
*hash ^= full_name_hash((void *)salt, name->name, name->len);
656+
*hash ^= full_name_hash((void *)name_len, name->name, name_len);
657+
}
658+
if (name2_len) {
659+
fanotify_info_copy_name2(info, name2);
660+
*hash ^= full_name_hash((void *)name2_len, name2->name,
661+
name2_len);
595662
}
596663

597-
pr_debug("%s: ino=%lu size=%u dir_fh_len=%u child_fh_len=%u name_len=%u name='%.*s'\n",
598-
__func__, id->i_ino, size, dir_fh_len, child_fh_len,
664+
pr_debug("%s: size=%u dir_fh_len=%u child_fh_len=%u name_len=%u name='%.*s'\n",
665+
__func__, size, dir_fh_len, child_fh_len,
599666
info->name_len, info->name_len, fanotify_info_name(info));
600667

668+
if (dir2_fh_len) {
669+
pr_debug("%s: dir2_fh_len=%u name2_len=%u name2='%.*s'\n",
670+
__func__, dir2_fh_len, info->name2_len,
671+
info->name2_len, fanotify_info_name2(info));
672+
}
673+
601674
return &fne->fae;
602675
}
603676

@@ -639,19 +712,21 @@ static struct fanotify_event *fanotify_alloc_error_event(
639712
return &fee->fae;
640713
}
641714

642-
static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
643-
u32 mask, const void *data,
644-
int data_type, struct inode *dir,
645-
const struct qstr *file_name,
646-
__kernel_fsid_t *fsid)
715+
static struct fanotify_event *fanotify_alloc_event(
716+
struct fsnotify_group *group,
717+
u32 mask, const void *data, int data_type,
718+
struct inode *dir, const struct qstr *file_name,
719+
__kernel_fsid_t *fsid, u32 match_mask)
647720
{
648721
struct fanotify_event *event = NULL;
649722
gfp_t gfp = GFP_KERNEL_ACCOUNT;
650-
struct inode *id = fanotify_fid_inode(mask, data, data_type, dir);
723+
unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
724+
struct inode *id = fanotify_fid_inode(mask, data, data_type, dir,
725+
fid_mode);
651726
struct inode *dirid = fanotify_dfid_inode(mask, data, data_type, dir);
652727
const struct path *path = fsnotify_data_path(data, data_type);
653-
unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS);
654728
struct mem_cgroup *old_memcg;
729+
struct dentry *moved = NULL;
655730
struct inode *child = NULL;
656731
bool name_event = false;
657732
unsigned int hash = 0;
@@ -660,11 +735,10 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
660735

661736
if ((fid_mode & FAN_REPORT_DIR_FID) && dirid) {
662737
/*
663-
* With both flags FAN_REPORT_DIR_FID and FAN_REPORT_FID, we
664-
* report the child fid for events reported on a non-dir child
738+
* For certain events and group flags, report the child fid
665739
* in addition to reporting the parent fid and maybe child name.
666740
*/
667-
if ((fid_mode & FAN_REPORT_FID) && id != dirid && !ondir)
741+
if (fanotify_report_child_fid(fid_mode, mask) && id != dirid)
668742
child = id;
669743

670744
id = dirid;
@@ -688,6 +762,38 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
688762
} else if ((mask & ALL_FSNOTIFY_DIRENT_EVENTS) || !ondir) {
689763
name_event = true;
690764
}
765+
766+
/*
767+
* In the special case of FAN_RENAME event, use the match_mask
768+
* to determine if we need to report only the old parent+name,
769+
* only the new parent+name or both.
770+
* 'dirid' and 'file_name' are the old parent+name and
771+
* 'moved' has the new parent+name.
772+
*/
773+
if (mask & FAN_RENAME) {
774+
bool report_old, report_new;
775+
776+
if (WARN_ON_ONCE(!match_mask))
777+
return NULL;
778+
779+
/* Report both old and new parent+name if sb watching */
780+
report_old = report_new =
781+
match_mask & (1U << FSNOTIFY_ITER_TYPE_SB);
782+
report_old |=
783+
match_mask & (1U << FSNOTIFY_ITER_TYPE_INODE);
784+
report_new |=
785+
match_mask & (1U << FSNOTIFY_ITER_TYPE_INODE2);
786+
787+
if (!report_old) {
788+
/* Do not report old parent+name */
789+
dirid = NULL;
790+
file_name = NULL;
791+
}
792+
if (report_new) {
793+
/* Report new parent+name */
794+
moved = fsnotify_data_dentry(data, data_type);
795+
}
796+
}
691797
}
692798

693799
/*
@@ -709,9 +815,9 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
709815
} else if (fanotify_is_error_event(mask)) {
710816
event = fanotify_alloc_error_event(group, fsid, data,
711817
data_type, &hash);
712-
} else if (name_event && (file_name || child)) {
713-
event = fanotify_alloc_name_event(id, fsid, file_name, child,
714-
&hash, gfp);
818+
} else if (name_event && (file_name || moved || child)) {
819+
event = fanotify_alloc_name_event(dirid, fsid, file_name, child,
820+
moved, &hash, gfp);
715821
} else if (fid_mode) {
716822
event = fanotify_alloc_fid_event(id, fsid, &hash, gfp);
717823
} else {
@@ -746,7 +852,7 @@ static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info)
746852
int type;
747853
__kernel_fsid_t fsid = {};
748854

749-
fsnotify_foreach_obj_type(type) {
855+
fsnotify_foreach_iter_type(type) {
750856
struct fsnotify_mark_connector *conn;
751857

752858
if (!fsnotify_iter_should_report_type(iter_info, type))
@@ -800,6 +906,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
800906
struct fanotify_event *event;
801907
struct fsnotify_event *fsn_event;
802908
__kernel_fsid_t fsid = {};
909+
u32 match_mask = 0;
803910

804911
BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS);
805912
BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY);
@@ -821,15 +928,17 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
821928
BUILD_BUG_ON(FAN_OPEN_EXEC != FS_OPEN_EXEC);
822929
BUILD_BUG_ON(FAN_OPEN_EXEC_PERM != FS_OPEN_EXEC_PERM);
823930
BUILD_BUG_ON(FAN_FS_ERROR != FS_ERROR);
931+
BUILD_BUG_ON(FAN_RENAME != FS_RENAME);
824932

825-
BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 20);
933+
BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 21);
826934

827-
mask = fanotify_group_event_mask(group, iter_info, mask, data,
828-
data_type, dir);
935+
mask = fanotify_group_event_mask(group, iter_info, &match_mask,
936+
mask, data, data_type, dir);
829937
if (!mask)
830938
return 0;
831939

832-
pr_debug("%s: group=%p mask=%x\n", __func__, group, mask);
940+
pr_debug("%s: group=%p mask=%x report_mask=%x\n", __func__,
941+
group, mask, match_mask);
833942

834943
if (fanotify_is_perm_event(mask)) {
835944
/*
@@ -848,7 +957,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask,
848957
}
849958

850959
event = fanotify_alloc_event(group, mask, data, data_type, dir,
851-
file_name, &fsid);
960+
file_name, &fsid, match_mask);
852961
ret = -ENOMEM;
853962
if (unlikely(!event)) {
854963
/*

0 commit comments

Comments
 (0)