History

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).

Amir Goldstein committed 7 years ago

Commit 5e469c81 parent e9e0c893 files · +102 −5Browse files

Unified · Split

3 files
  1. fs/notify/fanotify/fanotify.h +5 −0
  2. fs/notify/fanotify/fanotify_user.c +77 −5
  3. include/uapi/linux/fanotify.h +20 −0
fs/notify/fanotify/fanotify.h+5−0
@@ -95,6 +95,11 @@ static inline bool fanotify_event_has_ext_fh(struct fanotify_event *event) · 94 unmodified lines
95 event->fh_len > FANOTIFY_INLINE_FH_LEN;95 event->fh_len > FANOTIFY_INLINE_FH_LEN;
96}96}
97 97
98static inline void *fanotify_event_fh(struct fanotify_event *event)
99{
100 return fanotify_fid_fh(&event->fid, event->fh_len);
101}
102
98/*103/*
99 * Structure for permission fanotify events. It gets allocated and freed in104 * Structure for permission fanotify events. It gets allocated and freed in
100 * fanotify_handle_event() since we wait there for user response. When the105 * fanotify_handle_event() since we wait there for user response. When the
fs/notify/fanotify/fanotify_user.c+77−5
@@ -47,6 +47,18 @@ struct kmem_cache *fanotify_mark_cache __read_mostly; · 46 unmodified lines
47struct kmem_cache *fanotify_event_cachep __read_mostly;47struct kmem_cache *fanotify_event_cachep __read_mostly;
48struct kmem_cache *fanotify_perm_event_cachep __read_mostly;48struct kmem_cache *fanotify_perm_event_cachep __read_mostly;
49 49
50#define FANOTIFY_EVENT_ALIGN 4
51
52static 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
50/*62/*
51 * Get an fsnotify notification event if one exists and is small63 * Get an fsnotify notification event if one exists and is small
52 * enough to fit in "count". Return an error pointer if the count64 * enough to fit in "count". Return an error pointer if the count
@@ -57,6 +69,9 @@ struct kmem_cache *fanotify_perm_event_cachep __read_mostly; · 4 unmodified lines
57static struct fsnotify_event *get_one_event(struct fsnotify_group *group,69static struct fsnotify_event *get_one_event(struct fsnotify_group *group,
58 size_t count)70 size_t count)
59{71{
72 size_t event_size = FAN_EVENT_METADATA_LEN;
73 struct fanotify_event *event;
74
60 assert_spin_locked(&group->notification_lock);75 assert_spin_locked(&group->notification_lock);
61 76
62 pr_debug("%s: group=%p count=%zd\n", __func__, group, count);77 pr_debug("%s: group=%p count=%zd\n", __func__, group, count);
@@ -64,11 +79,18 @@ static struct fsnotify_event *get_one_event(struct fsnotify_group *group, · 1 unmodified line
64 if (fsnotify_notify_queue_is_empty(group))79 if (fsnotify_notify_queue_is_empty(group))
65 return NULL;80 return NULL;
66 81
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)
68 return ERR_PTR(-EINVAL);88 return ERR_PTR(-EINVAL);
69 89
70 /* held the notification_lock the whole time, so this is the90 /*
71 * same event we peeked above */91 * Held the notification_lock the whole time, so this is the
92 * same event we peeked above
93 */
72 return fsnotify_remove_first_event(group);94 return fsnotify_remove_first_event(group);
73}95}
74 96
@@ -174,6 +196,48 @@ static int process_access_response(struct fsnotify_group *group, · 99 unmodified lines
174 return 0;196 return 0;
175}197}
176 198
199static 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
177static ssize_t copy_event_to_user(struct fsnotify_group *group,241static ssize_t copy_event_to_user(struct fsnotify_group *group,
178 struct fsnotify_event *fsn_event,242 struct fsnotify_event *fsn_event,
179 char __user *buf, size_t count)243 char __user *buf, size_t count)
@@ -197,6 +261,8 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, · 17 unmodified lines
197 fd = create_fd(group, event, &f);261 fd = create_fd(group, event, &f);
198 if (fd < 0)262 if (fd < 0)
199 return fd;263 return fd;
264 } else if (fanotify_event_has_fid(event)) {
265 metadata.event_len += fanotify_event_info_len(event);
200 }266 }
201 metadata.fd = fd;267 metadata.fd = fd;
202 268
@@ -208,14 +274,20 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, · 5 unmodified lines
208 if (WARN_ON_ONCE(metadata.event_len > count))274 if (WARN_ON_ONCE(metadata.event_len > count))
209 goto out_close_fd;275 goto out_close_fd;
210 276
211 if (copy_to_user(buf, &metadata, metadata.event_len))277 if (copy_to_user(buf, &metadata, FAN_EVENT_METADATA_LEN))
212 goto out_close_fd;278 goto out_close_fd;
213 279
214 if (fanotify_is_perm_event(event->mask))280 if (fanotify_is_perm_event(event->mask))
215 FANOTIFY_PE(fsn_event)->fd = fd;281 FANOTIFY_PE(fsn_event)->fd = fd;
216 282
217 if (fd != FAN_NOFD)283 if (fanotify_event_has_path(event)) {
218 fd_install(fd, f);284 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
219 return metadata.event_len;291 return metadata.event_len;
220 292
221out_close_fd:293out_close_fd:
include/uapi/linux/fanotify.h+20−0
@@ -107,6 +107,26 @@ struct fanotify_event_metadata { · 106 unmodified lines
107 __s32 pid;107 __s32 pid;
108};108};
109 109
110#define FAN_EVENT_INFO_TYPE_FID 1
111
112/* Variable length info record following event metadata */
113struct fanotify_event_info_header {
114 __u8 info_type;
115 __u8 pad;
116 __u16 len;
117};
118
119/* Unique file identifier info record */
120struct 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
110struct fanotify_response {130struct fanotify_response {
111 __s32 fd;131 __s32 fd;
112 __u32 response;132 __u32 response;