Skip to content

Commit 44d705b

Browse files
amir73iljankara
authored andcommitted
fanotify: report name info for FAN_DIR_MODIFY event
Report event FAN_DIR_MODIFY with name in a variable length record similar to how fid's are reported. With name info reporting implemented, setting FAN_DIR_MODIFY in mark mask is now allowed. When events are reported with name, the reported fid identifies the directory and the name follows the fid. The info record type for this event info is FAN_EVENT_INFO_TYPE_DFID_NAME. For now, all reported events have at most one info record which is either FAN_EVENT_INFO_TYPE_FID or FAN_EVENT_INFO_TYPE_DFID_NAME (for FAN_DIR_MODIFY). Later on, events "on child" will report both records. There are several ways that an application can use this information: 1. When watching a single directory, the name is always relative to the watched directory, so application need to fstatat(2) the name relative to the watched directory. 2. When watching a set of directories, the application could keep a map of dirfd for all watched directories and hash the map by fid obtained with name_to_handle_at(2). When getting a name event, the fid in the event info could be used to lookup the base dirfd in the map and then call fstatat(2) with that dirfd. 3. When watching a filesystem (FAN_MARK_FILESYSTEM) or a large set of directories, the application could use open_by_handle_at(2) with the fid in event info to obtain dirfd for the directory where event happened and call fstatat(2) with this dirfd. The last option scales better for a large number of watched directories. The first two options may be available in the future also for non privileged fanotify watchers, because open_by_handle_at(2) requires the CAP_DAC_READ_SEARCH capability. Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Amir Goldstein <[email protected]> Signed-off-by: Jan Kara <[email protected]>
1 parent cacfb95 commit 44d705b

File tree

4 files changed

+100
-30
lines changed

4 files changed

+100
-30
lines changed

fs/notify/fanotify/fanotify.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ static int fanotify_handle_event(struct fsnotify_group *group,
520520
BUILD_BUG_ON(FAN_OPEN_EXEC != FS_OPEN_EXEC);
521521
BUILD_BUG_ON(FAN_OPEN_EXEC_PERM != FS_OPEN_EXEC_PERM);
522522

523-
BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 19);
523+
BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 20);
524524

525525
mask = fanotify_group_event_mask(group, iter_info, mask, data,
526526
data_type);

fs/notify/fanotify/fanotify_user.c

Lines changed: 90 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -51,22 +51,35 @@ struct kmem_cache *fanotify_path_event_cachep __read_mostly;
5151
struct kmem_cache *fanotify_perm_event_cachep __read_mostly;
5252

5353
#define FANOTIFY_EVENT_ALIGN 4
54+
#define FANOTIFY_INFO_HDR_LEN \
55+
(sizeof(struct fanotify_event_info_fid) + sizeof(struct file_handle))
5456

55-
static int fanotify_fid_info_len(int fh_len)
57+
static int fanotify_fid_info_len(int fh_len, int name_len)
5658
{
57-
return roundup(sizeof(struct fanotify_event_info_fid) +
58-
sizeof(struct file_handle) + fh_len,
59-
FANOTIFY_EVENT_ALIGN);
59+
int info_len = fh_len;
60+
61+
if (name_len)
62+
info_len += name_len + 1;
63+
64+
return roundup(FANOTIFY_INFO_HDR_LEN + info_len, FANOTIFY_EVENT_ALIGN);
6065
}
6166

6267
static int fanotify_event_info_len(struct fanotify_event *event)
6368
{
69+
int info_len = 0;
6470
int fh_len = fanotify_event_object_fh_len(event);
6571

66-
if (!fh_len)
67-
return 0;
72+
if (fh_len)
73+
info_len += fanotify_fid_info_len(fh_len, 0);
6874

69-
return fanotify_fid_info_len(fh_len);
75+
if (fanotify_event_name_len(event)) {
76+
struct fanotify_name_event *fne = FANOTIFY_NE(event);
77+
78+
info_len += fanotify_fid_info_len(fne->dir_fh.len,
79+
fne->name_len);
80+
}
81+
82+
return info_len;
7083
}
7184

