diff options
-rw-r--r-- | fs/notify/inotify/inotify_user.c | 194 |
1 files changed, 139 insertions, 55 deletions
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index e46ca685b9be..653c507b1bb3 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c | |||
@@ -357,6 +357,77 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns | |||
357 | return error; | 357 | return error; |
358 | } | 358 | } |
359 | 359 | ||
360 | static int inotify_add_to_idr(struct idr *idr, spinlock_t *idr_lock, | ||
361 | int last_wd, | ||
362 | struct inotify_inode_mark_entry *ientry) | ||
363 | { | ||
364 | int ret; | ||
365 | |||
366 | do { | ||
367 | if (unlikely(!idr_pre_get(idr, GFP_KERNEL))) | ||
368 | return -ENOMEM; | ||
369 | |||
370 | spin_lock(idr_lock); | ||
371 | ret = idr_get_new_above(idr, ientry, last_wd + 1, | ||
372 | &ientry->wd); | ||
373 | /* we added the mark to the idr, take a reference */ | ||
374 | if (!ret) | ||
375 | fsnotify_get_mark(&ientry->fsn_entry); | ||
376 | spin_unlock(idr_lock); | ||
377 | } while (ret == -EAGAIN); | ||
378 | |||
379 | return ret; | ||
380 | } | ||
381 | |||
382 | static struct inotify_inode_mark_entry *inotify_idr_find_locked(struct fsnotify_group *group, | ||
383 | int wd) | ||
384 | { | ||
385 | struct idr *idr = &group->inotify_data.idr; | ||
386 | spinlock_t *idr_lock = &group->inotify_data.idr_lock; | ||
387 | struct inotify_inode_mark_entry *ientry; | ||
388 | |||
389 | assert_spin_locked(idr_lock); | ||
390 | |||
391 | ientry = idr_find(idr, wd); | ||
392 | if (ientry) { | ||
393 | struct fsnotify_mark_entry *fsn_entry = &ientry->fsn_entry; | ||
394 | |||
395 | fsnotify_get_mark(fsn_entry); | ||
396 | /* One ref for being in the idr, one ref we just took */ | ||
397 | BUG_ON(atomic_read(&fsn_entry->refcnt) < 2); | ||
398 | } | ||
399 | |||
400 | return ientry; | ||
401 | } | ||
402 | |||
403 | static struct inotify_inode_mark_entry *inotify_idr_find(struct fsnotify_group *group, | ||
404 | int wd) | ||
405 | { | ||
406 | struct inotify_inode_mark_entry *ientry; | ||
407 | spinlock_t *idr_lock = &group->inotify_data.idr_lock; | ||
408 | |||
409 | spin_lock(idr_lock); | ||
410 | ientry = inotify_idr_find_locked(group, wd); | ||
411 | spin_unlock(idr_lock); | ||
412 | |||
413 | return ientry; | ||
414 | } | ||
415 | |||
416 | static void do_inotify_remove_from_idr(struct fsnotify_group *group, | ||
417 | struct inotify_inode_mark_entry *ientry) | ||
418 | { | ||
419 | struct idr *idr = &group->inotify_data.idr; | ||
420 | spinlock_t *idr_lock = &group->inotify_data.idr_lock; | ||
421 | int wd = ientry->wd; | ||
422 | |||
423 | assert_spin_locked(idr_lock); | ||
424 | |||
425 | idr_remove(idr, wd); | ||
426 | |||
427 | /* removed from the idr, drop that ref */ | ||
428 | fsnotify_put_mark(&ientry->fsn_entry); | ||
429 | } | ||
430 | |||
360 | /* | 431 | /* |
361 | * Remove the mark from the idr (if present) and drop the reference | 432 | * Remove the mark from the idr (if present) and drop the reference |
362 | * on the mark because it was in the idr. | 433 | * on the mark because it was in the idr. |
@@ -364,42 +435,72 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns | |||
364 | static void inotify_remove_from_idr(struct fsnotify_group *group, | 435 | static void inotify_remove_from_idr(struct fsnotify_group *group, |
365 | struct inotify_inode_mark_entry *ientry) | 436 | struct inotify_inode_mark_entry *ientry) |
366 | { | 437 | { |
367 | struct idr *idr; | 438 | spinlock_t *idr_lock = &group->inotify_data.idr_lock; |
368 | struct fsnotify_mark_entry *entry; | 439 | struct inotify_inode_mark_entry *found_ientry = NULL; |
369 | struct inotify_inode_mark_entry *found_ientry; | ||
370 | int wd; | 440 | int wd; |
371 | 441 | ||
372 | spin_lock(&group->inotify_data.idr_lock); | 442 | spin_lock(idr_lock); |
373 | idr = &group->inotify_data.idr; | ||
374 | wd = ientry->wd; | 443 | wd = ientry->wd; |
375 | 444 | ||
376 | if (wd == -1) | 445 | /* |
446 | * does this ientry think it is in the idr? we shouldn't get called | ||
447 | * if it wasn't.... | ||
448 | */ | ||
449 | if (wd == -1) { | ||
450 | printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p" | ||
451 | " ientry->inode=%p\n", __func__, ientry, ientry->wd, | ||
452 | ientry->fsn_entry.group, ientry->fsn_entry.inode); | ||
453 | WARN_ON(1); | ||
377 | goto out; | 454 | goto out; |
455 | } | ||
378 | 456 | ||
379 | entry = idr_find(&group->inotify_data.idr, wd); | 457 | /* Lets look in the idr to see if we find it */ |
380 | if (unlikely(!entry)) | 458 | found_ientry = inotify_idr_find_locked(group, wd); |
459 | if (unlikely(!found_ientry)) { | ||
460 | printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p" | ||
461 | " ientry->inode=%p\n", __func__, ientry, ientry->wd, | ||
462 | ientry->fsn_entry.group, ientry->fsn_entry.inode); | ||
463 | WARN_ON(1); | ||
381 | goto out; | 464 | goto out; |
465 | } | ||
382 | 466 | ||
383 | found_ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); | 467 | /* |
468 | * We found an entry in the idr at the right wd, but it's | ||
469 | * not the entry we were told to remove. eparis seriously | ||
470 | * fucked up somewhere. | ||
471 | */ | ||
384 | if (unlikely(found_ientry != ientry)) { | 472 | if (unlikely(found_ientry != ientry)) { |
385 | /* We found an entry in the idr with the right wd, but it's | ||
386 | * not the entry we were told to remove. eparis seriously | ||
387 | * fucked up somewhere. */ | ||
388 | WARN_ON(1); | 473 | WARN_ON(1); |
389 | ientry->wd = -1; | 474 | printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p " |
475 | "entry->inode=%p found_ientry=%p found_ientry->wd=%d " | ||
476 | "found_ientry->group=%p found_ientry->inode=%p\n", | ||
477 | __func__, ientry, ientry->wd, ientry->fsn_entry.group, | ||
478 | ientry->fsn_entry.inode, found_ientry, found_ientry->wd, | ||
479 | found_ientry->fsn_entry.group, | ||
480 | found_ientry->fsn_entry.inode); | ||
390 | goto out; | 481 | goto out; |
391 | } | 482 | } |
392 | 483 | ||
393 | /* One ref for being in the idr, one ref held by the caller */ | 484 | /* |
394 | BUG_ON(atomic_read(&entry->refcnt) < 2); | 485 | * One ref for being in the idr |
395 | 486 | * one ref held by the caller trying to kill us | |
396 | idr_remove(idr, wd); | 487 | * one ref grabbed by inotify_idr_find |
397 | ientry->wd = -1; | 488 | */ |
489 | if (unlikely(atomic_read(&ientry->fsn_entry.refcnt) < 3)) { | ||
490 | printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p" | ||
491 | " ientry->inode=%p\n", __func__, ientry, ientry->wd, | ||
492 | ientry->fsn_entry.group, ientry->fsn_entry.inode); | ||
493 | /* we can't really recover with bad ref cnting.. */ | ||
494 | BUG(); | ||
495 | } | ||
398 | 496 | ||
399 | /* removed from the idr, drop that ref */ | 497 | do_inotify_remove_from_idr(group, ientry); |
400 | fsnotify_put_mark(entry); | ||
401 | out: | 498 | out: |
402 | spin_unlock(&group->inotify_data.idr_lock); | 499 | /* match the ref taken by inotify_idr_find_locked() */ |
500 | if (found_ientry) | ||
501 | fsnotify_put_mark(&found_ientry->fsn_entry); | ||
502 | ientry->wd = -1; | ||
503 | spin_unlock(idr_lock); | ||
403 | } | 504 | } |
404 | 505 | ||
405 | /* | 506 | /* |
@@ -524,6 +625,8 @@ static int inotify_new_watch(struct fsnotify_group *group, | |||
524 | struct inotify_inode_mark_entry *tmp_ientry; | 625 | struct inotify_inode_mark_entry *tmp_ientry; |
525 | __u32 mask; | 626 | __u32 mask; |
526 | int ret; | 627 | int ret; |
628 | struct idr *idr = &group->inotify_data.idr; | ||
629 | spinlock_t *idr_lock = &group->inotify_data.idr_lock; | ||
527 | 630 | ||
528 | /* don't allow invalid bits: we don't want flags set */ | 631 | /* don't allow invalid bits: we don't want flags set */ |
529 | mask = inotify_arg_to_mask(arg); | 632 | mask = inotify_arg_to_mask(arg); |
@@ -541,28 +644,11 @@ static int inotify_new_watch(struct fsnotify_group *group, | |||
541 | ret = -ENOSPC; | 644 | ret = -ENOSPC; |
542 | if (atomic_read(&group->inotify_data.user->inotify_watches) >= inotify_max_user_watches) | 645 | if (atomic_read(&group->inotify_data.user->inotify_watches) >= inotify_max_user_watches) |
543 | goto out_err; | 646 | goto out_err; |
544 | retry: | ||
545 | ret = -ENOMEM; | ||
546 | if (unlikely(!idr_pre_get(&group->inotify_data.idr, GFP_KERNEL))) | ||
547 | goto out_err; | ||
548 | |||
549 | /* we are putting the mark on the idr, take a reference */ | ||
550 | fsnotify_get_mark(&tmp_ientry->fsn_entry); | ||
551 | |||
552 | spin_lock(&group->inotify_data.idr_lock); | ||
553 | ret = idr_get_new_above(&group->inotify_data.idr, &tmp_ientry->fsn_entry, | ||
554 | group->inotify_data.last_wd+1, | ||
555 | &tmp_ientry->wd); | ||
556 | spin_unlock(&group->inotify_data.idr_lock); | ||
557 | if (ret) { | ||
558 | /* we didn't get on the idr, drop the idr reference */ | ||
559 | fsnotify_put_mark(&tmp_ientry->fsn_entry); | ||
560 | 647 | ||
561 | /* idr was out of memory allocate and try again */ | 648 | ret = inotify_add_to_idr(idr, idr_lock, group->inotify_data.last_wd, |
562 | if (ret == -EAGAIN) | 649 | tmp_ientry); |
563 | goto retry; | 650 | if (ret) |
564 | goto out_err; | 651 | goto out_err; |
565 | } | ||
566 | 652 | ||
567 | /* we are on the idr, now get on the inode */ | 653 | /* we are on the idr, now get on the inode */ |
568 | ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode); | 654 | ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode); |
@@ -726,7 +812,7 @@ fput_and_out: | |||
726 | SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd) | 812 | SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd) |
727 | { | 813 | { |
728 | struct fsnotify_group *group; | 814 | struct fsnotify_group *group; |
729 | struct fsnotify_mark_entry *entry; | 815 | struct inotify_inode_mark_entry *ientry; |
730 | struct file *filp; | 816 | struct file *filp; |
731 | int ret = 0, fput_needed; | 817 | int ret = 0, fput_needed; |
732 | 818 | ||
@@ -735,25 +821,23 @@ SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd) | |||
735 | return -EBADF; | 821 | return -EBADF; |
736 | 822 | ||
737 | /* verify that this is indeed an inotify instance */ | 823 | /* verify that this is indeed an inotify instance */ |
738 | if (unlikely(filp->f_op != &inotify_fops)) { | 824 | ret = -EINVAL; |
739 | ret = -EINVAL; | 825 | if (unlikely(filp->f_op != &inotify_fops)) |
740 | goto out; | 826 | goto out; |
741 | } | ||
742 | 827 | ||
743 | group = filp->private_data; | 828 | group = filp->private_data; |
744 | 829 | ||
745 | spin_lock(&group->inotify_data.idr_lock); | 830 | ret = -EINVAL; |
746 | entry = idr_find(&group->inotify_data.idr, wd); | 831 | ientry = inotify_idr_find(group, wd); |
747 | if (unlikely(!entry)) { | 832 | if (unlikely(!ientry)) |
748 | spin_unlock(&group->inotify_data.idr_lock); | ||
749 | ret = -EINVAL; | ||
750 | goto out; | 833 | goto out; |
751 | } | ||
752 | fsnotify_get_mark(entry); | ||
753 | spin_unlock(&group->inotify_data.idr_lock); | ||
754 | 834 | ||
755 | fsnotify_destroy_mark_by_entry(entry); | 835 | ret = 0; |
756 | fsnotify_put_mark(entry); | 836 | |
837 | fsnotify_destroy_mark_by_entry(&ientry->fsn_entry); | ||
838 | |||
839 | /* match ref taken by inotify_idr_find */ | ||
840 | fsnotify_put_mark(&ientry->fsn_entry); | ||
757 | 841 | ||
758 | out: | 842 | out: |
759 | fput_light(filp, fput_needed); | 843 | fput_light(filp, fput_needed); |