| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * fs/inotify_user.c - inotify support for userspace |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 10 | 11 | * |
|---|
| 11 | 12 | * Copyright (C) 2009 Eric Paris <Red Hat Inc> |
|---|
| 12 | 13 | * inotify was largely rewriten to make use of the fsnotify infrastructure |
|---|
| 13 | | - * |
|---|
| 14 | | - * This program is free software; you can redistribute it and/or modify it |
|---|
| 15 | | - * under the terms of the GNU General Public License as published by the |
|---|
| 16 | | - * Free Software Foundation; either version 2, or (at your option) any |
|---|
| 17 | | - * later version. |
|---|
| 18 | | - * |
|---|
| 19 | | - * This program is distributed in the hope that it will be useful, but |
|---|
| 20 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 21 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|---|
| 22 | | - * General Public License for more details. |
|---|
| 23 | 14 | */ |
|---|
| 24 | 15 | |
|---|
| 25 | 16 | #include <linux/dcache.h> /* d_unlinked */ |
|---|
| .. | .. |
|---|
| 43 | 34 | { |
|---|
| 44 | 35 | struct inotify_event_info *old, *new; |
|---|
| 45 | 36 | |
|---|
| 46 | | - if (old_fsn->mask & FS_IN_IGNORED) |
|---|
| 47 | | - return false; |
|---|
| 48 | 37 | old = INOTIFY_E(old_fsn); |
|---|
| 49 | 38 | new = INOTIFY_E(new_fsn); |
|---|
| 50 | | - if ((old_fsn->mask == new_fsn->mask) && |
|---|
| 51 | | - (old_fsn->inode == new_fsn->inode) && |
|---|
| 39 | + if (old->mask & FS_IN_IGNORED) |
|---|
| 40 | + return false; |
|---|
| 41 | + if ((old->mask == new->mask) && |
|---|
| 42 | + (old->wd == new->wd) && |
|---|
| 52 | 43 | (old->name_len == new->name_len) && |
|---|
| 53 | 44 | (!old->name_len || !strcmp(old->name, new->name))) |
|---|
| 54 | 45 | return true; |
|---|
| .. | .. |
|---|
| 64 | 55 | return event_compare(last_event, event); |
|---|
| 65 | 56 | } |
|---|
| 66 | 57 | |
|---|
| 67 | | -int inotify_handle_event(struct fsnotify_group *group, |
|---|
| 68 | | - struct inode *inode, |
|---|
| 69 | | - u32 mask, const void *data, int data_type, |
|---|
| 70 | | - const unsigned char *file_name, u32 cookie, |
|---|
| 71 | | - struct fsnotify_iter_info *iter_info) |
|---|
| 58 | +int inotify_handle_inode_event(struct fsnotify_mark *inode_mark, u32 mask, |
|---|
| 59 | + struct inode *inode, struct inode *dir, |
|---|
| 60 | + const struct qstr *name, u32 cookie) |
|---|
| 72 | 61 | { |
|---|
| 73 | | - struct fsnotify_mark *inode_mark = fsnotify_iter_inode_mark(iter_info); |
|---|
| 74 | 62 | struct inotify_inode_mark *i_mark; |
|---|
| 75 | 63 | struct inotify_event_info *event; |
|---|
| 76 | 64 | struct fsnotify_event *fsn_event; |
|---|
| 65 | + struct fsnotify_group *group = inode_mark->group; |
|---|
| 77 | 66 | int ret; |
|---|
| 78 | | - int len = 0; |
|---|
| 67 | + int len = 0, wd; |
|---|
| 79 | 68 | int alloc_len = sizeof(struct inotify_event_info); |
|---|
| 69 | + struct mem_cgroup *old_memcg; |
|---|
| 80 | 70 | |
|---|
| 81 | | - if (WARN_ON(fsnotify_iter_vfsmount_mark(iter_info))) |
|---|
| 82 | | - return 0; |
|---|
| 83 | | - |
|---|
| 84 | | - if ((inode_mark->mask & FS_EXCL_UNLINK) && |
|---|
| 85 | | - (data_type == FSNOTIFY_EVENT_PATH)) { |
|---|
| 86 | | - const struct path *path = data; |
|---|
| 87 | | - |
|---|
| 88 | | - if (d_unlinked(path->dentry)) |
|---|
| 89 | | - return 0; |
|---|
| 90 | | - } |
|---|
| 91 | | - if (file_name) { |
|---|
| 92 | | - len = strlen(file_name); |
|---|
| 71 | + if (name) { |
|---|
| 72 | + len = name->len; |
|---|
| 93 | 73 | alloc_len += len + 1; |
|---|
| 94 | 74 | } |
|---|
| 95 | 75 | |
|---|
| 96 | | - pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode, |
|---|
| 76 | + pr_debug("%s: group=%p mark=%p mask=%x\n", __func__, group, inode_mark, |
|---|
| 97 | 77 | mask); |
|---|
| 98 | 78 | |
|---|
| 99 | 79 | i_mark = container_of(inode_mark, struct inotify_inode_mark, |
|---|
| 100 | 80 | fsn_mark); |
|---|
| 101 | 81 | |
|---|
| 102 | 82 | /* |
|---|
| 83 | + * We can be racing with mark being detached. Don't report event with |
|---|
| 84 | + * invalid wd. |
|---|
| 85 | + */ |
|---|
| 86 | + wd = READ_ONCE(i_mark->wd); |
|---|
| 87 | + if (wd == -1) |
|---|
| 88 | + return 0; |
|---|
| 89 | + /* |
|---|
| 103 | 90 | * Whoever is interested in the event, pays for the allocation. Do not |
|---|
| 104 | 91 | * trigger OOM killer in the target monitoring memcg as it may have |
|---|
| 105 | 92 | * security repercussion. |
|---|
| 106 | 93 | */ |
|---|
| 107 | | - memalloc_use_memcg(group->memcg); |
|---|
| 94 | + old_memcg = set_active_memcg(group->memcg); |
|---|
| 108 | 95 | event = kmalloc(alloc_len, GFP_KERNEL_ACCOUNT | __GFP_RETRY_MAYFAIL); |
|---|
| 109 | | - memalloc_unuse_memcg(); |
|---|
| 96 | + set_active_memcg(old_memcg); |
|---|
| 110 | 97 | |
|---|
| 111 | 98 | if (unlikely(!event)) { |
|---|
| 112 | 99 | /* |
|---|
| .. | .. |
|---|
| 117 | 104 | return -ENOMEM; |
|---|
| 118 | 105 | } |
|---|
| 119 | 106 | |
|---|
| 107 | + /* |
|---|
| 108 | + * We now report FS_ISDIR flag with MOVE_SELF and DELETE_SELF events |
|---|
| 109 | + * for fanotify. inotify never reported IN_ISDIR with those events. |
|---|
| 110 | + * It looks like an oversight, but to avoid the risk of breaking |
|---|
| 111 | + * existing inotify programs, mask the flag out from those events. |
|---|
| 112 | + */ |
|---|
| 113 | + if (mask & (IN_MOVE_SELF | IN_DELETE_SELF)) |
|---|
| 114 | + mask &= ~IN_ISDIR; |
|---|
| 115 | + |
|---|
| 120 | 116 | fsn_event = &event->fse; |
|---|
| 121 | | - fsnotify_init_event(fsn_event, inode, mask); |
|---|
| 122 | | - event->wd = i_mark->wd; |
|---|
| 117 | + fsnotify_init_event(fsn_event, 0); |
|---|
| 118 | + event->mask = mask; |
|---|
| 119 | + event->wd = wd; |
|---|
| 123 | 120 | event->sync_cookie = cookie; |
|---|
| 124 | 121 | event->name_len = len; |
|---|
| 125 | 122 | if (len) |
|---|
| 126 | | - strcpy(event->name, file_name); |
|---|
| 123 | + strcpy(event->name, name->name); |
|---|
| 127 | 124 | |
|---|
| 128 | 125 | ret = fsnotify_add_event(group, fsn_event, inotify_merge); |
|---|
| 129 | 126 | if (ret) { |
|---|
| .. | .. |
|---|
| 202 | 199 | } |
|---|
| 203 | 200 | |
|---|
| 204 | 201 | const struct fsnotify_ops inotify_fsnotify_ops = { |
|---|
| 205 | | - .handle_event = inotify_handle_event, |
|---|
| 202 | + .handle_inode_event = inotify_handle_inode_event, |
|---|
| 206 | 203 | .free_group_priv = inotify_free_group_priv, |
|---|
| 207 | 204 | .free_event = inotify_free_event, |
|---|
| 208 | 205 | .freeing_mark = inotify_freeing_mark, |
|---|