7285
/*
@@ -204,40 +217,55 @@ static int process_access_response(struct fsnotify_group *group,
204217
return -ENOENT;
205218
}
206219

207-
static int copy_fid_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
208-
char __user *buf)
220+
static int copy_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
221+
const char *name, size_t name_len,
222+
char __user *buf, size_t count)
209223
{
210224
struct fanotify_event_info_fid info = { };
211225
struct file_handle handle = { };
212226
unsigned char bounce[FANOTIFY_INLINE_FH_LEN], *fh_buf;
213227
size_t fh_len = fh ? fh->len : 0;
214-
size_t len = fanotify_fid_info_len(fh_len);
228+
size_t info_len = fanotify_fid_info_len(fh_len, name_len);
229+
size_t len = info_len;
230+
231+
pr_debug("%s: fh_len=%zu name_len=%zu, info_len=%zu, count=%zu\n",
232+
__func__, fh_len, name_len, info_len, count);
215233

216-
if (!len)
234+
if (!fh_len || (name && !name_len))
217235
return 0;
218236

219-
if (WARN_ON_ONCE(len < sizeof(info) + sizeof(handle) + fh_len))
237+
if (WARN_ON_ONCE(len < sizeof(info) || len > count))
220238
return -EFAULT;
221239

222-
/* Copy event info fid header followed by vaiable sized file handle */
223-
info.hdr.info_type = FAN_EVENT_INFO_TYPE_FID;
240+
/*
241+
* Copy event info fid header followed by variable sized file handle
242+
* and optionally followed by variable sized filename.
243+
*/
244+
info.hdr.info_type = name_len ? FAN_EVENT_INFO_TYPE_DFID_NAME :
245+
FAN_EVENT_INFO_TYPE_FID;
224246
info.hdr.len = len;
225247
info.fsid = *fsid;
226248
if (copy_to_user(buf, &info, sizeof(info)))
227249
return -EFAULT;
228250

229251
buf += sizeof(info);
230252
len -= sizeof(info);
253+
if (WARN_ON_ONCE(len < sizeof(handle)))
254+
return -EFAULT;
255+
231256
handle.handle_type = fh->type;
232257
handle.handle_bytes = fh_len;
233258
if (copy_to_user(buf, &handle, sizeof(handle)))
234259
return -EFAULT;
235260

236261
buf += sizeof(handle);
237262
len -= sizeof(handle);
263+
if (WARN_ON_ONCE(len < fh_len))
264+
return -EFAULT;
265+
238266
/*
239-
* For an inline fh, copy through stack to exclude the copy from
240-
* usercopy hardening protections.
267+
* For an inline fh and inline file name, copy through stack to exclude
268+
* the copy from usercopy hardening protections.
241269
*/
242270
fh_buf = fanotify_fh_buf(fh);
243271
if (fh_len <= FANOTIFY_INLINE_FH_LEN) {
@@ -247,14 +275,28 @@ static int copy_fid_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh,
247275
if (copy_to_user(buf, fh_buf, fh_len))
248276
return -EFAULT;
249277

250-
/* Pad with 0's */
251278
buf += fh_len;
252279
len -= fh_len;
280+
281+
if (name_len) {
282+
/* Copy the filename with terminating null */
283+
name_len++;
284+
if (WARN_ON_ONCE(len < name_len))
285+
return -EFAULT;
286+
287+
if (copy_to_user(buf, name, name_len))
288+
return -EFAULT;
289+
290+
buf += name_len;
291+
len -= name_len;
292+
}
293+
294+
/* Pad with 0's */
253295
WARN_ON_ONCE(len < 0 || len >= FANOTIFY_EVENT_ALIGN);
254296
if (len > 0 && clear_user(buf, len))
255297
return -EFAULT;
256298

257-
return 0;
299+
return info_len;
258300
}
259301

