| @@ -47,6 +47,18 @@ struct kmem_cache *fanotify_mark_cache __read_mostly; · 46 unmodified lines |
| 47 | struct kmem_cache *fanotify_event_cachep __read_mostly; | 47 | struct kmem_cache *fanotify_event_cachep __read_mostly; |
| 48 | struct kmem_cache *fanotify_perm_event_cachep __read_mostly; | 48 | struct kmem_cache *fanotify_perm_event_cachep __read_mostly; |
| 49 | | 49 | |
| | 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 | |
| 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; · 4 unmodified lines |
| 57 | static struct fsnotify_event *get_one_event(struct fsnotify_group *group, | 69 | static 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 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, · 99 unmodified lines |
| 174 | return 0; | 196 | return 0; |
| 175 | } | 197 | } |
| 176 | | 198 | |
| | 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 | |
| 177 | static ssize_t copy_event_to_user(struct fsnotify_group *group, | 241 | static 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 | |
| 221 | out_close_fd: | 293 | out_close_fd: |