Skip to content

Commit 7326e38

Browse files
amir73iljankara
authored andcommitted
fanotify: report old and/or new parent+name in FAN_RENAME event
In the special case of FAN_RENAME event, we report old or new or both old and new parent+name. A single info record will be reported if either the old or new dir is watched and two records will be reported if both old and new dir (or their filesystem) are watched. The old and new parent+name are reported using new info record types FAN_EVENT_INFO_TYPE_{OLD,NEW}_DFID_NAME, so if a single info record is reported, it is clear to the application, to which dir entry the fid+name info is referring to. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Amir Goldstein <[email protected]> Signed-off-by: Jan Kara <[email protected]>
1 parent 2bfbccc commit 7326e38

File tree

4 files changed

+78
-5
lines changed

4 files changed

+78
-5
lines changed

fs/notify/fanotify/fanotify.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,13 @@ static bool fanotify_should_merge(struct fanotify_event *old,
153153
if ((old->mask & FS_ISDIR) != (new->mask & FS_ISDIR))
154154
return false;
155155

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+
156163
switch (old->type) {
157164
case FANOTIFY_EVENT_TYPE_PATH:
158165
return fanotify_path_equal(fanotify_event_path(old),

fs/notify/fanotify/fanotify.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,13 @@ static inline int fanotify_event_dir_fh_len(struct fanotify_event *event)
373373
return info ? fanotify_info_dir_fh_len(info) : 0;
374374
}
375375

376+
static inline int fanotify_event_dir2_fh_len(struct fanotify_event *event)
377+
{
378+
struct fanotify_info *info = fanotify_event_info(event);
379+
380+
return info ? fanotify_info_dir2_fh_len(info) : 0;
381+
}
382+
376383
static inline bool fanotify_event_has_object_fh(struct fanotify_event *event)
377384
{
378385
/* For error events, even zeroed fh are reported. */
@@ -386,6 +393,17 @@ static inline bool fanotify_event_has_dir_fh(struct fanotify_event *event)
386393
return fanotify_event_dir_fh_len(event) > 0;
387394
}
388395

396+
static inline bool fanotify_event_has_dir2_fh(struct fanotify_event *event)
397+
{
398+
return fanotify_event_dir2_fh_len(event) > 0;
399+
}
400+
401+
static inline bool fanotify_event_has_any_dir_fh(struct fanotify_event *event)
402+
{
403+
return fanotify_event_has_dir_fh(event) ||
404+
fanotify_event_has_dir2_fh(event);
405+
}
406+
389407
struct fanotify_path_event {
390408
struct fanotify_event fae;
391409
struct path path;

fs/notify/fanotify/fanotify_user.c

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,29 @@ static int fanotify_fid_info_len(int fh_len, int name_len)
129129
FANOTIFY_EVENT_ALIGN);
130130
}
131131

132+
/* FAN_RENAME may have one or two dir+name info records */
133+
static int fanotify_dir_name_info_len(struct fanotify_event *event)
134+
{
135+
struct fanotify_info *info = fanotify_event_info(event);
136+
int dir_fh_len = fanotify_event_dir_fh_len(event);
137+
int dir2_fh_len = fanotify_event_dir2_fh_len(event);
138+
int info_len = 0;
139+
140+
if (dir_fh_len)
141+
info_len += fanotify_fid_info_len(dir_fh_len,
142+
info->name_len);
143+
if (dir2_fh_len)
144+
info_len += fanotify_fid_info_len(dir2_fh_len,
145+
info->name2_len);
146+
147+
return info_len;
148+
}
149+
132150
static size_t fanotify_event_len(unsigned int info_mode,
133151
struct fanotify_event *event)
134152
{
135153
size_t event_len = FAN_EVENT_METADATA_LEN;
136154
struct fanotify_info *info;
137-
int dir_fh_len;
138155
int fh_len;
139156
int dot_len = 0;
140157

@@ -146,9 +163,8 @@ static size_t fanotify_event_len(unsigned int info_mode,
146163

147164
info = fanotify_event_info(event);
148165

149-
if (fanotify_event_has_dir_fh(event)) {
150-
dir_fh_len = fanotify_event_dir_fh_len(event);
151-
event_len += fanotify_fid_info_len(dir_fh_len, info->name_len);
166+
if (fanotify_event_has_any_dir_fh(event)) {
167+
event_len += fanotify_dir_name_info_len(event);
152168
} else if ((info_mode & FAN_REPORT_NAME) &&
153169
(event->mask & FAN_ONDIR)) {
154170
/*
@@ -379,6 +395,8 @@ static int copy_fid_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
379395
return -EFAULT;
380396
break;
381397
case FAN_EVENT_INFO_TYPE_DFID_NAME:
398+
case FAN_EVENT_INFO_TYPE_OLD_DFID_NAME:
399+
case FAN_EVENT_INFO_TYPE_NEW_DFID_NAME:
382400
if (WARN_ON_ONCE(!name || !name_len))
383401
return -EFAULT;
384402
break;
@@ -478,11 +496,19 @@ static int copy_info_records_to_user(struct fanotify_event *event,
478496
unsigned int pidfd_mode = info_mode & FAN_REPORT_PIDFD;
479497

480498
/*
481-
* Event info records order is as follows: dir fid + name, child fid.
499+
* Event info records order is as follows:
500+
* 1. dir fid + name
501+
* 2. (optional) new dir fid + new name
502+
* 3. (optional) child fid
482503
*/
483504
if (fanotify_event_has_dir_fh(event)) {
484505
info_type = info->name_len ? FAN_EVENT_INFO_TYPE_DFID_NAME :
485506
FAN_EVENT_INFO_TYPE_DFID;
507+
508+
/* FAN_RENAME uses special info types */
509+
if (event->mask & FAN_RENAME)
510+
info_type = FAN_EVENT_INFO_TYPE_OLD_DFID_NAME;
511+
486512
ret = copy_fid_info_to_user(fanotify_event_fsid(event),
487513
fanotify_info_dir_fh(info),
488514
info_type,
@@ -496,6 +522,22 @@ static int copy_info_records_to_user(struct fanotify_event *event,
496522
total_bytes += ret;
497523
}
498524

525+
/* New dir fid+name may be reported in addition to old dir fid+name */
526+
if (fanotify_event_has_dir2_fh(event)) {
527+
info_type = FAN_EVENT_INFO_TYPE_NEW_DFID_NAME;
528+
ret = copy_fid_info_to_user(fanotify_event_fsid(event),
529+
fanotify_info_dir2_fh(info),
530+
info_type,
531+
fanotify_info_name2(info),
532+
info->name2_len, buf, count);
533+
if (ret < 0)
534+
return ret;
535+
536+
buf += ret;
537+
count -= ret;
538+
total_bytes += ret;
539+
}
540+
499541
if (fanotify_event_has_object_fh(event)) {
500542
const char *dot = NULL;
501543
int dot_len = 0;

include/uapi/linux/fanotify.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,12 @@ struct fanotify_event_metadata {
134134
#define FAN_EVENT_INFO_TYPE_PIDFD 4
135135
#define FAN_EVENT_INFO_TYPE_ERROR 5
136136

137+
/* Special info types for FAN_RENAME */
138+
#define FAN_EVENT_INFO_TYPE_OLD_DFID_NAME 10
139+
/* Reserved for FAN_EVENT_INFO_TYPE_OLD_DFID 11 */
140+
#define FAN_EVENT_INFO_TYPE_NEW_DFID_NAME 12
141+
/* Reserved for FAN_EVENT_INFO_TYPE_NEW_DFID 13 */
142+
137143
/* Variable length info record following event metadata */
138144
struct fanotify_event_info_header {
139145
__u8 info_type;

0 commit comments

Comments
 (0)