.. | .. |
---|
13 | 13 | #include <linux/wait.h> |
---|
14 | 14 | #include <linux/audit.h> |
---|
15 | 15 | #include <linux/sched/mm.h> |
---|
| 16 | +#include <linux/statfs.h> |
---|
16 | 17 | |
---|
17 | 18 | #include "fanotify.h" |
---|
18 | 19 | |
---|
19 | | -static bool should_merge(struct fsnotify_event *old_fsn, |
---|
20 | | - struct fsnotify_event *new_fsn) |
---|
| 20 | +static bool fanotify_path_equal(struct path *p1, struct path *p2) |
---|
21 | 21 | { |
---|
22 | | - struct fanotify_event_info *old, *new; |
---|
| 22 | + return p1->mnt == p2->mnt && p1->dentry == p2->dentry; |
---|
| 23 | +} |
---|
| 24 | + |
---|
| 25 | +static inline bool fanotify_fsid_equal(__kernel_fsid_t *fsid1, |
---|
| 26 | + __kernel_fsid_t *fsid2) |
---|
| 27 | +{ |
---|
| 28 | + return fsid1->val[0] == fsid2->val[0] && fsid1->val[1] == fsid2->val[1]; |
---|
| 29 | +} |
---|
| 30 | + |
---|
| 31 | +static bool fanotify_fh_equal(struct fanotify_fh *fh1, |
---|
| 32 | + struct fanotify_fh *fh2) |
---|
| 33 | +{ |
---|
| 34 | + if (fh1->type != fh2->type || fh1->len != fh2->len) |
---|
| 35 | + return false; |
---|
| 36 | + |
---|
| 37 | + return !fh1->len || |
---|
| 38 | + !memcmp(fanotify_fh_buf(fh1), fanotify_fh_buf(fh2), fh1->len); |
---|
| 39 | +} |
---|
| 40 | + |
---|
| 41 | +static bool fanotify_fid_event_equal(struct fanotify_fid_event *ffe1, |
---|
| 42 | + struct fanotify_fid_event *ffe2) |
---|
| 43 | +{ |
---|
| 44 | + /* Do not merge fid events without object fh */ |
---|
| 45 | + if (!ffe1->object_fh.len) |
---|
| 46 | + return false; |
---|
| 47 | + |
---|
| 48 | + return fanotify_fsid_equal(&ffe1->fsid, &ffe2->fsid) && |
---|
| 49 | + fanotify_fh_equal(&ffe1->object_fh, &ffe2->object_fh); |
---|
| 50 | +} |
---|
| 51 | + |
---|
| 52 | +static bool fanotify_info_equal(struct fanotify_info *info1, |
---|
| 53 | + struct fanotify_info *info2) |
---|
| 54 | +{ |
---|
| 55 | + if (info1->dir_fh_totlen != info2->dir_fh_totlen || |
---|
| 56 | + info1->file_fh_totlen != info2->file_fh_totlen || |
---|
| 57 | + info1->name_len != info2->name_len) |
---|
| 58 | + return false; |
---|
| 59 | + |
---|
| 60 | + if (info1->dir_fh_totlen && |
---|
| 61 | + !fanotify_fh_equal(fanotify_info_dir_fh(info1), |
---|
| 62 | + fanotify_info_dir_fh(info2))) |
---|
| 63 | + return false; |
---|
| 64 | + |
---|
| 65 | + if (info1->file_fh_totlen && |
---|
| 66 | + !fanotify_fh_equal(fanotify_info_file_fh(info1), |
---|
| 67 | + fanotify_info_file_fh(info2))) |
---|
| 68 | + return false; |
---|
| 69 | + |
---|
| 70 | + return !info1->name_len || |
---|
| 71 | + !memcmp(fanotify_info_name(info1), fanotify_info_name(info2), |
---|
| 72 | + info1->name_len); |
---|
| 73 | +} |
---|
| 74 | + |
---|
| 75 | +static bool fanotify_name_event_equal(struct fanotify_name_event *fne1, |
---|
| 76 | + struct fanotify_name_event *fne2) |
---|
| 77 | +{ |
---|
| 78 | + struct fanotify_info *info1 = &fne1->info; |
---|
| 79 | + struct fanotify_info *info2 = &fne2->info; |
---|
| 80 | + |
---|
| 81 | + /* Do not merge name events without dir fh */ |
---|
| 82 | + if (!info1->dir_fh_totlen) |
---|
| 83 | + return false; |
---|
| 84 | + |
---|
| 85 | + if (!fanotify_fsid_equal(&fne1->fsid, &fne2->fsid)) |
---|
| 86 | + return false; |
---|
| 87 | + |
---|
| 88 | + return fanotify_info_equal(info1, info2); |
---|
| 89 | +} |
---|
| 90 | + |
---|
| 91 | +static bool fanotify_should_merge(struct fsnotify_event *old_fsn, |
---|
| 92 | + struct fsnotify_event *new_fsn) |
---|
| 93 | +{ |
---|
| 94 | + struct fanotify_event *old, *new; |
---|
23 | 95 | |
---|
24 | 96 | pr_debug("%s: old=%p new=%p\n", __func__, old_fsn, new_fsn); |
---|
25 | 97 | old = FANOTIFY_E(old_fsn); |
---|
26 | 98 | new = FANOTIFY_E(new_fsn); |
---|
27 | 99 | |
---|
28 | | - if (old_fsn->inode == new_fsn->inode && old->tgid == new->tgid && |
---|
29 | | - old->path.mnt == new->path.mnt && |
---|
30 | | - old->path.dentry == new->path.dentry) |
---|
31 | | - return true; |
---|
| 100 | + if (old_fsn->objectid != new_fsn->objectid || |
---|
| 101 | + old->type != new->type || old->pid != new->pid) |
---|
| 102 | + return false; |
---|
| 103 | + |
---|
| 104 | + /* |
---|
| 105 | + * We want to merge many dirent events in the same dir (i.e. |
---|
| 106 | + * creates/unlinks/renames), but we do not want to merge dirent |
---|
| 107 | + * events referring to subdirs with dirent events referring to |
---|
| 108 | + * non subdirs, otherwise, user won't be able to tell from a |
---|
| 109 | + * mask FAN_CREATE|FAN_DELETE|FAN_ONDIR if it describes mkdir+ |
---|
| 110 | + * unlink pair or rmdir+create pair of events. |
---|
| 111 | + */ |
---|
| 112 | + if ((old->mask & FS_ISDIR) != (new->mask & FS_ISDIR)) |
---|
| 113 | + return false; |
---|
| 114 | + |
---|
| 115 | + switch (old->type) { |
---|
| 116 | + case FANOTIFY_EVENT_TYPE_PATH: |
---|
| 117 | + return fanotify_path_equal(fanotify_event_path(old), |
---|
| 118 | + fanotify_event_path(new)); |
---|
| 119 | + case FANOTIFY_EVENT_TYPE_FID: |
---|
| 120 | + return fanotify_fid_event_equal(FANOTIFY_FE(old), |
---|
| 121 | + FANOTIFY_FE(new)); |
---|
| 122 | + case FANOTIFY_EVENT_TYPE_FID_NAME: |
---|
| 123 | + return fanotify_name_event_equal(FANOTIFY_NE(old), |
---|
| 124 | + FANOTIFY_NE(new)); |
---|
| 125 | + default: |
---|
| 126 | + WARN_ON_ONCE(1); |
---|
| 127 | + } |
---|
| 128 | + |
---|
32 | 129 | return false; |
---|
33 | 130 | } |
---|
| 131 | + |
---|
| 132 | +/* Limit event merges to limit CPU overhead per event */ |
---|
| 133 | +#define FANOTIFY_MAX_MERGE_EVENTS 128 |
---|
34 | 134 | |
---|
35 | 135 | /* and the list better be locked by something too! */ |
---|
36 | 136 | static int fanotify_merge(struct list_head *list, struct fsnotify_event *event) |
---|
37 | 137 | { |
---|
38 | 138 | struct fsnotify_event *test_event; |
---|
| 139 | + struct fanotify_event *new; |
---|
| 140 | + int i = 0; |
---|
39 | 141 | |
---|
40 | 142 | pr_debug("%s: list=%p event=%p\n", __func__, list, event); |
---|
| 143 | + new = FANOTIFY_E(event); |
---|
41 | 144 | |
---|
42 | 145 | /* |
---|
43 | 146 | * Don't merge a permission event with any other event so that we know |
---|
44 | 147 | * the event structure we have created in fanotify_handle_event() is the |
---|
45 | 148 | * one we should check for permission response. |
---|
46 | 149 | */ |
---|
47 | | - if (fanotify_is_perm_event(event->mask)) |
---|
| 150 | + if (fanotify_is_perm_event(new->mask)) |
---|
48 | 151 | return 0; |
---|
49 | 152 | |
---|
50 | 153 | list_for_each_entry_reverse(test_event, list, list) { |
---|
51 | | - if (should_merge(test_event, event)) { |
---|
52 | | - test_event->mask |= event->mask; |
---|
| 154 | + if (++i > FANOTIFY_MAX_MERGE_EVENTS) |
---|
| 155 | + break; |
---|
| 156 | + if (fanotify_should_merge(test_event, event)) { |
---|
| 157 | + FANOTIFY_E(test_event)->mask |= new->mask; |
---|
53 | 158 | return 1; |
---|
54 | 159 | } |
---|
55 | 160 | } |
---|
.. | .. |
---|
57 | 162 | return 0; |
---|
58 | 163 | } |
---|
59 | 164 | |
---|
| 165 | +/* |
---|
| 166 | + * Wait for response to permission event. The function also takes care of |
---|
| 167 | + * freeing the permission event (or offloads that in case the wait is canceled |
---|
| 168 | + * by a signal). The function returns 0 in case access got allowed by userspace, |
---|
| 169 | + * -EPERM in case userspace disallowed the access, and -ERESTARTSYS in case |
---|
| 170 | + * the wait got interrupted by a signal. |
---|
| 171 | + */ |
---|
60 | 172 | static int fanotify_get_response(struct fsnotify_group *group, |
---|
61 | | - struct fanotify_perm_event_info *event, |
---|
| 173 | + struct fanotify_perm_event *event, |
---|
62 | 174 | struct fsnotify_iter_info *iter_info) |
---|
63 | 175 | { |
---|
64 | 176 | int ret; |
---|
65 | 177 | |
---|
66 | 178 | pr_debug("%s: group=%p event=%p\n", __func__, group, event); |
---|
67 | 179 | |
---|
68 | | - wait_event(group->fanotify_data.access_waitq, event->response); |
---|
| 180 | + ret = wait_event_killable(group->fanotify_data.access_waitq, |
---|
| 181 | + event->state == FAN_EVENT_ANSWERED); |
---|
| 182 | + /* Signal pending? */ |
---|
| 183 | + if (ret < 0) { |
---|
| 184 | + spin_lock(&group->notification_lock); |
---|
| 185 | + /* Event reported to userspace and no answer yet? */ |
---|
| 186 | + if (event->state == FAN_EVENT_REPORTED) { |
---|
| 187 | + /* Event will get freed once userspace answers to it */ |
---|
| 188 | + event->state = FAN_EVENT_CANCELED; |
---|
| 189 | + spin_unlock(&group->notification_lock); |
---|
| 190 | + return ret; |
---|
| 191 | + } |
---|
| 192 | + /* Event not yet reported? Just remove it. */ |
---|
| 193 | + if (event->state == FAN_EVENT_INIT) |
---|
| 194 | + fsnotify_remove_queued_event(group, &event->fae.fse); |
---|
| 195 | + /* |
---|
| 196 | + * Event may be also answered in case signal delivery raced |
---|
| 197 | + * with wakeup. In that case we have nothing to do besides |
---|
| 198 | + * freeing the event and reporting error. |
---|
| 199 | + */ |
---|
| 200 | + spin_unlock(&group->notification_lock); |
---|
| 201 | + goto out; |
---|
| 202 | + } |
---|
69 | 203 | |
---|
70 | 204 | /* userspace responded, convert to something usable */ |
---|
71 | 205 | switch (event->response & ~FAN_AUDIT) { |
---|
.. | .. |
---|
81 | 215 | if (event->response & FAN_AUDIT) |
---|
82 | 216 | audit_fanotify(event->response & ~FAN_AUDIT); |
---|
83 | 217 | |
---|
84 | | - event->response = 0; |
---|
85 | | - |
---|
86 | 218 | pr_debug("%s: group=%p event=%p about to return ret=%d\n", __func__, |
---|
87 | 219 | group, event, ret); |
---|
88 | | - |
---|
| 220 | +out: |
---|
| 221 | + fsnotify_destroy_event(group, &event->fae.fse); |
---|
| 222 | + |
---|
89 | 223 | return ret; |
---|
90 | 224 | } |
---|
91 | 225 | |
---|
92 | | -static bool fanotify_should_send_event(struct fsnotify_iter_info *iter_info, |
---|
93 | | - u32 event_mask, const void *data, |
---|
94 | | - int data_type) |
---|
| 226 | +/* |
---|
| 227 | + * This function returns a mask for an event that only contains the flags |
---|
| 228 | + * that have been specifically requested by the user. Flags that may have |
---|
| 229 | + * been included within the event mask, but have not been explicitly |
---|
| 230 | + * requested by the user, will not be present in the returned mask. |
---|
| 231 | + */ |
---|
| 232 | +static u32 fanotify_group_event_mask(struct fsnotify_group *group, |
---|
| 233 | + struct fsnotify_iter_info *iter_info, |
---|
| 234 | + u32 event_mask, const void *data, |
---|
| 235 | + int data_type, struct inode *dir) |
---|
95 | 236 | { |
---|
96 | 237 | __u32 marks_mask = 0, marks_ignored_mask = 0; |
---|
97 | | - const struct path *path = data; |
---|
| 238 | + __u32 test_mask, user_mask = FANOTIFY_OUTGOING_EVENTS | |
---|
| 239 | + FANOTIFY_EVENT_FLAGS; |
---|
| 240 | + const struct path *path = fsnotify_data_path(data, data_type); |
---|
| 241 | + unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS); |
---|
98 | 242 | struct fsnotify_mark *mark; |
---|
99 | 243 | int type; |
---|
100 | 244 | |
---|
101 | 245 | pr_debug("%s: report_mask=%x mask=%x data=%p data_type=%d\n", |
---|
102 | 246 | __func__, iter_info->report_mask, event_mask, data, data_type); |
---|
103 | 247 | |
---|
104 | | - /* if we don't have enough info to send an event to userspace say no */ |
---|
105 | | - if (data_type != FSNOTIFY_EVENT_PATH) |
---|
106 | | - return false; |
---|
107 | | - |
---|
108 | | - /* sorry, fanotify only gives a damn about files and dirs */ |
---|
109 | | - if (!d_is_reg(path->dentry) && |
---|
110 | | - !d_can_lookup(path->dentry)) |
---|
111 | | - return false; |
---|
| 248 | + if (!fid_mode) { |
---|
| 249 | + /* Do we have path to open a file descriptor? */ |
---|
| 250 | + if (!path) |
---|
| 251 | + return 0; |
---|
| 252 | + /* Path type events are only relevant for files and dirs */ |
---|
| 253 | + if (!d_is_reg(path->dentry) && !d_can_lookup(path->dentry)) |
---|
| 254 | + return 0; |
---|
| 255 | + } else if (!(fid_mode & FAN_REPORT_FID)) { |
---|
| 256 | + /* Do we have a directory inode to report? */ |
---|
| 257 | + if (!dir && !(event_mask & FS_ISDIR)) |
---|
| 258 | + return 0; |
---|
| 259 | + } |
---|
112 | 260 | |
---|
113 | 261 | fsnotify_foreach_obj_type(type) { |
---|
114 | 262 | if (!fsnotify_iter_should_report_type(iter_info, type)) |
---|
.. | .. |
---|
119 | 267 | marks_ignored_mask |= mark->ignored_mask; |
---|
120 | 268 | |
---|
121 | 269 | /* |
---|
122 | | - * If the event is for a child and this mark doesn't care about |
---|
123 | | - * events on a child, don't send it! |
---|
| 270 | + * If the event is on dir and this mark doesn't care about |
---|
| 271 | + * events on dir, don't send it! |
---|
124 | 272 | */ |
---|
125 | | - if (event_mask & FS_EVENT_ON_CHILD && |
---|
126 | | - (type != FSNOTIFY_OBJ_TYPE_INODE || |
---|
127 | | - !(mark->mask & FS_EVENT_ON_CHILD))) |
---|
| 273 | + if (event_mask & FS_ISDIR && !(mark->mask & FS_ISDIR)) |
---|
| 274 | + continue; |
---|
| 275 | + |
---|
| 276 | + /* |
---|
| 277 | + * If the event is on a child and this mark is on a parent not |
---|
| 278 | + * watching children, don't send it! |
---|
| 279 | + */ |
---|
| 280 | + if (type == FSNOTIFY_OBJ_TYPE_PARENT && |
---|
| 281 | + !(mark->mask & FS_EVENT_ON_CHILD)) |
---|
128 | 282 | continue; |
---|
129 | 283 | |
---|
130 | 284 | marks_mask |= mark->mask; |
---|
131 | 285 | } |
---|
132 | 286 | |
---|
133 | | - if (d_is_dir(path->dentry) && |
---|
134 | | - !(marks_mask & FS_ISDIR & ~marks_ignored_mask)) |
---|
135 | | - return false; |
---|
| 287 | + test_mask = event_mask & marks_mask & ~marks_ignored_mask; |
---|
136 | 288 | |
---|
137 | | - if (event_mask & FAN_ALL_OUTGOING_EVENTS & marks_mask & |
---|
138 | | - ~marks_ignored_mask) |
---|
139 | | - return true; |
---|
| 289 | + /* |
---|
| 290 | + * For dirent modification events (create/delete/move) that do not carry |
---|
| 291 | + * the child entry name information, we report FAN_ONDIR for mkdir/rmdir |
---|
| 292 | + * so user can differentiate them from creat/unlink. |
---|
| 293 | + * |
---|
| 294 | + * For backward compatibility and consistency, do not report FAN_ONDIR |
---|
| 295 | + * to user in legacy fanotify mode (reporting fd) and report FAN_ONDIR |
---|
| 296 | + * to user in fid mode for all event types. |
---|
| 297 | + * |
---|
| 298 | + * We never report FAN_EVENT_ON_CHILD to user, but we do pass it in to |
---|
| 299 | + * fanotify_alloc_event() when group is reporting fid as indication |
---|
| 300 | + * that event happened on child. |
---|
| 301 | + */ |
---|
| 302 | + if (fid_mode) { |
---|
| 303 | + /* Do not report event flags without any event */ |
---|
| 304 | + if (!(test_mask & ~FANOTIFY_EVENT_FLAGS)) |
---|
| 305 | + return 0; |
---|
| 306 | + } else { |
---|
| 307 | + user_mask &= ~FANOTIFY_EVENT_FLAGS; |
---|
| 308 | + } |
---|
140 | 309 | |
---|
141 | | - return false; |
---|
| 310 | + return test_mask & user_mask; |
---|
142 | 311 | } |
---|
143 | 312 | |
---|
144 | | -struct fanotify_event_info *fanotify_alloc_event(struct fsnotify_group *group, |
---|
145 | | - struct inode *inode, u32 mask, |
---|
146 | | - const struct path *path) |
---|
| 313 | +/* |
---|
| 314 | + * Check size needed to encode fanotify_fh. |
---|
| 315 | + * |
---|
| 316 | + * Return size of encoded fh without fanotify_fh header. |
---|
| 317 | + * Return 0 on failure to encode. |
---|
| 318 | + */ |
---|
| 319 | +static int fanotify_encode_fh_len(struct inode *inode) |
---|
147 | 320 | { |
---|
148 | | - struct fanotify_event_info *event = NULL; |
---|
| 321 | + int dwords = 0; |
---|
| 322 | + |
---|
| 323 | + if (!inode) |
---|
| 324 | + return 0; |
---|
| 325 | + |
---|
| 326 | + exportfs_encode_inode_fh(inode, NULL, &dwords, NULL); |
---|
| 327 | + |
---|
| 328 | + return dwords << 2; |
---|
| 329 | +} |
---|
| 330 | + |
---|
| 331 | +/* |
---|
| 332 | + * Encode fanotify_fh. |
---|
| 333 | + * |
---|
| 334 | + * Return total size of encoded fh including fanotify_fh header. |
---|
| 335 | + * Return 0 on failure to encode. |
---|
| 336 | + */ |
---|
| 337 | +static int fanotify_encode_fh(struct fanotify_fh *fh, struct inode *inode, |
---|
| 338 | + unsigned int fh_len, gfp_t gfp) |
---|
| 339 | +{ |
---|
| 340 | + int dwords, type = 0; |
---|
| 341 | + char *ext_buf = NULL; |
---|
| 342 | + void *buf = fh->buf; |
---|
| 343 | + int err; |
---|
| 344 | + |
---|
| 345 | + fh->type = FILEID_ROOT; |
---|
| 346 | + fh->len = 0; |
---|
| 347 | + fh->flags = 0; |
---|
| 348 | + if (!inode) |
---|
| 349 | + return 0; |
---|
| 350 | + |
---|
| 351 | + /* |
---|
| 352 | + * !gpf means preallocated variable size fh, but fh_len could |
---|
| 353 | + * be zero in that case if encoding fh len failed. |
---|
| 354 | + */ |
---|
| 355 | + err = -ENOENT; |
---|
| 356 | + if (fh_len < 4 || WARN_ON_ONCE(fh_len % 4)) |
---|
| 357 | + goto out_err; |
---|
| 358 | + |
---|
| 359 | + /* No external buffer in a variable size allocated fh */ |
---|
| 360 | + if (gfp && fh_len > FANOTIFY_INLINE_FH_LEN) { |
---|
| 361 | + /* Treat failure to allocate fh as failure to encode fh */ |
---|
| 362 | + err = -ENOMEM; |
---|
| 363 | + ext_buf = kmalloc(fh_len, gfp); |
---|
| 364 | + if (!ext_buf) |
---|
| 365 | + goto out_err; |
---|
| 366 | + |
---|
| 367 | + *fanotify_fh_ext_buf_ptr(fh) = ext_buf; |
---|
| 368 | + buf = ext_buf; |
---|
| 369 | + fh->flags |= FANOTIFY_FH_FLAG_EXT_BUF; |
---|
| 370 | + } |
---|
| 371 | + |
---|
| 372 | + dwords = fh_len >> 2; |
---|
| 373 | + type = exportfs_encode_inode_fh(inode, buf, &dwords, NULL); |
---|
| 374 | + err = -EINVAL; |
---|
| 375 | + if (!type || type == FILEID_INVALID || fh_len != dwords << 2) |
---|
| 376 | + goto out_err; |
---|
| 377 | + |
---|
| 378 | + fh->type = type; |
---|
| 379 | + fh->len = fh_len; |
---|
| 380 | + |
---|
| 381 | + return FANOTIFY_FH_HDR_LEN + fh_len; |
---|
| 382 | + |
---|
| 383 | +out_err: |
---|
| 384 | + pr_warn_ratelimited("fanotify: failed to encode fid (type=%d, len=%d, err=%i)\n", |
---|
| 385 | + type, fh_len, err); |
---|
| 386 | + kfree(ext_buf); |
---|
| 387 | + *fanotify_fh_ext_buf_ptr(fh) = NULL; |
---|
| 388 | + /* Report the event without a file identifier on encode error */ |
---|
| 389 | + fh->type = FILEID_INVALID; |
---|
| 390 | + fh->len = 0; |
---|
| 391 | + return 0; |
---|
| 392 | +} |
---|
| 393 | + |
---|
| 394 | +/* |
---|
| 395 | + * The inode to use as identifier when reporting fid depends on the event. |
---|
| 396 | + * Report the modified directory inode on dirent modification events. |
---|
| 397 | + * Report the "victim" inode otherwise. |
---|
| 398 | + * For example: |
---|
| 399 | + * FS_ATTRIB reports the child inode even if reported on a watched parent. |
---|
| 400 | + * FS_CREATE reports the modified dir inode and not the created inode. |
---|
| 401 | + */ |
---|
| 402 | +static struct inode *fanotify_fid_inode(u32 event_mask, const void *data, |
---|
| 403 | + int data_type, struct inode *dir) |
---|
| 404 | +{ |
---|
| 405 | + if (event_mask & ALL_FSNOTIFY_DIRENT_EVENTS) |
---|
| 406 | + return dir; |
---|
| 407 | + |
---|
| 408 | + return fsnotify_data_inode(data, data_type); |
---|
| 409 | +} |
---|
| 410 | + |
---|
| 411 | +/* |
---|
| 412 | + * The inode to use as identifier when reporting dir fid depends on the event. |
---|
| 413 | + * Report the modified directory inode on dirent modification events. |
---|
| 414 | + * Report the "victim" inode if "victim" is a directory. |
---|
| 415 | + * Report the parent inode if "victim" is not a directory and event is |
---|
| 416 | + * reported to parent. |
---|
| 417 | + * Otherwise, do not report dir fid. |
---|
| 418 | + */ |
---|
| 419 | +static struct inode *fanotify_dfid_inode(u32 event_mask, const void *data, |
---|
| 420 | + int data_type, struct inode *dir) |
---|
| 421 | +{ |
---|
| 422 | + struct inode *inode = fsnotify_data_inode(data, data_type); |
---|
| 423 | + |
---|
| 424 | + if (event_mask & ALL_FSNOTIFY_DIRENT_EVENTS) |
---|
| 425 | + return dir; |
---|
| 426 | + |
---|
| 427 | + if (S_ISDIR(inode->i_mode)) |
---|
| 428 | + return inode; |
---|
| 429 | + |
---|
| 430 | + return dir; |
---|
| 431 | +} |
---|
| 432 | + |
---|
| 433 | +static struct fanotify_event *fanotify_alloc_path_event(const struct path *path, |
---|
| 434 | + gfp_t gfp) |
---|
| 435 | +{ |
---|
| 436 | + struct fanotify_path_event *pevent; |
---|
| 437 | + |
---|
| 438 | + pevent = kmem_cache_alloc(fanotify_path_event_cachep, gfp); |
---|
| 439 | + if (!pevent) |
---|
| 440 | + return NULL; |
---|
| 441 | + |
---|
| 442 | + pevent->fae.type = FANOTIFY_EVENT_TYPE_PATH; |
---|
| 443 | + pevent->path = *path; |
---|
| 444 | + path_get(path); |
---|
| 445 | + |
---|
| 446 | + return &pevent->fae; |
---|
| 447 | +} |
---|
| 448 | + |
---|
| 449 | +static struct fanotify_event *fanotify_alloc_perm_event(const struct path *path, |
---|
| 450 | + gfp_t gfp) |
---|
| 451 | +{ |
---|
| 452 | + struct fanotify_perm_event *pevent; |
---|
| 453 | + |
---|
| 454 | + pevent = kmem_cache_alloc(fanotify_perm_event_cachep, gfp); |
---|
| 455 | + if (!pevent) |
---|
| 456 | + return NULL; |
---|
| 457 | + |
---|
| 458 | + pevent->fae.type = FANOTIFY_EVENT_TYPE_PATH_PERM; |
---|
| 459 | + pevent->response = 0; |
---|
| 460 | + pevent->state = FAN_EVENT_INIT; |
---|
| 461 | + pevent->path = *path; |
---|
| 462 | + path_get(path); |
---|
| 463 | + |
---|
| 464 | + return &pevent->fae; |
---|
| 465 | +} |
---|
| 466 | + |
---|
| 467 | +static struct fanotify_event *fanotify_alloc_fid_event(struct inode *id, |
---|
| 468 | + __kernel_fsid_t *fsid, |
---|
| 469 | + gfp_t gfp) |
---|
| 470 | +{ |
---|
| 471 | + struct fanotify_fid_event *ffe; |
---|
| 472 | + |
---|
| 473 | + ffe = kmem_cache_alloc(fanotify_fid_event_cachep, gfp); |
---|
| 474 | + if (!ffe) |
---|
| 475 | + return NULL; |
---|
| 476 | + |
---|
| 477 | + ffe->fae.type = FANOTIFY_EVENT_TYPE_FID; |
---|
| 478 | + ffe->fsid = *fsid; |
---|
| 479 | + fanotify_encode_fh(&ffe->object_fh, id, fanotify_encode_fh_len(id), |
---|
| 480 | + gfp); |
---|
| 481 | + |
---|
| 482 | + return &ffe->fae; |
---|
| 483 | +} |
---|
| 484 | + |
---|
| 485 | +static struct fanotify_event *fanotify_alloc_name_event(struct inode *id, |
---|
| 486 | + __kernel_fsid_t *fsid, |
---|
| 487 | + const struct qstr *file_name, |
---|
| 488 | + struct inode *child, |
---|
| 489 | + gfp_t gfp) |
---|
| 490 | +{ |
---|
| 491 | + struct fanotify_name_event *fne; |
---|
| 492 | + struct fanotify_info *info; |
---|
| 493 | + struct fanotify_fh *dfh, *ffh; |
---|
| 494 | + unsigned int dir_fh_len = fanotify_encode_fh_len(id); |
---|
| 495 | + unsigned int child_fh_len = fanotify_encode_fh_len(child); |
---|
| 496 | + unsigned int size; |
---|
| 497 | + |
---|
| 498 | + size = sizeof(*fne) + FANOTIFY_FH_HDR_LEN + dir_fh_len; |
---|
| 499 | + if (child_fh_len) |
---|
| 500 | + size += FANOTIFY_FH_HDR_LEN + child_fh_len; |
---|
| 501 | + if (file_name) |
---|
| 502 | + size += file_name->len + 1; |
---|
| 503 | + fne = kmalloc(size, gfp); |
---|
| 504 | + if (!fne) |
---|
| 505 | + return NULL; |
---|
| 506 | + |
---|
| 507 | + fne->fae.type = FANOTIFY_EVENT_TYPE_FID_NAME; |
---|
| 508 | + fne->fsid = *fsid; |
---|
| 509 | + info = &fne->info; |
---|
| 510 | + fanotify_info_init(info); |
---|
| 511 | + dfh = fanotify_info_dir_fh(info); |
---|
| 512 | + info->dir_fh_totlen = fanotify_encode_fh(dfh, id, dir_fh_len, 0); |
---|
| 513 | + if (child_fh_len) { |
---|
| 514 | + ffh = fanotify_info_file_fh(info); |
---|
| 515 | + info->file_fh_totlen = fanotify_encode_fh(ffh, child, child_fh_len, 0); |
---|
| 516 | + } |
---|
| 517 | + if (file_name) |
---|
| 518 | + fanotify_info_copy_name(info, file_name); |
---|
| 519 | + |
---|
| 520 | + pr_debug("%s: ino=%lu size=%u dir_fh_len=%u child_fh_len=%u name_len=%u name='%.*s'\n", |
---|
| 521 | + __func__, id->i_ino, size, dir_fh_len, child_fh_len, |
---|
| 522 | + info->name_len, info->name_len, fanotify_info_name(info)); |
---|
| 523 | + |
---|
| 524 | + return &fne->fae; |
---|
| 525 | +} |
---|
| 526 | + |
---|
| 527 | +static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group, |
---|
| 528 | + u32 mask, const void *data, |
---|
| 529 | + int data_type, struct inode *dir, |
---|
| 530 | + const struct qstr *file_name, |
---|
| 531 | + __kernel_fsid_t *fsid) |
---|
| 532 | +{ |
---|
| 533 | + struct fanotify_event *event = NULL; |
---|
149 | 534 | gfp_t gfp = GFP_KERNEL_ACCOUNT; |
---|
| 535 | + struct inode *id = fanotify_fid_inode(mask, data, data_type, dir); |
---|
| 536 | + struct inode *dirid = fanotify_dfid_inode(mask, data, data_type, dir); |
---|
| 537 | + const struct path *path = fsnotify_data_path(data, data_type); |
---|
| 538 | + unsigned int fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS); |
---|
| 539 | + struct mem_cgroup *old_memcg; |
---|
| 540 | + struct inode *child = NULL; |
---|
| 541 | + bool name_event = false; |
---|
| 542 | + |
---|
| 543 | + if ((fid_mode & FAN_REPORT_DIR_FID) && dirid) { |
---|
| 544 | + /* |
---|
| 545 | + * With both flags FAN_REPORT_DIR_FID and FAN_REPORT_FID, we |
---|
| 546 | + * report the child fid for events reported on a non-dir child |
---|
| 547 | + * in addition to reporting the parent fid and maybe child name. |
---|
| 548 | + */ |
---|
| 549 | + if ((fid_mode & FAN_REPORT_FID) && |
---|
| 550 | + id != dirid && !(mask & FAN_ONDIR)) |
---|
| 551 | + child = id; |
---|
| 552 | + |
---|
| 553 | + id = dirid; |
---|
| 554 | + |
---|
| 555 | + /* |
---|
| 556 | + * We record file name only in a group with FAN_REPORT_NAME |
---|
| 557 | + * and when we have a directory inode to report. |
---|
| 558 | + * |
---|
| 559 | + * For directory entry modification event, we record the fid of |
---|
| 560 | + * the directory and the name of the modified entry. |
---|
| 561 | + * |
---|
| 562 | + * For event on non-directory that is reported to parent, we |
---|
| 563 | + * record the fid of the parent and the name of the child. |
---|
| 564 | + * |
---|
| 565 | + * Even if not reporting name, we need a variable length |
---|
| 566 | + * fanotify_name_event if reporting both parent and child fids. |
---|
| 567 | + */ |
---|
| 568 | + if (!(fid_mode & FAN_REPORT_NAME)) { |
---|
| 569 | + name_event = !!child; |
---|
| 570 | + file_name = NULL; |
---|
| 571 | + } else if ((mask & ALL_FSNOTIFY_DIRENT_EVENTS) || |
---|
| 572 | + !(mask & FAN_ONDIR)) { |
---|
| 573 | + name_event = true; |
---|
| 574 | + } |
---|
| 575 | + } |
---|
150 | 576 | |
---|
151 | 577 | /* |
---|
152 | 578 | * For queues with unlimited length lost events are not expected and |
---|
.. | .. |
---|
160 | 586 | gfp |= __GFP_RETRY_MAYFAIL; |
---|
161 | 587 | |
---|
162 | 588 | /* Whoever is interested in the event, pays for the allocation. */ |
---|
163 | | - memalloc_use_memcg(group->memcg); |
---|
| 589 | + old_memcg = set_active_memcg(group->memcg); |
---|
164 | 590 | |
---|
165 | 591 | if (fanotify_is_perm_event(mask)) { |
---|
166 | | - struct fanotify_perm_event_info *pevent; |
---|
167 | | - |
---|
168 | | - pevent = kmem_cache_alloc(fanotify_perm_event_cachep, gfp); |
---|
169 | | - if (!pevent) |
---|
170 | | - goto out; |
---|
171 | | - event = &pevent->fae; |
---|
172 | | - pevent->response = 0; |
---|
173 | | - goto init; |
---|
| 592 | + event = fanotify_alloc_perm_event(path, gfp); |
---|
| 593 | + } else if (name_event && (file_name || child)) { |
---|
| 594 | + event = fanotify_alloc_name_event(id, fsid, file_name, child, |
---|
| 595 | + gfp); |
---|
| 596 | + } else if (fid_mode) { |
---|
| 597 | + event = fanotify_alloc_fid_event(id, fsid, gfp); |
---|
| 598 | + } else { |
---|
| 599 | + event = fanotify_alloc_path_event(path, gfp); |
---|
174 | 600 | } |
---|
175 | | - event = kmem_cache_alloc(fanotify_event_cachep, gfp); |
---|
| 601 | + |
---|
176 | 602 | if (!event) |
---|
177 | 603 | goto out; |
---|
178 | | -init: __maybe_unused |
---|
179 | | - fsnotify_init_event(&event->fse, inode, mask); |
---|
180 | | - event->tgid = get_pid(task_tgid(current)); |
---|
181 | | - if (path) { |
---|
182 | | - event->path = *path; |
---|
183 | | - path_get(&event->path); |
---|
184 | | - } else { |
---|
185 | | - event->path.mnt = NULL; |
---|
186 | | - event->path.dentry = NULL; |
---|
187 | | - } |
---|
| 604 | + |
---|
| 605 | + /* |
---|
| 606 | + * Use the victim inode instead of the watching inode as the id for |
---|
| 607 | + * event queue, so event reported on parent is merged with event |
---|
| 608 | + * reported on child when both directory and child watches exist. |
---|
| 609 | + */ |
---|
| 610 | + fanotify_init_event(event, (unsigned long)id, mask); |
---|
| 611 | + if (FAN_GROUP_FLAG(group, FAN_REPORT_TID)) |
---|
| 612 | + event->pid = get_pid(task_pid(current)); |
---|
| 613 | + else |
---|
| 614 | + event->pid = get_pid(task_tgid(current)); |
---|
| 615 | + |
---|
188 | 616 | out: |
---|
189 | | - memalloc_unuse_memcg(); |
---|
| 617 | + set_active_memcg(old_memcg); |
---|
190 | 618 | return event; |
---|
191 | 619 | } |
---|
192 | 620 | |
---|
193 | | -static int fanotify_handle_event(struct fsnotify_group *group, |
---|
194 | | - struct inode *inode, |
---|
195 | | - u32 mask, const void *data, int data_type, |
---|
196 | | - const unsigned char *file_name, u32 cookie, |
---|
| 621 | +/* |
---|
| 622 | + * Get cached fsid of the filesystem containing the object from any connector. |
---|
| 623 | + * All connectors are supposed to have the same fsid, but we do not verify that |
---|
| 624 | + * here. |
---|
| 625 | + */ |
---|
| 626 | +static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info) |
---|
| 627 | +{ |
---|
| 628 | + int type; |
---|
| 629 | + __kernel_fsid_t fsid = {}; |
---|
| 630 | + |
---|
| 631 | + fsnotify_foreach_obj_type(type) { |
---|
| 632 | + struct fsnotify_mark_connector *conn; |
---|
| 633 | + |
---|
| 634 | + if (!fsnotify_iter_should_report_type(iter_info, type)) |
---|
| 635 | + continue; |
---|
| 636 | + |
---|
| 637 | + conn = READ_ONCE(iter_info->marks[type]->connector); |
---|
| 638 | + /* Mark is just getting destroyed or created? */ |
---|
| 639 | + if (!conn) |
---|
| 640 | + continue; |
---|
| 641 | + if (!(conn->flags & FSNOTIFY_CONN_FLAG_HAS_FSID)) |
---|
| 642 | + continue; |
---|
| 643 | + /* Pairs with smp_wmb() in fsnotify_add_mark_list() */ |
---|
| 644 | + smp_rmb(); |
---|
| 645 | + fsid = conn->fsid; |
---|
| 646 | + if (WARN_ON_ONCE(!fsid.val[0] && !fsid.val[1])) |
---|
| 647 | + continue; |
---|
| 648 | + return fsid; |
---|
| 649 | + } |
---|
| 650 | + |
---|
| 651 | + return fsid; |
---|
| 652 | +} |
---|
| 653 | + |
---|
| 654 | +static int fanotify_handle_event(struct fsnotify_group *group, u32 mask, |
---|
| 655 | + const void *data, int data_type, |
---|
| 656 | + struct inode *dir, |
---|
| 657 | + const struct qstr *file_name, u32 cookie, |
---|
197 | 658 | struct fsnotify_iter_info *iter_info) |
---|
198 | 659 | { |
---|
199 | 660 | int ret = 0; |
---|
200 | | - struct fanotify_event_info *event; |
---|
| 661 | + struct fanotify_event *event; |
---|
201 | 662 | struct fsnotify_event *fsn_event; |
---|
| 663 | + __kernel_fsid_t fsid = {}; |
---|
202 | 664 | |
---|
203 | 665 | BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); |
---|
204 | 666 | BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY); |
---|
| 667 | + BUILD_BUG_ON(FAN_ATTRIB != FS_ATTRIB); |
---|
205 | 668 | BUILD_BUG_ON(FAN_CLOSE_NOWRITE != FS_CLOSE_NOWRITE); |
---|
206 | 669 | BUILD_BUG_ON(FAN_CLOSE_WRITE != FS_CLOSE_WRITE); |
---|
207 | 670 | BUILD_BUG_ON(FAN_OPEN != FS_OPEN); |
---|
| 671 | + BUILD_BUG_ON(FAN_MOVED_TO != FS_MOVED_TO); |
---|
| 672 | + BUILD_BUG_ON(FAN_MOVED_FROM != FS_MOVED_FROM); |
---|
| 673 | + BUILD_BUG_ON(FAN_CREATE != FS_CREATE); |
---|
| 674 | + BUILD_BUG_ON(FAN_DELETE != FS_DELETE); |
---|
| 675 | + BUILD_BUG_ON(FAN_DELETE_SELF != FS_DELETE_SELF); |
---|
| 676 | + BUILD_BUG_ON(FAN_MOVE_SELF != FS_MOVE_SELF); |
---|
208 | 677 | BUILD_BUG_ON(FAN_EVENT_ON_CHILD != FS_EVENT_ON_CHILD); |
---|
209 | 678 | BUILD_BUG_ON(FAN_Q_OVERFLOW != FS_Q_OVERFLOW); |
---|
210 | 679 | BUILD_BUG_ON(FAN_OPEN_PERM != FS_OPEN_PERM); |
---|
211 | 680 | BUILD_BUG_ON(FAN_ACCESS_PERM != FS_ACCESS_PERM); |
---|
212 | 681 | BUILD_BUG_ON(FAN_ONDIR != FS_ISDIR); |
---|
| 682 | + BUILD_BUG_ON(FAN_OPEN_EXEC != FS_OPEN_EXEC); |
---|
| 683 | + BUILD_BUG_ON(FAN_OPEN_EXEC_PERM != FS_OPEN_EXEC_PERM); |
---|
213 | 684 | |
---|
214 | | - if (!fanotify_should_send_event(iter_info, mask, data, data_type)) |
---|
| 685 | + BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 19); |
---|
| 686 | + |
---|
| 687 | + mask = fanotify_group_event_mask(group, iter_info, mask, data, |
---|
| 688 | + data_type, dir); |
---|
| 689 | + if (!mask) |
---|
215 | 690 | return 0; |
---|
216 | 691 | |
---|
217 | | - pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode, |
---|
218 | | - mask); |
---|
| 692 | + pr_debug("%s: group=%p mask=%x\n", __func__, group, mask); |
---|
219 | 693 | |
---|
220 | 694 | if (fanotify_is_perm_event(mask)) { |
---|
221 | 695 | /* |
---|
.. | .. |
---|
226 | 700 | return 0; |
---|
227 | 701 | } |
---|
228 | 702 | |
---|
229 | | - event = fanotify_alloc_event(group, inode, mask, data); |
---|
| 703 | + if (FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS)) { |
---|
| 704 | + fsid = fanotify_get_fsid(iter_info); |
---|
| 705 | + /* Racing with mark destruction or creation? */ |
---|
| 706 | + if (!fsid.val[0] && !fsid.val[1]) |
---|
| 707 | + return 0; |
---|
| 708 | + } |
---|
| 709 | + |
---|
| 710 | + event = fanotify_alloc_event(group, mask, data, data_type, dir, |
---|
| 711 | + file_name, &fsid); |
---|
230 | 712 | ret = -ENOMEM; |
---|
231 | 713 | if (unlikely(!event)) { |
---|
232 | 714 | /* |
---|
.. | .. |
---|
242 | 724 | ret = fsnotify_add_event(group, fsn_event, fanotify_merge); |
---|
243 | 725 | if (ret) { |
---|
244 | 726 | /* Permission events shouldn't be merged */ |
---|
245 | | - BUG_ON(ret == 1 && mask & FAN_ALL_PERM_EVENTS); |
---|
| 727 | + BUG_ON(ret == 1 && mask & FANOTIFY_PERM_EVENTS); |
---|
246 | 728 | /* Our event wasn't used in the end. Free it. */ |
---|
247 | 729 | fsnotify_destroy_event(group, fsn_event); |
---|
248 | 730 | |
---|
249 | 731 | ret = 0; |
---|
250 | 732 | } else if (fanotify_is_perm_event(mask)) { |
---|
251 | | - ret = fanotify_get_response(group, FANOTIFY_PE(fsn_event), |
---|
| 733 | + ret = fanotify_get_response(group, FANOTIFY_PERM(event), |
---|
252 | 734 | iter_info); |
---|
253 | | - fsnotify_destroy_event(group, fsn_event); |
---|
254 | 735 | } |
---|
255 | 736 | finish: |
---|
256 | 737 | if (fanotify_is_perm_event(mask)) |
---|
.. | .. |
---|
268 | 749 | free_uid(user); |
---|
269 | 750 | } |
---|
270 | 751 | |
---|
| 752 | +static void fanotify_free_path_event(struct fanotify_event *event) |
---|
| 753 | +{ |
---|
| 754 | + path_put(fanotify_event_path(event)); |
---|
| 755 | + kmem_cache_free(fanotify_path_event_cachep, FANOTIFY_PE(event)); |
---|
| 756 | +} |
---|
| 757 | + |
---|
| 758 | +static void fanotify_free_perm_event(struct fanotify_event *event) |
---|
| 759 | +{ |
---|
| 760 | + path_put(fanotify_event_path(event)); |
---|
| 761 | + kmem_cache_free(fanotify_perm_event_cachep, FANOTIFY_PERM(event)); |
---|
| 762 | +} |
---|
| 763 | + |
---|
| 764 | +static void fanotify_free_fid_event(struct fanotify_event *event) |
---|
| 765 | +{ |
---|
| 766 | + struct fanotify_fid_event *ffe = FANOTIFY_FE(event); |
---|
| 767 | + |
---|
| 768 | + if (fanotify_fh_has_ext_buf(&ffe->object_fh)) |
---|
| 769 | + kfree(fanotify_fh_ext_buf(&ffe->object_fh)); |
---|
| 770 | + kmem_cache_free(fanotify_fid_event_cachep, ffe); |
---|
| 771 | +} |
---|
| 772 | + |
---|
| 773 | +static void fanotify_free_name_event(struct fanotify_event *event) |
---|
| 774 | +{ |
---|
| 775 | + kfree(FANOTIFY_NE(event)); |
---|
| 776 | +} |
---|
| 777 | + |
---|
271 | 778 | static void fanotify_free_event(struct fsnotify_event *fsn_event) |
---|
272 | 779 | { |
---|
273 | | - struct fanotify_event_info *event; |
---|
| 780 | + struct fanotify_event *event; |
---|
274 | 781 | |
---|
275 | 782 | event = FANOTIFY_E(fsn_event); |
---|
276 | | - path_put(&event->path); |
---|
277 | | - put_pid(event->tgid); |
---|
278 | | - if (fanotify_is_perm_event(fsn_event->mask)) { |
---|
279 | | - kmem_cache_free(fanotify_perm_event_cachep, |
---|
280 | | - FANOTIFY_PE(fsn_event)); |
---|
281 | | - return; |
---|
| 783 | + put_pid(event->pid); |
---|
| 784 | + switch (event->type) { |
---|
| 785 | + case FANOTIFY_EVENT_TYPE_PATH: |
---|
| 786 | + fanotify_free_path_event(event); |
---|
| 787 | + break; |
---|
| 788 | + case FANOTIFY_EVENT_TYPE_PATH_PERM: |
---|
| 789 | + fanotify_free_perm_event(event); |
---|
| 790 | + break; |
---|
| 791 | + case FANOTIFY_EVENT_TYPE_FID: |
---|
| 792 | + fanotify_free_fid_event(event); |
---|
| 793 | + break; |
---|
| 794 | + case FANOTIFY_EVENT_TYPE_FID_NAME: |
---|
| 795 | + fanotify_free_name_event(event); |
---|
| 796 | + break; |
---|
| 797 | + case FANOTIFY_EVENT_TYPE_OVERFLOW: |
---|
| 798 | + kfree(event); |
---|
| 799 | + break; |
---|
| 800 | + default: |
---|
| 801 | + WARN_ON_ONCE(1); |
---|
282 | 802 | } |
---|
283 | | - kmem_cache_free(fanotify_event_cachep, event); |
---|
284 | 803 | } |
---|
285 | 804 | |
---|
286 | 805 | static void fanotify_free_mark(struct fsnotify_mark *fsn_mark) |
---|