diff options
Diffstat (limited to 'fs/notify')
-rw-r--r-- | fs/notify/inotify/inotify_user.c | 135 |
1 files changed, 74 insertions, 61 deletions
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index d53a1838d6e8..bed766e435b5 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c | |||
@@ -427,10 +427,61 @@ static unsigned int inotify_poll(struct file *file, poll_table *wait) | |||
427 | return ret; | 427 | return ret; |
428 | } | 428 | } |
429 | 429 | ||
430 | /* | ||
431 | * Get an inotify_kernel_event if one exists and is small | ||
432 | * enough to fit in "count". Return an error pointer if | ||
433 | * not large enough. | ||
434 | * | ||
435 | * Called with the device ev_mutex held. | ||
436 | */ | ||
437 | static struct inotify_kernel_event *get_one_event(struct inotify_device *dev, | ||
438 | size_t count) | ||
439 | { | ||
440 | size_t event_size = sizeof(struct inotify_event); | ||
441 | struct inotify_kernel_event *kevent; | ||
442 | |||
443 | if (list_empty(&dev->events)) | ||
444 | return NULL; | ||
445 | |||
446 | kevent = inotify_dev_get_event(dev); | ||
447 | if (kevent->name) | ||
448 | event_size += kevent->event.len; | ||
449 | |||
450 | if (event_size > count) | ||
451 | return ERR_PTR(-EINVAL); | ||
452 | |||
453 | remove_kevent(dev, kevent); | ||
454 | return kevent; | ||
455 | } | ||
456 | |||
457 | /* | ||
458 | * Copy an event to user space, returning how much we copied. | ||
459 | * | ||
460 | * We already checked that the event size is smaller than the | ||
461 | * buffer we had in "get_one_event()" above. | ||
462 | */ | ||
463 | static ssize_t copy_event_to_user(struct inotify_kernel_event *kevent, | ||
464 | char __user *buf) | ||
465 | { | ||
466 | size_t event_size = sizeof(struct inotify_event); | ||
467 | |||
468 | if (copy_to_user(buf, &kevent->event, event_size)) | ||
469 | return -EFAULT; | ||
470 | |||
471 | if (kevent->name) { | ||
472 | buf += event_size; | ||
473 | |||
474 | if (copy_to_user(buf, kevent->name, kevent->event.len)) | ||
475 | return -EFAULT; | ||
476 | |||
477 | event_size += kevent->event.len; | ||
478 | } | ||
479 | return event_size; | ||
480 | } | ||
481 | |||
430 | static ssize_t inotify_read(struct file *file, char __user *buf, | 482 | static ssize_t inotify_read(struct file *file, char __user *buf, |
431 | size_t count, loff_t *pos) | 483 | size_t count, loff_t *pos) |
432 | { | 484 | { |
433 | size_t event_size = sizeof (struct inotify_event); | ||
434 | struct inotify_device *dev; | 485 | struct inotify_device *dev; |
435 | char __user *start; | 486 | char __user *start; |
436 | int ret; | 487 | int ret; |
@@ -440,81 +491,43 @@ static ssize_t inotify_read(struct file *file, char __user *buf, | |||
440 | dev = file->private_data; | 491 | dev = file->private_data; |
441 | 492 | ||
442 | while (1) { | 493 | while (1) { |
494 | struct inotify_kernel_event *kevent; | ||
443 | 495 | ||
444 | prepare_to_wait(&dev->wq, &wait, TASK_INTERRUPTIBLE); | 496 | prepare_to_wait(&dev->wq, &wait, TASK_INTERRUPTIBLE); |
445 | 497 | ||
446 | mutex_lock(&dev->ev_mutex); | 498 | mutex_lock(&dev->ev_mutex); |
447 | if (!list_empty(&dev->events)) { | 499 | kevent = get_one_event(dev, count); |
448 | ret = 0; | ||
449 | break; | ||
450 | } | ||
451 | mutex_unlock(&dev->ev_mutex); | 500 | mutex_unlock(&dev->ev_mutex); |
452 | 501 | ||
453 | if (file->f_flags & O_NONBLOCK) { | 502 | if (kevent) { |
454 | ret = -EAGAIN; | 503 | ret = PTR_ERR(kevent); |
455 | break; | 504 | if (IS_ERR(kevent)) |
456 | } | 505 | break; |
457 | 506 | ret = copy_event_to_user(kevent, buf); | |
458 | if (signal_pending(current)) { | 507 | free_kevent(kevent); |
459 | ret = -EINTR; | 508 | if (ret < 0) |
460 | break; | 509 | break; |
510 | buf += ret; | ||
511 | count -= ret; | ||
512 | continue; | ||
461 | } | 513 | } |
462 | 514 | ||
463 | schedule(); | 515 | ret = -EAGAIN; |
464 | } | 516 | if (file->f_flags & O_NONBLOCK) |
465 | |||
466 | finish_wait(&dev->wq, &wait); | ||
467 | if (ret) | ||
468 | return ret; | ||
469 | |||
470 | while (1) { | ||
471 | struct inotify_kernel_event *kevent; | ||
472 | |||
473 | ret = buf - start; | ||
474 | if (list_empty(&dev->events)) | ||
475 | break; | 517 | break; |
476 | 518 | ret = -EINTR; | |
477 | kevent = inotify_dev_get_event(dev); | 519 | if (signal_pending(current)) |
478 | if (event_size + kevent->event.len > count) { | ||
479 | if (ret == 0 && count > 0) { | ||
480 | /* | ||
481 | * could not get a single event because we | ||
482 | * didn't have enough buffer space. | ||
483 | */ | ||
484 | ret = -EINVAL; | ||
485 | } | ||
486 | break; | 520 | break; |
487 | } | ||
488 | remove_kevent(dev, kevent); | ||
489 | 521 | ||
490 | /* | 522 | if (start != buf) |
491 | * Must perform the copy_to_user outside the mutex in order | ||
492 | * to avoid a lock order reversal with mmap_sem. | ||
493 | */ | ||
494 | mutex_unlock(&dev->ev_mutex); | ||
495 | |||
496 | if (copy_to_user(buf, &kevent->event, event_size)) { | ||
497 | ret = -EFAULT; | ||
498 | break; | 523 | break; |
499 | } | ||
500 | buf += event_size; | ||
501 | count -= event_size; | ||
502 | |||
503 | if (kevent->name) { | ||
504 | if (copy_to_user(buf, kevent->name, kevent->event.len)){ | ||
505 | ret = -EFAULT; | ||
506 | break; | ||
507 | } | ||
508 | buf += kevent->event.len; | ||
509 | count -= kevent->event.len; | ||
510 | } | ||
511 | |||
512 | free_kevent(kevent); | ||
513 | 524 | ||
514 | mutex_lock(&dev->ev_mutex); | 525 | schedule(); |
515 | } | 526 | } |
516 | mutex_unlock(&dev->ev_mutex); | ||
517 | 527 | ||
528 | finish_wait(&dev->wq, &wait); | ||
529 | if (start != buf && ret != -EFAULT) | ||
530 | ret = buf - start; | ||
518 | return ret; | 531 | return ret; |
519 | } | 532 | } |
520 | 533 | ||