Skip to content

Commit 5e469c8

Browse files
amir73iljankara
authored andcommitted
fanotify: copy event fid info to user
If group requested FAN_REPORT_FID and event has file identifier, copy that information to user reading the event after event metadata. fid information is formatted as struct fanotify_event_info_fid that includes a generic header struct fanotify_event_info_header, so that other info types could be defined in the future using the same header. metadata->event_len includes the length of the fid information. The fid information includes the filesystem's fsid (see statfs(2)) followed by an NFS file handle of the file that could be passed as an argument to open_by_handle_at(2). Cc: <linux-api@vger.kernel.org> Signed-off-by: Amir Goldstein <amir73il@gmail.com> Signed-off-by: Jan Kara <jack@suse.cz>
1 parent e9e0c89 commit 5e469c8

File tree

3 files changed

+102
-5
lines changed

3 files changed

+102
-5
lines changed

fs/notify/fanotify/fanotify.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ static inline bool fanotify_event_has_ext_fh(struct fanotify_event *event)
9595
event->fh_len > FANOTIFY_INLINE_FH_LEN;
9696
}
9797

98+
static inline void *fanotify_event_fh(struct fanotify_event *event)
99+
{
100+
return fanotify_fid_fh(&event->fid, event->fh_len);
101+
}
102+
98103
/*
99104
* Structure for permission fanotify events. It gets allocated and freed in
100105
* fanotify_handle_event() since we wait there for user response. When the

fs/notify/fanotify/fanotify_user.c

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,18 @@ struct kmem_cache *fanotify_mark_cache __read_mostly;
4747
struct kmem_cache *fanotify_event_cachep __read_mostly;
4848
struct kmem_cache *fanotify_perm_event_cachep __read_mostly;
4949

50+
#define FANOTIFY_EVENT_ALIGN 4
51+
52+
static int fanotify_event_info_len(struct fanotify_event *event)
53+
{
54+
if (!fanotify_event_has_fid(event))
55+
return 0;
56+
57+
return roundup(sizeof(struct fanotify_event_info_fid) +
58+
sizeof(struct file_handle) + event->fh_len,
59+
FANOTIFY_EVENT_ALIGN);
60+
}
61+
5062
/*
5163
* Get an fsnotify notification event if one exists and is small
5264
* enough to fit in "count". Return an error pointer if the count
@@ -57,18 +69,28 @@ struct kmem_cache *fanotify_perm_event_cachep __read_mostly;
5769
static struct fsnotify_event *get_one_event(struct fsnotify_group *group,
5870
size_t count)
5971
{
72+
size_t event_size = FAN_EVENT_METADATA_LEN;
73+
struct fanotify_event *event;
74+
6075
assert_spin_locked(&group->notification_lock);
6176

6277
pr_debug("%s: group=%p count=%zd\n", __func__, group, count);
6378

6479
if (fsnotify_notify_queue_is_empty(group))
6580
return NULL;
6681

67-
if (FAN_EVENT_METADATA_LEN > count)
82+
if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) {
83+
event = FANOTIFY_E(fsnotify_peek_first_event(group));
84+
event_size += fanotify_event_info_len(event);
85+
}
86+
87+
if (event_size > count)
6888
return ERR_PTR(-EINVAL);
6989

70-
/* held the notification_lock the whole time, so this is the
71-
* same event we peeked above */
90+
/*
91+
* Held the notification_lock the whole time, so this is the
92+
* same event we peeked above
93+
*/
7294
return fsnotify_remove_first_event(group);
7395
}
7496

@@ -174,6 +196,48 @@ static int process_access_response(struct fsnotify_group *group,
174196
return 0;
175197
}
176198

199+
static int copy_fid_to_user(struct fanotify_event *event, char __user *buf)
200+
{
201+
struct fanotify_event_info_fid info = { };
202+
struct file_handle handle = { };
203+
size_t fh_len = event->fh_len;
204+
size_t len = fanotify_event_info_len(event);
205+
206+
if (!len)
207+
return 0;
208+
209+
if (WARN_ON_ONCE(len < sizeof(info) + sizeof(handle) + fh_len))
210+
return -EFAULT;
211+
212+
/* Copy event info fid header followed by vaiable sized file handle */
213+
info.hdr.info_type = FAN_EVENT_INFO_TYPE_FID;
214+
info.hdr.len = len;
215+
info.fsid = event->fid.fsid;
216+
if (copy_to_user(buf, &info, sizeof(info)))
217+
return -EFAULT;
218+
219+
buf += sizeof(info);
220+
len -= sizeof(info);
221+
handle.handle_type = event->fh_type;
222+
handle.handle_bytes = fh_len;
223+
if (copy_to_user(buf, &handle, sizeof(handle)))
224+
return -EFAULT;
225+
226+
buf += sizeof(handle);
227+
len -= sizeof(handle);
228+
if (copy_to_user(buf, fanotify_event_fh(event), fh_len))
229+
return -EFAULT;
230+
231+
/* Pad with 0's */
232+
buf += fh_len;
233+
len -= fh_len;
234+
WARN_ON_ONCE(len < 0 || len >= FANOTIFY_EVENT_ALIGN);
235+
if (len > 0 && clear_user(buf, len))
236+
return -EFAULT;
237+
238+
return 0;
239+
}
240+
177241
static ssize_t copy_event_to_user(struct fsnotify_group *group,
178242
struct fsnotify_event *fsn_event,
179243
char __user *buf, size_t count)
@@ -197,6 +261,8 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
197261
fd = create_fd(group, event, &f);
198262
if (fd < 0)
199263
return fd;
264+
} else if (fanotify_event_has_fid(event)) {
265+
metadata.event_len += fanotify_event_info_len(event);
200266
}
201267
metadata.fd = fd;
202268

@@ -208,14 +274,20 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group,
208274
if (WARN_ON_ONCE(metadata.event_len > count))
209275
goto out_close_fd;
210276

211-
if (copy_to_user(buf, &metadata, metadata.event_len))
277+
if (copy_to_user(buf, &metadata, FAN_EVENT_METADATA_LEN))
212278
goto out_close_fd;
213279

214280
if (fanotify_is_perm_event(event->mask))
215281
FANOTIFY_PE(fsn_event)->fd = fd;
216282

217-
if (fd != FAN_NOFD)
283+
if (fanotify_event_has_path(event)) {
218284
fd_install(fd, f);
285+
} else if (fanotify_event_has_fid(event)) {
286+
ret = copy_fid_to_user(event, buf + FAN_EVENT_METADATA_LEN);
287+
if (ret < 0)
288+
return ret;
289+
}
290+
219291
return metadata.event_len;
220292

221293
out_close_fd:

include/uapi/linux/fanotify.h

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,26 @@ struct fanotify_event_metadata {
107107
__s32 pid;
108108
};
109109

110+
#define FAN_EVENT_INFO_TYPE_FID 1
111+
112+
/* Variable length info record following event metadata */
113+
struct fanotify_event_info_header {
114+
__u8 info_type;
115+
__u8 pad;
116+
__u16 len;
117+
};
118+
119+
/* Unique file identifier info record */
120+
struct fanotify_event_info_fid {
121+
struct fanotify_event_info_header hdr;
122+
__kernel_fsid_t fsid;
123+
/*
124+
* Following is an opaque struct file_handle that can be passed as
125+
* an argument to open_by_handle_at(2).
126+
*/
127+
unsigned char handle[0];
128+
};
129+
110130
struct fanotify_response {
111131
__s32 fd;
112132
__u32 response;

0 commit comments

Comments
 (0)