summaryrefslogtreecommitdiffstats
path: root/fs/notify
diff options
context:
space:
mode:
authorAmir Goldstein <amir73il@gmail.com>2019-01-10 12:04:35 -0500
committerJan Kara <jack@suse.cz>2019-02-07 10:38:35 -0500
commit5e469c830fdb5a1ebaa69b375b87f583326fd296 (patch)
tree0c3ca43d6626960107108b2db0c93a17300ee0d9 /fs/notify
parente9e0c8903009477b630e37a8b6364b26a00720da (diff)
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>
Diffstat (limited to 'fs/notify')
-rw-r--r--fs/notify/fanotify/fanotify.h5
-rw-r--r--fs/notify/fanotify/fanotify_user.c82
2 files changed, 82 insertions, 5 deletions
diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h
index 271482fb9611..4aafc7144c3d 100644
--- a/fs/notify/fanotify/fanotify.h
+++ b/fs/notify/fanotify/fanotify.h
@@ -95,6 +95,11 @@ static inline bool fanotify_event_has_ext_fh(struct fanotify_event *event)
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 in 104 * Structure for permission fanotify events. It gets allocated and freed in
100 * fanotify_handle_event() since we wait there for user response. When the 105 * fanotify_handle_event() since we wait there for user response. When the
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index c965fcf4979e..cd82dd713c91 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -47,6 +47,18 @@ struct kmem_cache *fanotify_mark_cache __read_mostly;
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 small 63 * Get an fsnotify notification event if one exists and is small
52 * enough to fit in "count". Return an error pointer if the count 64 * 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;
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,
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 the 90 /*
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,
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,
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,
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: