diff options
| -rw-r--r-- | fs/notify/dnotify/dnotify.c | 4 | ||||
| -rw-r--r-- | fs/notify/fanotify/fanotify.c | 6 | ||||
| -rw-r--r-- | fs/notify/fanotify/fanotify_user.c | 37 | ||||
| -rw-r--r-- | fs/notify/fdinfo.c | 4 | ||||
| -rw-r--r-- | fs/notify/group.c | 47 | ||||
| -rw-r--r-- | fs/notify/inode_mark.c | 14 | ||||
| -rw-r--r-- | fs/notify/inotify/inotify_fsnotify.c | 4 | ||||
| -rw-r--r-- | fs/notify/inotify/inotify_user.c | 34 | ||||
| -rw-r--r-- | fs/notify/mark.c | 91 | ||||
| -rw-r--r-- | fs/notify/notification.c | 1 | ||||
| -rw-r--r-- | fs/notify/vfsmount_mark.c | 14 | ||||
| -rw-r--r-- | include/linux/fsnotify_backend.h | 31 | ||||
| -rw-r--r-- | kernel/audit_tree.c | 10 | ||||
| -rw-r--r-- | kernel/audit_watch.c | 4 |
14 files changed, 180 insertions, 121 deletions
diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 3344bdd5506e..08b886f119ce 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c | |||
| @@ -201,7 +201,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id) | |||
| 201 | 201 | ||
| 202 | /* nothing else could have found us thanks to the dnotify_mark_mutex */ | 202 | /* nothing else could have found us thanks to the dnotify_mark_mutex */ |
| 203 | if (dn_mark->dn == NULL) | 203 | if (dn_mark->dn == NULL) |
| 204 | fsnotify_destroy_mark(fsn_mark); | 204 | fsnotify_destroy_mark(fsn_mark, dnotify_group); |
| 205 | 205 | ||
| 206 | mutex_unlock(&dnotify_mark_mutex); | 206 | mutex_unlock(&dnotify_mark_mutex); |
| 207 | 207 | ||
| @@ -385,7 +385,7 @@ out: | |||
| 385 | spin_unlock(&fsn_mark->lock); | 385 | spin_unlock(&fsn_mark->lock); |
| 386 | 386 | ||
| 387 | if (destroy) | 387 | if (destroy) |
| 388 | fsnotify_destroy_mark(fsn_mark); | 388 | fsnotify_destroy_mark(fsn_mark, dnotify_group); |
| 389 | 389 | ||
| 390 | mutex_unlock(&dnotify_mark_mutex); | 390 | mutex_unlock(&dnotify_mark_mutex); |
| 391 | fsnotify_put_mark(fsn_mark); | 391 | fsnotify_put_mark(fsn_mark); |
diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index a50636025364..0c2f9122b262 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c | |||
| @@ -18,6 +18,12 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) | |||
| 18 | old->tgid == new->tgid) { | 18 | old->tgid == new->tgid) { |
| 19 | switch (old->data_type) { | 19 | switch (old->data_type) { |
| 20 | case (FSNOTIFY_EVENT_PATH): | 20 | case (FSNOTIFY_EVENT_PATH): |
| 21 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS | ||
| 22 | /* dont merge two permission events */ | ||
| 23 | if ((old->mask & FAN_ALL_PERM_EVENTS) && | ||
| 24 | (new->mask & FAN_ALL_PERM_EVENTS)) | ||
| 25 | return false; | ||
| 26 | #endif | ||
| 21 | if ((old->path.mnt == new->path.mnt) && | 27 | if ((old->path.mnt == new->path.mnt) && |
| 22 | (old->path.dentry == new->path.dentry)) | 28 | (old->path.dentry == new->path.dentry)) |
| 23 | return true; | 29 | return true; |
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index a5cd9bba022f..9ff4a5ee6e20 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c | |||
| @@ -397,8 +397,12 @@ static int fanotify_release(struct inode *ignored, struct file *file) | |||
| 397 | 397 | ||
| 398 | wake_up(&group->fanotify_data.access_waitq); | 398 | wake_up(&group->fanotify_data.access_waitq); |
| 399 | #endif | 399 | #endif |
| 400 | |||
| 401 | if (file->f_flags & FASYNC) | ||
| 402 | fsnotify_fasync(-1, file, 0); | ||
| 403 | |||
| 400 | /* matches the fanotify_init->fsnotify_alloc_group */ | 404 | /* matches the fanotify_init->fsnotify_alloc_group */ |
| 401 | fsnotify_put_group(group); | 405 | fsnotify_destroy_group(group); |
| 402 | 406 | ||
| 403 | return 0; | 407 | return 0; |
| 404 | } | 408 | } |
| @@ -493,7 +497,8 @@ out: | |||
| 493 | 497 | ||
| 494 | static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, | 498 | static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, |
| 495 | __u32 mask, | 499 | __u32 mask, |
| 496 | unsigned int flags) | 500 | unsigned int flags, |
| 501 | int *destroy) | ||
| 497 | { | 502 | { |
| 498 | __u32 oldmask; | 503 | __u32 oldmask; |
| 499 | 504 | ||
| @@ -507,8 +512,7 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, | |||
| 507 | } | 512 | } |
| 508 | spin_unlock(&fsn_mark->lock); | 513 | spin_unlock(&fsn_mark->lock); |
| 509 | 514 | ||
| 510 | if (!(oldmask & ~mask)) | 515 | *destroy = !(oldmask & ~mask); |
| 511 | fsnotify_destroy_mark(fsn_mark); | ||
| 512 | 516 | ||
| 513 | return mask & oldmask; | 517 | return mask & oldmask; |
| 514 | } | 518 | } |
| @@ -519,12 +523,17 @@ static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group, | |||
| 519 | { | 523 | { |
| 520 | struct fsnotify_mark *fsn_mark = NULL; | 524 | struct fsnotify_mark *fsn_mark = NULL; |
| 521 | __u32 removed; | 525 | __u32 removed; |
| 526 | int destroy_mark; | ||
| 522 | 527 | ||
| 523 | fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); | 528 | fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); |
| 524 | if (!fsn_mark) | 529 | if (!fsn_mark) |
| 525 | return -ENOENT; | 530 | return -ENOENT; |
| 526 | 531 | ||
| 527 | removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags); | 532 | removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags, |
| 533 | &destroy_mark); | ||
| 534 | if (destroy_mark) | ||
| 535 | fsnotify_destroy_mark(fsn_mark, group); | ||
| 536 | |||
| 528 | fsnotify_put_mark(fsn_mark); | 537 | fsnotify_put_mark(fsn_mark); |
| 529 | if (removed & real_mount(mnt)->mnt_fsnotify_mask) | 538 | if (removed & real_mount(mnt)->mnt_fsnotify_mask) |
| 530 | fsnotify_recalc_vfsmount_mask(mnt); | 539 | fsnotify_recalc_vfsmount_mask(mnt); |
| @@ -538,12 +547,16 @@ static int fanotify_remove_inode_mark(struct fsnotify_group *group, | |||
| 538 | { | 547 | { |
| 539 | struct fsnotify_mark *fsn_mark = NULL; | 548 | struct fsnotify_mark *fsn_mark = NULL; |
| 540 | __u32 removed; | 549 | __u32 removed; |
| 550 | int destroy_mark; | ||
| 541 | 551 | ||
| 542 | fsn_mark = fsnotify_find_inode_mark(group, inode); | 552 | fsn_mark = fsnotify_find_inode_mark(group, inode); |
| 543 | if (!fsn_mark) | 553 | if (!fsn_mark) |
| 544 | return -ENOENT; | 554 | return -ENOENT; |
| 545 | 555 | ||
| 546 | removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags); | 556 | removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags, |
| 557 | &destroy_mark); | ||
| 558 | if (destroy_mark) | ||
| 559 | fsnotify_destroy_mark(fsn_mark, group); | ||
| 547 | /* matches the fsnotify_find_inode_mark() */ | 560 | /* matches the fsnotify_find_inode_mark() */ |
| 548 | fsnotify_put_mark(fsn_mark); | 561 | fsnotify_put_mark(fsn_mark); |
| 549 | if (removed & inode->i_fsnotify_mask) | 562 | if (removed & inode->i_fsnotify_mask) |
| @@ -710,13 +723,13 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) | |||
| 710 | break; | 723 | break; |
| 711 | default: | 724 | default: |
| 712 | fd = -EINVAL; | 725 | fd = -EINVAL; |
| 713 | goto out_put_group; | 726 | goto out_destroy_group; |
| 714 | } | 727 | } |
| 715 | 728 | ||
| 716 | if (flags & FAN_UNLIMITED_QUEUE) { | 729 | if (flags & FAN_UNLIMITED_QUEUE) { |
| 717 | fd = -EPERM; | 730 | fd = -EPERM; |
| 718 | if (!capable(CAP_SYS_ADMIN)) | 731 | if (!capable(CAP_SYS_ADMIN)) |
| 719 | goto out_put_group; | 732 | goto out_destroy_group; |
| 720 | group->max_events = UINT_MAX; | 733 | group->max_events = UINT_MAX; |
| 721 | } else { | 734 | } else { |
| 722 | group->max_events = FANOTIFY_DEFAULT_MAX_EVENTS; | 735 | group->max_events = FANOTIFY_DEFAULT_MAX_EVENTS; |
| @@ -725,7 +738,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) | |||
| 725 | if (flags & FAN_UNLIMITED_MARKS) { | 738 | if (flags & FAN_UNLIMITED_MARKS) { |
| 726 | fd = -EPERM; | 739 | fd = -EPERM; |
| 727 | if (!capable(CAP_SYS_ADMIN)) | 740 | if (!capable(CAP_SYS_ADMIN)) |
| 728 | goto out_put_group; | 741 | goto out_destroy_group; |
| 729 | group->fanotify_data.max_marks = UINT_MAX; | 742 | group->fanotify_data.max_marks = UINT_MAX; |
| 730 | } else { | 743 | } else { |
| 731 | group->fanotify_data.max_marks = FANOTIFY_DEFAULT_MAX_MARKS; | 744 | group->fanotify_data.max_marks = FANOTIFY_DEFAULT_MAX_MARKS; |
| @@ -733,12 +746,12 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) | |||
| 733 | 746 | ||
| 734 | fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags); | 747 | fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags); |
| 735 | if (fd < 0) | 748 | if (fd < 0) |
| 736 | goto out_put_group; | 749 | goto out_destroy_group; |
| 737 | 750 | ||
| 738 | return fd; | 751 | return fd; |
| 739 | 752 | ||
| 740 | out_put_group: | 753 | out_destroy_group: |
| 741 | fsnotify_put_group(group); | 754 | fsnotify_destroy_group(group); |
| 742 | return fd; | 755 | return fd; |
| 743 | } | 756 | } |
| 744 | 757 | ||
diff --git a/fs/notify/fdinfo.c b/fs/notify/fdinfo.c index 514c4b81483d..238a5930cb3c 100644 --- a/fs/notify/fdinfo.c +++ b/fs/notify/fdinfo.c | |||
| @@ -27,13 +27,13 @@ static int show_fdinfo(struct seq_file *m, struct file *f, | |||
| 27 | struct fsnotify_mark *mark; | 27 | struct fsnotify_mark *mark; |
| 28 | int ret = 0; | 28 | int ret = 0; |
| 29 | 29 | ||
| 30 | spin_lock(&group->mark_lock); | 30 | mutex_lock(&group->mark_mutex); |
| 31 | list_for_each_entry(mark, &group->marks_list, g_list) { | 31 | list_for_each_entry(mark, &group->marks_list, g_list) { |
| 32 | ret = show(m, mark); | 32 | ret = show(m, mark); |
| 33 | if (ret) | 33 | if (ret) |
| 34 | break; | 34 | break; |
| 35 | } | 35 | } |
| 36 | spin_unlock(&group->mark_lock); | 36 | mutex_unlock(&group->mark_mutex); |
| 37 | return ret; | 37 | return ret; |
| 38 | } | 38 | } |
| 39 | 39 | ||
diff --git a/fs/notify/group.c b/fs/notify/group.c index 63fc294a4692..bd2625bd88b4 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c | |||
| @@ -33,9 +33,6 @@ | |||
| 33 | */ | 33 | */ |
| 34 | void fsnotify_final_destroy_group(struct fsnotify_group *group) | 34 | void fsnotify_final_destroy_group(struct fsnotify_group *group) |
| 35 | { | 35 | { |
| 36 | /* clear the notification queue of all events */ | ||
| 37 | fsnotify_flush_notify(group); | ||
| 38 | |||
| 39 | if (group->ops->free_group_priv) | 36 | if (group->ops->free_group_priv) |
| 40 | group->ops->free_group_priv(group); | 37 | group->ops->free_group_priv(group); |
| 41 | 38 | ||
| @@ -43,23 +40,30 @@ void fsnotify_final_destroy_group(struct fsnotify_group *group) | |||
| 43 | } | 40 | } |
| 44 | 41 | ||
| 45 | /* | 42 | /* |
| 46 | * Trying to get rid of a group. We need to first get rid of any outstanding | 43 | * Trying to get rid of a group. Remove all marks, flush all events and release |
| 47 | * allocations and then free the group. Remember that fsnotify_clear_marks_by_group | 44 | * the group reference. |
| 48 | * could miss marks that are being freed by inode and those marks could still | 45 | * Note that another thread calling fsnotify_clear_marks_by_group() may still |
| 49 | * hold a reference to this group (via group->num_marks) If we get into that | 46 | * hold a ref to the group. |
| 50 | * situtation, the fsnotify_final_destroy_group will get called when that final | ||
| 51 | * mark is freed. | ||
| 52 | */ | 47 | */ |
| 53 | static void fsnotify_destroy_group(struct fsnotify_group *group) | 48 | void fsnotify_destroy_group(struct fsnotify_group *group) |
| 54 | { | 49 | { |
| 55 | /* clear all inode marks for this group */ | 50 | /* clear all inode marks for this group */ |
| 56 | fsnotify_clear_marks_by_group(group); | 51 | fsnotify_clear_marks_by_group(group); |
| 57 | 52 | ||
| 58 | synchronize_srcu(&fsnotify_mark_srcu); | 53 | synchronize_srcu(&fsnotify_mark_srcu); |
| 59 | 54 | ||
| 60 | /* past the point of no return, matches the initial value of 1 */ | 55 | /* clear the notification queue of all events */ |
| 61 | if (atomic_dec_and_test(&group->num_marks)) | 56 | fsnotify_flush_notify(group); |
| 62 | fsnotify_final_destroy_group(group); | 57 | |
| 58 | fsnotify_put_group(group); | ||
| 59 | } | ||
| 60 | |||
| 61 | /* | ||
| 62 | * Get reference to a group. | ||
| 63 | */ | ||
| 64 | void fsnotify_get_group(struct fsnotify_group *group) | ||
| 65 | { | ||
| 66 | atomic_inc(&group->refcnt); | ||
| 63 | } | 67 | } |
| 64 | 68 | ||
| 65 | /* | 69 | /* |
| @@ -68,7 +72,7 @@ static void fsnotify_destroy_group(struct fsnotify_group *group) | |||
| 68 | void fsnotify_put_group(struct fsnotify_group *group) | 72 | void fsnotify_put_group(struct fsnotify_group *group) |
| 69 | { | 73 | { |
| 70 | if (atomic_dec_and_test(&group->refcnt)) | 74 | if (atomic_dec_and_test(&group->refcnt)) |
| 71 | fsnotify_destroy_group(group); | 75 | fsnotify_final_destroy_group(group); |
| 72 | } | 76 | } |
| 73 | 77 | ||
| 74 | /* | 78 | /* |
| @@ -84,21 +88,24 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) | |||
| 84 | 88 | ||
| 85 | /* set to 0 when there a no external references to this group */ | 89 | /* set to 0 when there a no external references to this group */ |
| 86 | atomic_set(&group->refcnt, 1); | 90 | atomic_set(&group->refcnt, 1); |
| 87 | /* | 91 | atomic_set(&group->num_marks, 0); |
| 88 | * hits 0 when there are no external references AND no marks for | ||
| 89 | * this group | ||
| 90 | */ | ||
| 91 | atomic_set(&group->num_marks, 1); | ||
| 92 | 92 | ||
| 93 | mutex_init(&group->notification_mutex); | 93 | mutex_init(&group->notification_mutex); |
| 94 | INIT_LIST_HEAD(&group->notification_list); | 94 | INIT_LIST_HEAD(&group->notification_list); |
| 95 | init_waitqueue_head(&group->notification_waitq); | 95 | init_waitqueue_head(&group->notification_waitq); |
| 96 | group->max_events = UINT_MAX; | 96 | group->max_events = UINT_MAX; |
| 97 | 97 | ||
| 98 | spin_lock_init(&group->mark_lock); | 98 | mutex_init(&group->mark_mutex); |
| 99 | INIT_LIST_HEAD(&group->marks_list); | 99 | INIT_LIST_HEAD(&group->marks_list); |
| 100 | 100 | ||
| 101 | group->ops = ops; | 101 | group->ops = ops; |
| 102 | 102 | ||
| 103 | return group; | 103 | return group; |
| 104 | } | 104 | } |
| 105 | |||
| 106 | int fsnotify_fasync(int fd, struct file *file, int on) | ||
| 107 | { | ||
| 108 | struct fsnotify_group *group = file->private_data; | ||
| 109 | |||
| 110 | return fasync_helper(fd, file, on, &group->fsn_fa) >= 0 ? 0 : -EIO; | ||
| 111 | } | ||
diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index f3035691f528..f31e90fc050d 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c | |||
| @@ -63,8 +63,8 @@ void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark) | |||
| 63 | { | 63 | { |
| 64 | struct inode *inode = mark->i.inode; | 64 | struct inode *inode = mark->i.inode; |
| 65 | 65 | ||
| 66 | BUG_ON(!mutex_is_locked(&mark->group->mark_mutex)); | ||
| 66 | assert_spin_locked(&mark->lock); | 67 | assert_spin_locked(&mark->lock); |
| 67 | assert_spin_locked(&mark->group->mark_lock); | ||
| 68 | 68 | ||
| 69 | spin_lock(&inode->i_lock); | 69 | spin_lock(&inode->i_lock); |
| 70 | 70 | ||
| @@ -99,8 +99,16 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) | |||
| 99 | spin_unlock(&inode->i_lock); | 99 | spin_unlock(&inode->i_lock); |
| 100 | 100 | ||
| 101 | list_for_each_entry_safe(mark, lmark, &free_list, i.free_i_list) { | 101 | list_for_each_entry_safe(mark, lmark, &free_list, i.free_i_list) { |
| 102 | fsnotify_destroy_mark(mark); | 102 | struct fsnotify_group *group; |
| 103 | |||
| 104 | spin_lock(&mark->lock); | ||
| 105 | fsnotify_get_group(mark->group); | ||
| 106 | group = mark->group; | ||
| 107 | spin_unlock(&mark->lock); | ||
| 108 | |||
| 109 | fsnotify_destroy_mark(mark, group); | ||
| 103 | fsnotify_put_mark(mark); | 110 | fsnotify_put_mark(mark); |
| 111 | fsnotify_put_group(group); | ||
| 104 | } | 112 | } |
| 105 | } | 113 | } |
| 106 | 114 | ||
| @@ -192,8 +200,8 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, | |||
| 192 | 200 | ||
| 193 | mark->flags |= FSNOTIFY_MARK_FLAG_INODE; | 201 | mark->flags |= FSNOTIFY_MARK_FLAG_INODE; |
| 194 | 202 | ||
| 203 | BUG_ON(!mutex_is_locked(&group->mark_mutex)); | ||
| 195 | assert_spin_locked(&mark->lock); | 204 | assert_spin_locked(&mark->lock); |
| 196 | assert_spin_locked(&group->mark_lock); | ||
| 197 | 205 | ||
| 198 | spin_lock(&inode->i_lock); | 206 | spin_lock(&inode->i_lock); |
| 199 | 207 | ||
diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index e3cbd746f64a..871569c7d609 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c | |||
| @@ -118,6 +118,7 @@ static int inotify_handle_event(struct fsnotify_group *group, | |||
| 118 | 118 | ||
| 119 | fsn_event_priv = &event_priv->fsnotify_event_priv_data; | 119 | fsn_event_priv = &event_priv->fsnotify_event_priv_data; |
| 120 | 120 | ||
| 121 | fsnotify_get_group(group); | ||
| 121 | fsn_event_priv->group = group; | 122 | fsn_event_priv->group = group; |
| 122 | event_priv->wd = wd; | 123 | event_priv->wd = wd; |
| 123 | 124 | ||
| @@ -131,7 +132,7 @@ static int inotify_handle_event(struct fsnotify_group *group, | |||
| 131 | } | 132 | } |
| 132 | 133 | ||
| 133 | if (inode_mark->mask & IN_ONESHOT) | 134 | if (inode_mark->mask & IN_ONESHOT) |
| 134 | fsnotify_destroy_mark(inode_mark); | 135 | fsnotify_destroy_mark(inode_mark, group); |
| 135 | 136 | ||
| 136 | return ret; | 137 | return ret; |
| 137 | } | 138 | } |
| @@ -210,6 +211,7 @@ void inotify_free_event_priv(struct fsnotify_event_private_data *fsn_event_priv) | |||
| 210 | event_priv = container_of(fsn_event_priv, struct inotify_event_private_data, | 211 | event_priv = container_of(fsn_event_priv, struct inotify_event_private_data, |
| 211 | fsnotify_event_priv_data); | 212 | fsnotify_event_priv_data); |
| 212 | 213 | ||
| 214 | fsnotify_put_group(fsn_event_priv->group); | ||
| 213 | kmem_cache_free(event_priv_cachep, event_priv); | 215 | kmem_cache_free(event_priv_cachep, event_priv); |
| 214 | } | 216 | } |
| 215 | 217 | ||
diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 36cb013c7c13..228a2c2ad8d7 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c | |||
| @@ -265,7 +265,7 @@ static ssize_t inotify_read(struct file *file, char __user *buf, | |||
| 265 | ret = -EAGAIN; | 265 | ret = -EAGAIN; |
| 266 | if (file->f_flags & O_NONBLOCK) | 266 | if (file->f_flags & O_NONBLOCK) |
| 267 | break; | 267 | break; |
| 268 | ret = -EINTR; | 268 | ret = -ERESTARTSYS; |
| 269 | if (signal_pending(current)) | 269 | if (signal_pending(current)) |
| 270 | break; | 270 | break; |
| 271 | 271 | ||
| @@ -281,23 +281,17 @@ static ssize_t inotify_read(struct file *file, char __user *buf, | |||
| 281 | return ret; | 281 | return ret; |
| 282 | } | 282 | } |
| 283 | 283 | ||
| 284 | static int inotify_fasync(int fd, struct file *file, int on) | ||
| 285 | { | ||
| 286 | struct fsnotify_group *group = file->private_data; | ||
| 287 | |||
| 288 | return fasync_helper(fd, file, on, &group->inotify_data.fa) >= 0 ? 0 : -EIO; | ||
| 289 | } | ||
| 290 | |||
| 291 | static int inotify_release(struct inode *ignored, struct file *file) | 284 | static int inotify_release(struct inode *ignored, struct file *file) |
| 292 | { | 285 | { |
| 293 | struct fsnotify_group *group = file->private_data; | 286 | struct fsnotify_group *group = file->private_data; |
| 294 | 287 | ||
| 295 | pr_debug("%s: group=%p\n", __func__, group); | 288 | pr_debug("%s: group=%p\n", __func__, group); |
| 296 | 289 | ||
| 297 | fsnotify_clear_marks_by_group(group); | 290 | if (file->f_flags & FASYNC) |
| 291 | fsnotify_fasync(-1, file, 0); | ||
| 298 | 292 | ||
| 299 | /* free this group, matching get was inotify_init->fsnotify_obtain_group */ | 293 | /* free this group, matching get was inotify_init->fsnotify_obtain_group */ |
| 300 | fsnotify_put_group(group); | 294 | fsnotify_destroy_group(group); |
| 301 | 295 | ||
| 302 | return 0; | 296 | return 0; |
| 303 | } | 297 | } |
| @@ -339,7 +333,7 @@ static const struct file_operations inotify_fops = { | |||
| 339 | .show_fdinfo = inotify_show_fdinfo, | 333 | .show_fdinfo = inotify_show_fdinfo, |
| 340 | .poll = inotify_poll, | 334 | .poll = inotify_poll, |
| 341 | .read = inotify_read, | 335 | .read = inotify_read, |
| 342 | .fasync = inotify_fasync, | 336 | .fasync = fsnotify_fasync, |
| 343 | .release = inotify_release, | 337 | .release = inotify_release, |
| 344 | .unlocked_ioctl = inotify_ioctl, | 338 | .unlocked_ioctl = inotify_ioctl, |
| 345 | .compat_ioctl = inotify_ioctl, | 339 | .compat_ioctl = inotify_ioctl, |
| @@ -521,13 +515,13 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, | |||
| 521 | struct fsnotify_event_private_data *fsn_event_priv; | 515 | struct fsnotify_event_private_data *fsn_event_priv; |
| 522 | int ret; | 516 | int ret; |
| 523 | 517 | ||
| 518 | i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); | ||
| 519 | |||
| 524 | ignored_event = fsnotify_create_event(NULL, FS_IN_IGNORED, NULL, | 520 | ignored_event = fsnotify_create_event(NULL, FS_IN_IGNORED, NULL, |
| 525 | FSNOTIFY_EVENT_NONE, NULL, 0, | 521 | FSNOTIFY_EVENT_NONE, NULL, 0, |
| 526 | GFP_NOFS); | 522 | GFP_NOFS); |
| 527 | if (!ignored_event) | 523 | if (!ignored_event) |
| 528 | return; | 524 | goto skip_send_ignore; |
| 529 | |||
| 530 | i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); | ||
| 531 | 525 | ||
| 532 | event_priv = kmem_cache_alloc(event_priv_cachep, GFP_NOFS); | 526 | event_priv = kmem_cache_alloc(event_priv_cachep, GFP_NOFS); |
| 533 | if (unlikely(!event_priv)) | 527 | if (unlikely(!event_priv)) |
| @@ -535,6 +529,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, | |||
| 535 | 529 | ||
| 536 | fsn_event_priv = &event_priv->fsnotify_event_priv_data; | 530 | fsn_event_priv = &event_priv->fsnotify_event_priv_data; |
| 537 | 531 | ||
| 532 | fsnotify_get_group(group); | ||
| 538 | fsn_event_priv->group = group; | 533 | fsn_event_priv->group = group; |
| 539 | event_priv->wd = i_mark->wd; | 534 | event_priv->wd = i_mark->wd; |
| 540 | 535 | ||
| @@ -548,9 +543,9 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, | |||
| 548 | } | 543 | } |
| 549 | 544 | ||
| 550 | skip_send_ignore: | 545 | skip_send_ignore: |
| 551 | |||
| 552 | /* matches the reference taken when the event was created */ | 546 | /* matches the reference taken when the event was created */ |
| 553 | fsnotify_put_event(ignored_event); | 547 | if (ignored_event) |
| 548 | fsnotify_put_event(ignored_event); | ||
| 554 | 549 | ||
| 555 | /* remove this mark from the idr */ | 550 | /* remove this mark from the idr */ |
| 556 | inotify_remove_from_idr(group, i_mark); | 551 | inotify_remove_from_idr(group, i_mark); |
| @@ -709,12 +704,11 @@ static struct fsnotify_group *inotify_new_group(unsigned int max_events) | |||
| 709 | spin_lock_init(&group->inotify_data.idr_lock); | 704 | spin_lock_init(&group->inotify_data.idr_lock); |
| 710 | idr_init(&group->inotify_data.idr); | 705 | idr_init(&group->inotify_data.idr); |
| 711 | group->inotify_data.last_wd = 0; | 706 | group->inotify_data.last_wd = 0; |
| 712 | group->inotify_data.fa = NULL; | ||
| 713 | group->inotify_data.user = get_current_user(); | 707 | group->inotify_data.user = get_current_user(); |
| 714 | 708 | ||
| 715 | if (atomic_inc_return(&group->inotify_data.user->inotify_devs) > | 709 | if (atomic_inc_return(&group->inotify_data.user->inotify_devs) > |
| 716 | inotify_max_user_instances) { | 710 | inotify_max_user_instances) { |
| 717 | fsnotify_put_group(group); | 711 | fsnotify_destroy_group(group); |
| 718 | return ERR_PTR(-EMFILE); | 712 | return ERR_PTR(-EMFILE); |
| 719 | } | 713 | } |
| 720 | 714 | ||
| @@ -743,7 +737,7 @@ SYSCALL_DEFINE1(inotify_init1, int, flags) | |||
| 743 | ret = anon_inode_getfd("inotify", &inotify_fops, group, | 737 | ret = anon_inode_getfd("inotify", &inotify_fops, group, |
| 744 | O_RDONLY | flags); | 738 | O_RDONLY | flags); |
| 745 | if (ret < 0) | 739 | if (ret < 0) |
| 746 | fsnotify_put_group(group); | 740 | fsnotify_destroy_group(group); |
| 747 | 741 | ||
| 748 | return ret; | 742 | return ret; |
| 749 | } | 743 | } |
| @@ -819,7 +813,7 @@ SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd) | |||
| 819 | 813 | ||
| 820 | ret = 0; | 814 | ret = 0; |
| 821 | 815 | ||
| 822 | fsnotify_destroy_mark(&i_mark->fsn_mark); | 816 | fsnotify_destroy_mark(&i_mark->fsn_mark, group); |
| 823 | 817 | ||
| 824 | /* match ref taken by inotify_idr_find */ | 818 | /* match ref taken by inotify_idr_find */ |
| 825 | fsnotify_put_mark(&i_mark->fsn_mark); | 819 | fsnotify_put_mark(&i_mark->fsn_mark); |
diff --git a/fs/notify/mark.c b/fs/notify/mark.c index f104d565b682..fc6b49bf7360 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c | |||
| @@ -109,8 +109,11 @@ void fsnotify_get_mark(struct fsnotify_mark *mark) | |||
| 109 | 109 | ||
| 110 | void fsnotify_put_mark(struct fsnotify_mark *mark) | 110 | void fsnotify_put_mark(struct fsnotify_mark *mark) |
| 111 | { | 111 | { |
| 112 | if (atomic_dec_and_test(&mark->refcnt)) | 112 | if (atomic_dec_and_test(&mark->refcnt)) { |
| 113 | if (mark->group) | ||
| 114 | fsnotify_put_group(mark->group); | ||
| 113 | mark->free_mark(mark); | 115 | mark->free_mark(mark); |
| 116 | } | ||
| 114 | } | 117 | } |
| 115 | 118 | ||
| 116 | /* | 119 | /* |
| @@ -118,14 +121,14 @@ void fsnotify_put_mark(struct fsnotify_mark *mark) | |||
| 118 | * The caller had better be holding a reference to this mark so we don't actually | 121 | * The caller had better be holding a reference to this mark so we don't actually |
| 119 | * do the final put under the mark->lock | 122 | * do the final put under the mark->lock |
| 120 | */ | 123 | */ |
| 121 | void fsnotify_destroy_mark(struct fsnotify_mark *mark) | 124 | void fsnotify_destroy_mark_locked(struct fsnotify_mark *mark, |
| 125 | struct fsnotify_group *group) | ||
| 122 | { | 126 | { |
| 123 | struct fsnotify_group *group; | ||
| 124 | struct inode *inode = NULL; | 127 | struct inode *inode = NULL; |
| 125 | 128 | ||
| 126 | spin_lock(&mark->lock); | 129 | BUG_ON(!mutex_is_locked(&group->mark_mutex)); |
| 127 | 130 | ||
| 128 | group = mark->group; | 131 | spin_lock(&mark->lock); |
| 129 | 132 | ||
| 130 | /* something else already called this function on this mark */ | 133 | /* something else already called this function on this mark */ |
| 131 | if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE)) { | 134 | if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE)) { |
| @@ -135,8 +138,6 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) | |||
| 135 | 138 | ||
| 136 | mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; | 139 | mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; |
| 137 | 140 | ||
| 138 | spin_lock(&group->mark_lock); | ||
| 139 | |||
| 140 | if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) { | 141 | if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) { |
| 141 | inode = mark->i.inode; | 142 | inode = mark->i.inode; |
| 142 | fsnotify_destroy_inode_mark(mark); | 143 | fsnotify_destroy_inode_mark(mark); |
| @@ -147,13 +148,22 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) | |||
| 147 | 148 | ||
| 148 | list_del_init(&mark->g_list); | 149 | list_del_init(&mark->g_list); |
| 149 | 150 | ||
| 150 | spin_unlock(&group->mark_lock); | ||
| 151 | spin_unlock(&mark->lock); | 151 | spin_unlock(&mark->lock); |
| 152 | 152 | ||
| 153 | if (inode && (mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED)) | ||
| 154 | iput(inode); | ||
| 155 | /* release lock temporarily */ | ||
| 156 | mutex_unlock(&group->mark_mutex); | ||
| 157 | |||
| 153 | spin_lock(&destroy_lock); | 158 | spin_lock(&destroy_lock); |
| 154 | list_add(&mark->destroy_list, &destroy_list); | 159 | list_add(&mark->destroy_list, &destroy_list); |
| 155 | spin_unlock(&destroy_lock); | 160 | spin_unlock(&destroy_lock); |
| 156 | wake_up(&destroy_waitq); | 161 | wake_up(&destroy_waitq); |
| 162 | /* | ||
| 163 | * We don't necessarily have a ref on mark from caller so the above destroy | ||
| 164 | * may have actually freed it, unless this group provides a 'freeing_mark' | ||
| 165 | * function which must be holding a reference. | ||
| 166 | */ | ||
| 157 | 167 | ||
| 158 | /* | 168 | /* |
| 159 | * Some groups like to know that marks are being freed. This is a | 169 | * Some groups like to know that marks are being freed. This is a |
| @@ -175,21 +185,17 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) | |||
| 175 | * is just a lazy update (and could be a perf win...) | 185 | * is just a lazy update (and could be a perf win...) |
| 176 | */ | 186 | */ |
| 177 | 187 | ||
| 178 | if (inode && (mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED)) | 188 | atomic_dec(&group->num_marks); |
| 179 | iput(inode); | ||
| 180 | 189 | ||
| 181 | /* | 190 | mutex_lock_nested(&group->mark_mutex, SINGLE_DEPTH_NESTING); |
| 182 | * We don't necessarily have a ref on mark from caller so the above iput | 191 | } |
| 183 | * may have already destroyed it. Don't touch from now on. | ||
| 184 | */ | ||
| 185 | 192 | ||
| 186 | /* | 193 | void fsnotify_destroy_mark(struct fsnotify_mark *mark, |
| 187 | * it's possible that this group tried to destroy itself, but this | 194 | struct fsnotify_group *group) |
| 188 | * this mark was simultaneously being freed by inode. If that's the | 195 | { |
| 189 | * case, we finish freeing the group here. | 196 | mutex_lock_nested(&group->mark_mutex, SINGLE_DEPTH_NESTING); |
| 190 | */ | 197 | fsnotify_destroy_mark_locked(mark, group); |
| 191 | if (unlikely(atomic_dec_and_test(&group->num_marks))) | 198 | mutex_unlock(&group->mark_mutex); |
| 192 | fsnotify_final_destroy_group(group); | ||
| 193 | } | 199 | } |
| 194 | 200 | ||
| 195 | void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask) | 201 | void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask) |
| @@ -214,26 +220,26 @@ void fsnotify_set_mark_ignored_mask_locked(struct fsnotify_mark *mark, __u32 mas | |||
| 214 | * These marks may be used for the fsnotify backend to determine which | 220 | * These marks may be used for the fsnotify backend to determine which |
| 215 | * event types should be delivered to which group. | 221 | * event types should be delivered to which group. |
| 216 | */ | 222 | */ |
| 217 | int fsnotify_add_mark(struct fsnotify_mark *mark, | 223 | int fsnotify_add_mark_locked(struct fsnotify_mark *mark, |
| 218 | struct fsnotify_group *group, struct inode *inode, | 224 | struct fsnotify_group *group, struct inode *inode, |
| 219 | struct vfsmount *mnt, int allow_dups) | 225 | struct vfsmount *mnt, int allow_dups) |
| 220 | { | 226 | { |
| 221 | int ret = 0; | 227 | int ret = 0; |
| 222 | 228 | ||
| 223 | BUG_ON(inode && mnt); | 229 | BUG_ON(inode && mnt); |
| 224 | BUG_ON(!inode && !mnt); | 230 | BUG_ON(!inode && !mnt); |
| 231 | BUG_ON(!mutex_is_locked(&group->mark_mutex)); | ||
| 225 | 232 | ||
| 226 | /* | 233 | /* |
| 227 | * LOCKING ORDER!!!! | 234 | * LOCKING ORDER!!!! |
| 235 | * group->mark_mutex | ||
| 228 | * mark->lock | 236 | * mark->lock |
| 229 | * group->mark_lock | ||
| 230 | * inode->i_lock | 237 | * inode->i_lock |
| 231 | */ | 238 | */ |
| 232 | spin_lock(&mark->lock); | 239 | spin_lock(&mark->lock); |
| 233 | spin_lock(&group->mark_lock); | ||
| 234 | |||
| 235 | mark->flags |= FSNOTIFY_MARK_FLAG_ALIVE; | 240 | mark->flags |= FSNOTIFY_MARK_FLAG_ALIVE; |
| 236 | 241 | ||
| 242 | fsnotify_get_group(group); | ||
| 237 | mark->group = group; | 243 | mark->group = group; |
| 238 | list_add(&mark->g_list, &group->marks_list); | 244 | list_add(&mark->g_list, &group->marks_list); |
| 239 | atomic_inc(&group->num_marks); | 245 | atomic_inc(&group->num_marks); |
| @@ -251,11 +257,8 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, | |||
| 251 | BUG(); | 257 | BUG(); |
| 252 | } | 258 | } |
| 253 | 259 | ||
| 254 | spin_unlock(&group->mark_lock); | ||
| 255 | |||
| 256 | /* this will pin the object if appropriate */ | 260 | /* this will pin the object if appropriate */ |
| 257 | fsnotify_set_mark_mask_locked(mark, mark->mask); | 261 | fsnotify_set_mark_mask_locked(mark, mark->mask); |
| 258 | |||
| 259 | spin_unlock(&mark->lock); | 262 | spin_unlock(&mark->lock); |
| 260 | 263 | ||
| 261 | if (inode) | 264 | if (inode) |
| @@ -265,10 +268,10 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, | |||
| 265 | err: | 268 | err: |
| 266 | mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; | 269 | mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; |
| 267 | list_del_init(&mark->g_list); | 270 | list_del_init(&mark->g_list); |
| 271 | fsnotify_put_group(group); | ||
| 268 | mark->group = NULL; | 272 | mark->group = NULL; |
| 269 | atomic_dec(&group->num_marks); | 273 | atomic_dec(&group->num_marks); |
| 270 | 274 | ||
| 271 | spin_unlock(&group->mark_lock); | ||
| 272 | spin_unlock(&mark->lock); | 275 | spin_unlock(&mark->lock); |
| 273 | 276 | ||
| 274 | spin_lock(&destroy_lock); | 277 | spin_lock(&destroy_lock); |
| @@ -279,6 +282,16 @@ err: | |||
| 279 | return ret; | 282 | return ret; |
| 280 | } | 283 | } |
| 281 | 284 | ||
| 285 | int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, | ||
| 286 | struct inode *inode, struct vfsmount *mnt, int allow_dups) | ||
| 287 | { | ||
| 288 | int ret; | ||
| 289 | mutex_lock(&group->mark_mutex); | ||
| 290 | ret = fsnotify_add_mark_locked(mark, group, inode, mnt, allow_dups); | ||
| 291 | mutex_unlock(&group->mark_mutex); | ||
| 292 | return ret; | ||
| 293 | } | ||
| 294 | |||
| 282 | /* | 295 | /* |
| 283 | * clear any marks in a group in which mark->flags & flags is true | 296 | * clear any marks in a group in which mark->flags & flags is true |
| 284 | */ | 297 | */ |
| @@ -286,22 +299,16 @@ void fsnotify_clear_marks_by_group_flags(struct fsnotify_group *group, | |||
| 286 | unsigned int flags) | 299 | unsigned int flags) |
| 287 | { | 300 | { |
| 288 | struct fsnotify_mark *lmark, *mark; | 301 | struct fsnotify_mark *lmark, *mark; |
| 289 | LIST_HEAD(free_list); | ||
| 290 | 302 | ||
| 291 | spin_lock(&group->mark_lock); | 303 | mutex_lock_nested(&group->mark_mutex, SINGLE_DEPTH_NESTING); |
| 292 | list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) { | 304 | list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) { |
| 293 | if (mark->flags & flags) { | 305 | if (mark->flags & flags) { |
| 294 | list_add(&mark->free_g_list, &free_list); | ||
| 295 | list_del_init(&mark->g_list); | ||
| 296 | fsnotify_get_mark(mark); | 306 | fsnotify_get_mark(mark); |
| 307 | fsnotify_destroy_mark_locked(mark, group); | ||
| 308 | fsnotify_put_mark(mark); | ||
| 297 | } | 309 | } |
| 298 | } | 310 | } |
| 299 | spin_unlock(&group->mark_lock); | 311 | mutex_unlock(&group->mark_mutex); |
| 300 | |||
| 301 | list_for_each_entry_safe(mark, lmark, &free_list, free_g_list) { | ||
| 302 | fsnotify_destroy_mark(mark); | ||
| 303 | fsnotify_put_mark(mark); | ||
| 304 | } | ||
| 305 | } | 312 | } |
| 306 | 313 | ||
| 307 | /* | 314 | /* |
| @@ -317,6 +324,8 @@ void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *ol | |||
| 317 | assert_spin_locked(&old->lock); | 324 | assert_spin_locked(&old->lock); |
| 318 | new->i.inode = old->i.inode; | 325 | new->i.inode = old->i.inode; |
| 319 | new->m.mnt = old->m.mnt; | 326 | new->m.mnt = old->m.mnt; |
| 327 | if (old->group) | ||
| 328 | fsnotify_get_group(old->group); | ||
| 320 | new->group = old->group; | 329 | new->group = old->group; |
| 321 | new->mask = old->mask; | 330 | new->mask = old->mask; |
| 322 | new->free_mark = old->free_mark; | 331 | new->free_mark = old->free_mark; |
diff --git a/fs/notify/notification.c b/fs/notify/notification.c index 48cb994e4922..7b51b05f160c 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c | |||
| @@ -225,6 +225,7 @@ alloc_holder: | |||
| 225 | mutex_unlock(&group->notification_mutex); | 225 | mutex_unlock(&group->notification_mutex); |
| 226 | 226 | ||
| 227 | wake_up(&group->notification_waitq); | 227 | wake_up(&group->notification_waitq); |
| 228 | kill_fasync(&group->fsn_fa, SIGIO, POLL_IN); | ||
| 228 | return return_event; | 229 | return return_event; |
| 229 | } | 230 | } |
| 230 | 231 | ||
diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c index b7b4b0e8554f..4df58b8ea64a 100644 --- a/fs/notify/vfsmount_mark.c +++ b/fs/notify/vfsmount_mark.c | |||
| @@ -46,8 +46,16 @@ void fsnotify_clear_marks_by_mount(struct vfsmount *mnt) | |||
| 46 | spin_unlock(&mnt->mnt_root->d_lock); | 46 | spin_unlock(&mnt->mnt_root->d_lock); |
| 47 | 47 | ||
| 48 | list_for_each_entry_safe(mark, lmark, &free_list, m.free_m_list) { | 48 | list_for_each_entry_safe(mark, lmark, &free_list, m.free_m_list) { |
| 49 | fsnotify_destroy_mark(mark); | 49 | struct fsnotify_group *group; |
| 50 | |||
| 51 | spin_lock(&mark->lock); | ||
| 52 | fsnotify_get_group(mark->group); | ||
| 53 | group = mark->group; | ||
| 54 | spin_unlock(&mark->lock); | ||
| 55 | |||
| 56 | fsnotify_destroy_mark(mark, group); | ||
| 50 | fsnotify_put_mark(mark); | 57 | fsnotify_put_mark(mark); |
| 58 | fsnotify_put_group(group); | ||
| 51 | } | 59 | } |
| 52 | } | 60 | } |
| 53 | 61 | ||
| @@ -88,8 +96,8 @@ void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark) | |||
| 88 | { | 96 | { |
| 89 | struct vfsmount *mnt = mark->m.mnt; | 97 | struct vfsmount *mnt = mark->m.mnt; |
| 90 | 98 | ||
| 99 | BUG_ON(!mutex_is_locked(&mark->group->mark_mutex)); | ||
| 91 | assert_spin_locked(&mark->lock); | 100 | assert_spin_locked(&mark->lock); |
| 92 | assert_spin_locked(&mark->group->mark_lock); | ||
| 93 | 101 | ||
| 94 | spin_lock(&mnt->mnt_root->d_lock); | 102 | spin_lock(&mnt->mnt_root->d_lock); |
| 95 | 103 | ||
| @@ -151,8 +159,8 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, | |||
| 151 | 159 | ||
| 152 | mark->flags |= FSNOTIFY_MARK_FLAG_VFSMOUNT; | 160 | mark->flags |= FSNOTIFY_MARK_FLAG_VFSMOUNT; |
| 153 | 161 | ||
| 162 | BUG_ON(!mutex_is_locked(&group->mark_mutex)); | ||
| 154 | assert_spin_locked(&mark->lock); | 163 | assert_spin_locked(&mark->lock); |
| 155 | assert_spin_locked(&group->mark_lock); | ||
| 156 | 164 | ||
| 157 | spin_lock(&mnt->mnt_root->d_lock); | 165 | spin_lock(&mnt->mnt_root->d_lock); |
| 158 | 166 | ||
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h index 63d966d5c2ea..d5b0910d4961 100644 --- a/include/linux/fsnotify_backend.h +++ b/include/linux/fsnotify_backend.h | |||
| @@ -88,9 +88,10 @@ struct fsnotify_event_private_data; | |||
| 88 | * if the group is interested in this event. | 88 | * if the group is interested in this event. |
| 89 | * handle_event - main call for a group to handle an fs event | 89 | * handle_event - main call for a group to handle an fs event |
| 90 | * free_group_priv - called when a group refcnt hits 0 to clean up the private union | 90 | * free_group_priv - called when a group refcnt hits 0 to clean up the private union |
| 91 | * freeing-mark - this means that a mark has been flagged to die when everything | 91 | * freeing_mark - called when a mark is being destroyed for some reason. The group |
| 92 | * finishes using it. The function is supplied with what must be a | 92 | * MUST be holding a reference on each mark and that reference must be |
| 93 | * valid group and inode to use to clean up. | 93 | * dropped in this function. inotify uses this function to send |
| 94 | * userspace messages that marks have been removed. | ||
| 94 | */ | 95 | */ |
| 95 | struct fsnotify_ops { | 96 | struct fsnotify_ops { |
| 96 | bool (*should_send_event)(struct fsnotify_group *group, struct inode *inode, | 97 | bool (*should_send_event)(struct fsnotify_group *group, struct inode *inode, |
| @@ -141,12 +142,14 @@ struct fsnotify_group { | |||
| 141 | unsigned int priority; | 142 | unsigned int priority; |
| 142 | 143 | ||
| 143 | /* stores all fastpath marks assoc with this group so they can be cleaned on unregister */ | 144 | /* stores all fastpath marks assoc with this group so they can be cleaned on unregister */ |
| 144 | spinlock_t mark_lock; /* protect marks_list */ | 145 | struct mutex mark_mutex; /* protect marks_list */ |
| 145 | atomic_t num_marks; /* 1 for each mark and 1 for not being | 146 | atomic_t num_marks; /* 1 for each mark and 1 for not being |
| 146 | * past the point of no return when freeing | 147 | * past the point of no return when freeing |
| 147 | * a group */ | 148 | * a group */ |
| 148 | struct list_head marks_list; /* all inode marks for this group */ | 149 | struct list_head marks_list; /* all inode marks for this group */ |
| 149 | 150 | ||
| 151 | struct fasync_struct *fsn_fa; /* async notification */ | ||
| 152 | |||
| 150 | /* groups can define private fields here or use the void *private */ | 153 | /* groups can define private fields here or use the void *private */ |
| 151 | union { | 154 | union { |
| 152 | void *private; | 155 | void *private; |
| @@ -155,7 +158,6 @@ struct fsnotify_group { | |||
| 155 | spinlock_t idr_lock; | 158 | spinlock_t idr_lock; |
| 156 | struct idr idr; | 159 | struct idr idr; |
| 157 | u32 last_wd; | 160 | u32 last_wd; |
| 158 | struct fasync_struct *fa; /* async notification */ | ||
| 159 | struct user_struct *user; | 161 | struct user_struct *user; |
| 160 | } inotify_data; | 162 | } inotify_data; |
| 161 | #endif | 163 | #endif |
| @@ -287,7 +289,6 @@ struct fsnotify_mark { | |||
| 287 | struct fsnotify_inode_mark i; | 289 | struct fsnotify_inode_mark i; |
| 288 | struct fsnotify_vfsmount_mark m; | 290 | struct fsnotify_vfsmount_mark m; |
| 289 | }; | 291 | }; |
| 290 | struct list_head free_g_list; /* tmp list used when freeing this mark */ | ||
| 291 | __u32 ignored_mask; /* events types to ignore */ | 292 | __u32 ignored_mask; /* events types to ignore */ |
| 292 | #define FSNOTIFY_MARK_FLAG_INODE 0x01 | 293 | #define FSNOTIFY_MARK_FLAG_INODE 0x01 |
| 293 | #define FSNOTIFY_MARK_FLAG_VFSMOUNT 0x02 | 294 | #define FSNOTIFY_MARK_FLAG_VFSMOUNT 0x02 |
| @@ -360,11 +361,16 @@ static inline void __fsnotify_d_instantiate(struct dentry *dentry, struct inode | |||
| 360 | 361 | ||
| 361 | /* called from fsnotify listeners, such as fanotify or dnotify */ | 362 | /* called from fsnotify listeners, such as fanotify or dnotify */ |
| 362 | 363 | ||
| 363 | /* get a reference to an existing or create a new group */ | 364 | /* create a new group */ |
| 364 | extern struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops); | 365 | extern struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops); |
| 366 | /* get reference to a group */ | ||
| 367 | extern void fsnotify_get_group(struct fsnotify_group *group); | ||
| 365 | /* drop reference on a group from fsnotify_alloc_group */ | 368 | /* drop reference on a group from fsnotify_alloc_group */ |
| 366 | extern void fsnotify_put_group(struct fsnotify_group *group); | 369 | extern void fsnotify_put_group(struct fsnotify_group *group); |
| 367 | 370 | /* destroy group */ | |
| 371 | extern void fsnotify_destroy_group(struct fsnotify_group *group); | ||
| 372 | /* fasync handler function */ | ||
| 373 | extern int fsnotify_fasync(int fd, struct file *file, int on); | ||
| 368 | /* take a reference to an event */ | 374 | /* take a reference to an event */ |
| 369 | extern void fsnotify_get_event(struct fsnotify_event *event); | 375 | extern void fsnotify_get_event(struct fsnotify_event *event); |
| 370 | extern void fsnotify_put_event(struct fsnotify_event *event); | 376 | extern void fsnotify_put_event(struct fsnotify_event *event); |
| @@ -405,8 +411,13 @@ extern void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask | |||
| 405 | /* attach the mark to both the group and the inode */ | 411 | /* attach the mark to both the group and the inode */ |
| 406 | extern int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, | 412 | extern int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, |
| 407 | struct inode *inode, struct vfsmount *mnt, int allow_dups); | 413 | struct inode *inode, struct vfsmount *mnt, int allow_dups); |
| 408 | /* given a mark, flag it to be freed when all references are dropped */ | 414 | extern int fsnotify_add_mark_locked(struct fsnotify_mark *mark, struct fsnotify_group *group, |
| 409 | extern void fsnotify_destroy_mark(struct fsnotify_mark *mark); | 415 | struct inode *inode, struct vfsmount *mnt, int allow_dups); |
| 416 | /* given a group and a mark, flag mark to be freed when all references are dropped */ | ||
| 417 | extern void fsnotify_destroy_mark(struct fsnotify_mark *mark, | ||
| 418 | struct fsnotify_group *group); | ||
| 419 | extern void fsnotify_destroy_mark_locked(struct fsnotify_mark *mark, | ||
| 420 | struct fsnotify_group *group); | ||
| 410 | /* run all the marks in a group, and clear all of the vfsmount marks */ | 421 | /* run all the marks in a group, and clear all of the vfsmount marks */ |
| 411 | extern void fsnotify_clear_vfsmount_marks_by_group(struct fsnotify_group *group); | 422 | extern void fsnotify_clear_vfsmount_marks_by_group(struct fsnotify_group *group); |
| 412 | /* run all the marks in a group, and clear all of the inode marks */ | 423 | /* run all the marks in a group, and clear all of the inode marks */ |
diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index ed206fd88cca..e81175ef25f8 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c | |||
| @@ -249,7 +249,7 @@ static void untag_chunk(struct node *p) | |||
| 249 | list_del_rcu(&chunk->hash); | 249 | list_del_rcu(&chunk->hash); |
| 250 | spin_unlock(&hash_lock); | 250 | spin_unlock(&hash_lock); |
| 251 | spin_unlock(&entry->lock); | 251 | spin_unlock(&entry->lock); |
| 252 | fsnotify_destroy_mark(entry); | 252 | fsnotify_destroy_mark(entry, audit_tree_group); |
| 253 | goto out; | 253 | goto out; |
| 254 | } | 254 | } |
| 255 | 255 | ||
| @@ -291,7 +291,7 @@ static void untag_chunk(struct node *p) | |||
| 291 | owner->root = new; | 291 | owner->root = new; |
| 292 | spin_unlock(&hash_lock); | 292 | spin_unlock(&hash_lock); |
| 293 | spin_unlock(&entry->lock); | 293 | spin_unlock(&entry->lock); |
| 294 | fsnotify_destroy_mark(entry); | 294 | fsnotify_destroy_mark(entry, audit_tree_group); |
| 295 | fsnotify_put_mark(&new->mark); /* drop initial reference */ | 295 | fsnotify_put_mark(&new->mark); /* drop initial reference */ |
| 296 | goto out; | 296 | goto out; |
| 297 | 297 | ||
| @@ -331,7 +331,7 @@ static int create_chunk(struct inode *inode, struct audit_tree *tree) | |||
| 331 | spin_unlock(&hash_lock); | 331 | spin_unlock(&hash_lock); |
| 332 | chunk->dead = 1; | 332 | chunk->dead = 1; |
| 333 | spin_unlock(&entry->lock); | 333 | spin_unlock(&entry->lock); |
| 334 | fsnotify_destroy_mark(entry); | 334 | fsnotify_destroy_mark(entry, audit_tree_group); |
| 335 | fsnotify_put_mark(entry); | 335 | fsnotify_put_mark(entry); |
| 336 | return 0; | 336 | return 0; |
| 337 | } | 337 | } |
| @@ -412,7 +412,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) | |||
| 412 | spin_unlock(&chunk_entry->lock); | 412 | spin_unlock(&chunk_entry->lock); |
| 413 | spin_unlock(&old_entry->lock); | 413 | spin_unlock(&old_entry->lock); |
| 414 | 414 | ||
| 415 | fsnotify_destroy_mark(chunk_entry); | 415 | fsnotify_destroy_mark(chunk_entry, audit_tree_group); |
| 416 | 416 | ||
| 417 | fsnotify_put_mark(chunk_entry); | 417 | fsnotify_put_mark(chunk_entry); |
| 418 | fsnotify_put_mark(old_entry); | 418 | fsnotify_put_mark(old_entry); |
| @@ -443,7 +443,7 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) | |||
| 443 | spin_unlock(&hash_lock); | 443 | spin_unlock(&hash_lock); |
| 444 | spin_unlock(&chunk_entry->lock); | 444 | spin_unlock(&chunk_entry->lock); |
| 445 | spin_unlock(&old_entry->lock); | 445 | spin_unlock(&old_entry->lock); |
| 446 | fsnotify_destroy_mark(old_entry); | 446 | fsnotify_destroy_mark(old_entry, audit_tree_group); |
| 447 | fsnotify_put_mark(chunk_entry); /* drop initial reference */ | 447 | fsnotify_put_mark(chunk_entry); /* drop initial reference */ |
| 448 | fsnotify_put_mark(old_entry); /* pair to fsnotify_find mark_entry */ | 448 | fsnotify_put_mark(old_entry); /* pair to fsnotify_find mark_entry */ |
| 449 | return 0; | 449 | return 0; |
diff --git a/kernel/audit_watch.c b/kernel/audit_watch.c index 9a9ae6e3d290..4a599f699adc 100644 --- a/kernel/audit_watch.c +++ b/kernel/audit_watch.c | |||
| @@ -350,7 +350,7 @@ static void audit_remove_parent_watches(struct audit_parent *parent) | |||
| 350 | } | 350 | } |
| 351 | mutex_unlock(&audit_filter_mutex); | 351 | mutex_unlock(&audit_filter_mutex); |
| 352 | 352 | ||
| 353 | fsnotify_destroy_mark(&parent->mark); | 353 | fsnotify_destroy_mark(&parent->mark, audit_watch_group); |
| 354 | } | 354 | } |
| 355 | 355 | ||
| 356 | /* Get path information necessary for adding watches. */ | 356 | /* Get path information necessary for adding watches. */ |
| @@ -457,7 +457,7 @@ void audit_remove_watch_rule(struct audit_krule *krule) | |||
| 457 | 457 | ||
| 458 | if (list_empty(&parent->watches)) { | 458 | if (list_empty(&parent->watches)) { |
| 459 | audit_get_parent(parent); | 459 | audit_get_parent(parent); |
| 460 | fsnotify_destroy_mark(&parent->mark); | 460 | fsnotify_destroy_mark(&parent->mark, audit_watch_group); |
| 461 | audit_put_parent(parent); | 461 | audit_put_parent(parent); |
| 462 | } | 462 | } |
| 463 | } | 463 | } |
