diff options
| -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 | ||
