| @@ -47,6 +47,18 @@ struct kmem_cache *fanotify_mark_cache __read_mostly; · 46 unmodified lines |
| 47 | 47 | struct kmem_cache *fanotify_event_cachep __read_mostly; |
| 48 | 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 | 63 | * Get an fsnotify notification event if one exists and is small |
| 52 | 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 | 69 | static struct fsnotify_event *get_one_event(struct fsnotify_group *group, |
| 58 | 70 | size_t count) |
| 59 | 71 | { |
| 72 | + size_t event_size = FAN_EVENT_METADATA_LEN; |
| 73 | + struct fanotify_event *event; |
| 74 | + |
| 60 | 75 | assert_spin_locked(&group->notification_lock); |
| 61 | 76 | |
| 62 | 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 | 79 | if (fsnotify_notify_queue_is_empty(group)) |
| 65 | 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 | 88 | return ERR_PTR(-EINVAL); |
| 69 | 89 | |
| 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 | + */ |
| 72 | 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 | 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 | 241 | static ssize_t copy_event_to_user(struct fsnotify_group *group, |
| 178 | 242 | struct fsnotify_event *fsn_event, |
| 179 | 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 | 261 | fd = create_fd(group, event, &f); |
| 198 | 262 | if (fd < 0) |
| 199 | 263 | return fd; |
| 264 | + } else if (fanotify_event_has_fid(event)) { |
| 265 | + metadata.event_len += fanotify_event_info_len(event); |
| 200 | 266 | } |
| 201 | 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 | 274 | if (WARN_ON_ONCE(metadata.event_len > count)) |
| 209 | 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 | 278 | goto out_close_fd; |
| 213 | 279 | |
| 214 | 280 | if (fanotify_is_perm_event(event->mask)) |
| 215 | 281 | FANOTIFY_PE(fsn_event)->fd = fd; |
| 216 | 282 | |
| 217 | | − if (fd != FAN_NOFD) |
| 283 | + if (fanotify_event_has_path(event)) { |
| 218 | 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 | 291 | return metadata.event_len; |
| 220 | 292 | |
| 221 | 293 | out_close_fd: |