260302
static ssize_t copy_event_to_user(struct fsnotify_group *group,
@@ -268,16 +310,15 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
268310

269311
pr_debug("%s: group=%p event=%p\n", __func__, group, event);
270312

271-
metadata.event_len = FAN_EVENT_METADATA_LEN;
313+
metadata.event_len = FAN_EVENT_METADATA_LEN +
314+
fanotify_event_info_len(event);
272315
metadata.metadata_len = FAN_EVENT_METADATA_LEN;
273316
metadata.vers = FANOTIFY_METADATA_VERSION;
274317
metadata.reserved = 0;
275318
metadata.mask = event->mask & FANOTIFY_OUTGOING_EVENTS;
276319
metadata.pid = pid_vnr(event->pid);
277320

278-
if (fanotify_event_object_fh(event)) {
279-
metadata.event_len += fanotify_event_info_len(event);
280-
} else if (path && path->mnt && path->dentry) {
321+
if (path && path->mnt && path->dentry) {
281322
fd = create_fd(group, path, &f);
282323
if (fd < 0)
283324
return fd;
@@ -295,17 +336,39 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
295336
if (copy_to_user(buf, &metadata, FAN_EVENT_METADATA_LEN))
296337
goto out_close_fd;
297338

339+
buf += FAN_EVENT_METADATA_LEN;
340+
count -= FAN_EVENT_METADATA_LEN;
341+
298342
if (fanotify_is_perm_event(event->mask))
299343
FANOTIFY_PERM(event)->fd = fd;
300344

301-
if (f) {
345+
if (f)
302346
fd_install(fd, f);
303-
} else if (fanotify_event_object_fh(event)) {
304-
ret = copy_fid_to_user(fanotify_event_fsid(event),
305-
fanotify_event_object_fh(event),
306-
buf + FAN_EVENT_METADATA_LEN);
347+
348+
/* Event info records order is: dir fid + name, child fid */
349+
if (fanotify_event_name_len(event)) {
350+
struct fanotify_name_event *fne = FANOTIFY_NE(event);
351+
352+
ret = copy_info_to_user(fanotify_event_fsid(event),
353+
fanotify_event_dir_fh(event),
354+
fne->name, fne->name_len,
355+
buf, count);
307356
if (ret < 0)
308357
return ret;
358+
359+
buf += ret;
360+
count -= ret;
361+
}
362+
363+
if (fanotify_event_object_fh_len(event)) {
364+
ret = copy_info_to_user(fanotify_event_fsid(event),
365+
fanotify_event_object_fh(event),
366+
NULL, 0, buf, count);
367+
if (ret < 0)
368+
return ret;
369+
370+
buf += ret;
371+
count -= ret;
309372
}
310373

311374
return metadata.event_len;

include/linux/fanotify.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@
4747
* Directory entry modification events - reported only to directory
4848
* where entry is modified and not to a watching parent.
4949
*/
50-
#define FANOTIFY_DIRENT_EVENTS (FAN_MOVE | FAN_CREATE | FAN_DELETE)
50+
#define FANOTIFY_DIRENT_EVENTS (FAN_MOVE | FAN_CREATE | FAN_DELETE | \
51+
FAN_DIR_MODIFY)
5152

5253
/* Events that can only be reported with data type FSNOTIFY_EVENT_INODE */
5354
#define FANOTIFY_INODE_EVENTS (FANOTIFY_DIRENT_EVENTS | \

include/uapi/linux/fanotify.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ struct fanotify_event_metadata {
117117
};
118118

119119
#define FAN_EVENT_INFO_TYPE_FID 1
120+
#define FAN_EVENT_INFO_TYPE_DFID_NAME 2
120121

121122
/* Variable length info record following event metadata */
122123
struct fanotify_event_info_header {
@@ -125,7 +126,12 @@ struct fanotify_event_info_header {
125126
__u16 len;
126127
};
127128

128-
/* Unique file identifier info record */
129+
/*
130+
* Unique file identifier info record. This is used both for
131+
* FAN_EVENT_INFO_TYPE_FID records and for FAN_EVENT_INFO_TYPE_DFID_NAME
132+
* records. For FAN_EVENT_INFO_TYPE_DFID_NAME there is additionally a null
133+
* terminated name immediately after the file handle.
134+
*/
129135
struct fanotify_event_info_fid {
130136
struct fanotify_event_info_header hdr;
131137
__kernel_fsid_t fsid;

0 commit comments

Comments
 (0)