diff options
| -rw-r--r-- | fs/notify/inotify/inotify_user.c | 50 |
1 files changed, 40 insertions, 10 deletions
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index d8f73c253073..ce1f5823e2c2 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c | |||
| @@ -364,20 +364,53 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns | |||
| 364 | return error; | 364 | return error; |
| 365 | } | 365 | } |
| 366 | 366 | ||
| 367 | /* | ||
| 368 | * Remove the mark from the idr (if present) and drop the reference | ||
| 369 | * on the mark because it was in the idr. | ||
| 370 | */ | ||
| 367 | static void inotify_remove_from_idr(struct fsnotify_group *group, | 371 | static void inotify_remove_from_idr(struct fsnotify_group *group, |
| 368 | struct inotify_inode_mark_entry *ientry) | 372 | struct inotify_inode_mark_entry *ientry) |
| 369 | { | 373 | { |
| 370 | struct idr *idr; | 374 | struct idr *idr; |
| 375 | struct fsnotify_mark_entry *entry; | ||
| 376 | struct inotify_inode_mark_entry *found_ientry; | ||
| 377 | int wd; | ||
| 371 | 378 | ||
| 372 | spin_lock(&group->inotify_data.idr_lock); | 379 | spin_lock(&group->inotify_data.idr_lock); |
| 373 | idr = &group->inotify_data.idr; | 380 | idr = &group->inotify_data.idr; |
| 374 | idr_remove(idr, ientry->wd); | 381 | wd = ientry->wd; |
| 375 | spin_unlock(&group->inotify_data.idr_lock); | 382 | |
| 383 | if (wd == -1) | ||
| 384 | goto out; | ||
| 385 | |||
| 386 | entry = idr_find(&group->inotify_data.idr, wd); | ||
| 387 | if (unlikely(!entry)) | ||
| 388 | goto out; | ||
| 389 | |||
| 390 | found_ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); | ||
| 391 | if (unlikely(found_ientry != ientry)) { | ||
| 392 | /* We found an entry in the idr with the right wd, but it's | ||
| 393 | * not the entry we were told to remove. eparis seriously | ||
| 394 | * fucked up somewhere. */ | ||
| 395 | WARN_ON(1); | ||
| 396 | ientry->wd = -1; | ||
| 397 | goto out; | ||
| 398 | } | ||
| 399 | |||
| 400 | /* One ref for being in the idr, one ref held by the caller */ | ||
| 401 | BUG_ON(atomic_read(&entry->refcnt) < 2); | ||
| 402 | |||
| 403 | idr_remove(idr, wd); | ||
| 376 | ientry->wd = -1; | 404 | ientry->wd = -1; |
| 405 | |||
| 406 | /* removed from the idr, drop that ref */ | ||
| 407 | fsnotify_put_mark(entry); | ||
| 408 | out: | ||
| 409 | spin_unlock(&group->inotify_data.idr_lock); | ||
| 377 | } | 410 | } |
| 411 | |||
| 378 | /* | 412 | /* |
| 379 | * Send IN_IGNORED for this wd, remove this wd from the idr, and drop the | 413 | * Send IN_IGNORED for this wd, remove this wd from the idr. |
| 380 | * internal reference help on the mark because it is in the idr. | ||
| 381 | */ | 414 | */ |
| 382 | void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry, | 415 | void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry, |
| 383 | struct fsnotify_group *group) | 416 | struct fsnotify_group *group) |
| @@ -417,9 +450,6 @@ skip_send_ignore: | |||
| 417 | /* remove this entry from the idr */ | 450 | /* remove this entry from the idr */ |
| 418 | inotify_remove_from_idr(group, ientry); | 451 | inotify_remove_from_idr(group, ientry); |
| 419 | 452 | ||
| 420 | /* removed from idr, drop that reference */ | ||
| 421 | fsnotify_put_mark(entry); | ||
| 422 | |||
| 423 | atomic_dec(&group->inotify_data.user->inotify_watches); | 453 | atomic_dec(&group->inotify_data.user->inotify_watches); |
| 424 | } | 454 | } |
| 425 | 455 | ||
| @@ -535,6 +565,9 @@ retry: | |||
| 535 | goto out_err; | 565 | goto out_err; |
| 536 | } | 566 | } |
| 537 | 567 | ||
| 568 | /* we put the mark on the idr, take a reference */ | ||
| 569 | fsnotify_get_mark(&tmp_ientry->fsn_entry); | ||
| 570 | |||
| 538 | /* we are on the idr, now get on the inode */ | 571 | /* we are on the idr, now get on the inode */ |
| 539 | ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode); | 572 | ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode); |
| 540 | if (ret) { | 573 | if (ret) { |
| @@ -543,9 +576,6 @@ retry: | |||
| 543 | goto out_err; | 576 | goto out_err; |
| 544 | } | 577 | } |
| 545 | 578 | ||
| 546 | /* we put the mark on the idr, take a reference */ | ||
| 547 | fsnotify_get_mark(&tmp_ientry->fsn_entry); | ||
| 548 | |||
| 549 | /* update the idr hint, who cares about races, it's just a hint */ | 579 | /* update the idr hint, who cares about races, it's just a hint */ |
| 550 | group->inotify_data.last_wd = tmp_ientry->wd; | 580 | group->inotify_data.last_wd = tmp_ientry->wd; |
| 551 | 581 | ||
