aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/notify/inotify/inotify_user.c194
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
360static 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
382static 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
403static 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
416static 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
364static void inotify_remove_from_idr(struct fsnotify_group *group, 435static 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);
401out: 498out:
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;
544retry:
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:
726SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd) 812SYSCALL_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
758out: 842out:
759 fput_light(filp, fput_needed); 843 fput_light(filp, fput_needed);