diff options
| -rw-r--r-- | fs/notify/inotify/inotify_user.c | 79 |
1 files changed, 49 insertions, 30 deletions
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index aff4214f16c3..726118a5845b 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c | |||
| @@ -365,6 +365,17 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns | |||
| 365 | return error; | 365 | return error; |
| 366 | } | 366 | } |
| 367 | 367 | ||
| 368 | static void inotify_remove_from_idr(struct fsnotify_group *group, | ||
| 369 | struct inotify_inode_mark_entry *ientry) | ||
| 370 | { | ||
| 371 | struct idr *idr; | ||
| 372 | |||
| 373 | spin_lock(&group->inotify_data.idr_lock); | ||
| 374 | idr = &group->inotify_data.idr; | ||
| 375 | idr_remove(idr, ientry->wd); | ||
| 376 | spin_unlock(&group->inotify_data.idr_lock); | ||
| 377 | ientry->wd = -1; | ||
| 378 | } | ||
| 368 | /* | 379 | /* |
| 369 | * Send IN_IGNORED for this wd, remove this wd from the idr, and drop the | 380 | * Send IN_IGNORED for this wd, remove this wd from the idr, and drop the |
| 370 | * internal reference help on the mark because it is in the idr. | 381 | * internal reference help on the mark because it is in the idr. |
| @@ -375,7 +386,6 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry, | |||
| 375 | struct inotify_inode_mark_entry *ientry; | 386 | struct inotify_inode_mark_entry *ientry; |
| 376 | struct inotify_event_private_data *event_priv; | 387 | struct inotify_event_private_data *event_priv; |
| 377 | struct fsnotify_event_private_data *fsn_event_priv; | 388 | struct fsnotify_event_private_data *fsn_event_priv; |
| 378 | struct idr *idr; | ||
| 379 | 389 | ||
| 380 | ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); | 390 | ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); |
| 381 | 391 | ||
| @@ -397,10 +407,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry, | |||
| 397 | skip_send_ignore: | 407 | skip_send_ignore: |
| 398 | 408 | ||
| 399 | /* remove this entry from the idr */ | 409 | /* remove this entry from the idr */ |
| 400 | spin_lock(&group->inotify_data.idr_lock); | 410 | inotify_remove_from_idr(group, ientry); |
| 401 | idr = &group->inotify_data.idr; | ||
| 402 | idr_remove(idr, ientry->wd); | ||
| 403 | spin_unlock(&group->inotify_data.idr_lock); | ||
| 404 | 411 | ||
| 405 | /* removed from idr, drop that reference */ | 412 | /* removed from idr, drop that reference */ |
| 406 | fsnotify_put_mark(entry); | 413 | fsnotify_put_mark(entry); |
| @@ -420,6 +427,7 @@ static int inotify_update_watch(struct fsnotify_group *group, struct inode *inod | |||
| 420 | { | 427 | { |
| 421 | struct fsnotify_mark_entry *entry = NULL; | 428 | struct fsnotify_mark_entry *entry = NULL; |
| 422 | struct inotify_inode_mark_entry *ientry; | 429 | struct inotify_inode_mark_entry *ientry; |
| 430 | struct inotify_inode_mark_entry *tmp_ientry; | ||
| 423 | int ret = 0; | 431 | int ret = 0; |
| 424 | int add = (arg & IN_MASK_ADD); | 432 | int add = (arg & IN_MASK_ADD); |
| 425 | __u32 mask; | 433 | __u32 mask; |
| @@ -430,50 +438,60 @@ static int inotify_update_watch(struct fsnotify_group *group, struct inode *inod | |||
| 430 | if (unlikely(!mask)) | 438 | if (unlikely(!mask)) |
| 431 | return -EINVAL; | 439 | return -EINVAL; |
| 432 | 440 | ||
| 433 | ientry = kmem_cache_alloc(inotify_inode_mark_cachep, GFP_KERNEL); | 441 | tmp_ientry = kmem_cache_alloc(inotify_inode_mark_cachep, GFP_KERNEL); |
| 434 | if (unlikely(!ientry)) | 442 | if (unlikely(!tmp_ientry)) |
| 435 | return -ENOMEM; | 443 | return -ENOMEM; |
| 436 | /* we set the mask at the end after attaching it */ | 444 | /* we set the mask at the end after attaching it */ |
| 437 | fsnotify_init_mark(&ientry->fsn_entry, inotify_free_mark); | 445 | fsnotify_init_mark(&tmp_ientry->fsn_entry, inotify_free_mark); |
| 438 | ientry->wd = 0; | 446 | tmp_ientry->wd = -1; |
| 439 | 447 | ||
| 440 | find_entry: | 448 | find_entry: |
| 441 | spin_lock(&inode->i_lock); | 449 | spin_lock(&inode->i_lock); |
| 442 | entry = fsnotify_find_mark_entry(group, inode); | 450 | entry = fsnotify_find_mark_entry(group, inode); |
| 443 | spin_unlock(&inode->i_lock); | 451 | spin_unlock(&inode->i_lock); |
| 444 | if (entry) { | 452 | if (entry) { |
| 445 | kmem_cache_free(inotify_inode_mark_cachep, ientry); | ||
| 446 | ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); | 453 | ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); |
| 447 | } else { | 454 | } else { |
| 448 | if (atomic_read(&group->inotify_data.user->inotify_watches) >= inotify_max_user_watches) { | 455 | ret = -ENOSPC; |
| 449 | ret = -ENOSPC; | 456 | if (atomic_read(&group->inotify_data.user->inotify_watches) >= inotify_max_user_watches) |
| 450 | goto out_err; | ||
| 451 | } | ||
| 452 | |||
| 453 | ret = fsnotify_add_mark(&ientry->fsn_entry, group, inode); | ||
| 454 | if (ret == -EEXIST) | ||
| 455 | goto find_entry; | ||
| 456 | else if (ret) | ||
| 457 | goto out_err; | 457 | goto out_err; |
| 458 | |||
| 459 | entry = &ientry->fsn_entry; | ||
| 460 | retry: | 458 | retry: |
| 461 | ret = -ENOMEM; | 459 | ret = -ENOMEM; |
| 462 | if (unlikely(!idr_pre_get(&group->inotify_data.idr, GFP_KERNEL))) | 460 | if (unlikely(!idr_pre_get(&group->inotify_data.idr, GFP_KERNEL))) |
| 463 | goto out_err; | 461 | goto out_err; |
| 464 | 462 | ||
| 465 | spin_lock(&group->inotify_data.idr_lock); | 463 | spin_lock(&group->inotify_data.idr_lock); |
| 466 | ret = idr_get_new_above(&group->inotify_data.idr, entry, | 464 | ret = idr_get_new_above(&group->inotify_data.idr, &tmp_ientry->fsn_entry, |
| 467 | ++group->inotify_data.last_wd, | 465 | group->inotify_data.last_wd, |
| 468 | &ientry->wd); | 466 | &tmp_ientry->wd); |
| 469 | spin_unlock(&group->inotify_data.idr_lock); | 467 | spin_unlock(&group->inotify_data.idr_lock); |
| 470 | if (ret) { | 468 | if (ret) { |
| 471 | if (ret == -EAGAIN) | 469 | if (ret == -EAGAIN) |
| 472 | goto retry; | 470 | goto retry; |
| 473 | goto out_err; | 471 | goto out_err; |
| 474 | } | 472 | } |
| 473 | |||
| 474 | ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode); | ||
| 475 | if (ret) { | ||
| 476 | inotify_remove_from_idr(group, tmp_ientry); | ||
| 477 | if (ret == -EEXIST) | ||
| 478 | goto find_entry; | ||
| 479 | goto out_err; | ||
| 480 | } | ||
| 481 | |||
| 482 | /* tmp_ientry has been added to the inode, so we are all set up. | ||
| 483 | * now we just need to make sure tmp_ientry doesn't get freed and | ||
| 484 | * we need to set up entry and ientry so the generic code can | ||
| 485 | * do its thing. */ | ||
| 486 | ientry = tmp_ientry; | ||
| 487 | entry = &ientry->fsn_entry; | ||
| 488 | tmp_ientry = NULL; | ||
| 489 | |||
| 475 | atomic_inc(&group->inotify_data.user->inotify_watches); | 490 | atomic_inc(&group->inotify_data.user->inotify_watches); |
| 476 | 491 | ||
| 492 | /* update the idr hint */ | ||
| 493 | group->inotify_data.last_wd = ientry->wd; | ||
| 494 | |||
| 477 | /* we put the mark on the idr, take a reference */ | 495 | /* we put the mark on the idr, take a reference */ |
| 478 | fsnotify_get_mark(entry); | 496 | fsnotify_get_mark(entry); |
| 479 | } | 497 | } |
| @@ -514,14 +532,15 @@ retry: | |||
| 514 | * depending on which path we took... */ | 532 | * depending on which path we took... */ |
| 515 | fsnotify_put_mark(entry); | 533 | fsnotify_put_mark(entry); |
| 516 | 534 | ||
| 517 | return ret; | ||
| 518 | |||
| 519 | out_err: | 535 | out_err: |
| 520 | /* see this isn't supposed to happen, just kill the watch */ | 536 | /* could be an error, could be that we found an existing mark */ |
| 521 | if (entry) { | 537 | if (tmp_ientry) { |
| 522 | fsnotify_destroy_mark_by_entry(entry); | 538 | /* on the idr but didn't make it on the inode */ |
| 523 | fsnotify_put_mark(entry); | 539 | if (tmp_ientry->wd != -1) |
| 540 | inotify_remove_from_idr(group, tmp_ientry); | ||
| 541 | kmem_cache_free(inotify_inode_mark_cachep, tmp_ientry); | ||
| 524 | } | 542 | } |
| 543 | |||
| 525 | return ret; | 544 | return ret; |
| 526 | } | 545 | } |
| 527 | 546 | ||
