Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

fanotify: record name info for FAN_DIR_MODIFY event

For FAN_DIR_MODIFY event, allocate a variable size event struct to store
the dir entry name along side the directory file handle.

At this point, name info reporting is not yet implemented, so trying to
set FAN_DIR_MODIFY in mark mask will return -EINVAL.

Link: https://lore.kernel.org/r/20200319151022.31456-14-amir73il@gmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>

authored by

Amir Goldstein and committed by
Jan Kara
cacfb956 01affd54

+108 -12
+67 -9
fs/notify/fanotify/fanotify.c
··· 53 53 fanotify_fh_equal(&ffe1->object_fh, &ffe2->object_fh); 54 54 } 55 55 56 + static bool fanotify_name_event_equal(struct fanotify_name_event *fne1, 57 + struct fanotify_name_event *fne2) 58 + { 59 + /* 60 + * Do not merge name events without dir fh. 61 + * FAN_DIR_MODIFY does not encode object fh, so it may be empty. 62 + */ 63 + if (!fne1->dir_fh.len) 64 + return false; 65 + 66 + if (fne1->name_len != fne2->name_len || 67 + !fanotify_fh_equal(&fne1->dir_fh, &fne2->dir_fh)) 68 + return false; 69 + 70 + return !memcmp(fne1->name, fne2->name, fne1->name_len); 71 + } 72 + 56 73 static bool should_merge(struct fsnotify_event *old_fsn, 57 74 struct fsnotify_event *new_fsn) 58 75 { ··· 101 84 102 85 return fanotify_fid_event_equal(FANOTIFY_FE(old), 103 86 FANOTIFY_FE(new)); 87 + case FANOTIFY_EVENT_TYPE_FID_NAME: 88 + return fanotify_name_event_equal(FANOTIFY_NE(old), 89 + FANOTIFY_NE(new)); 104 90 default: 105 91 WARN_ON_ONCE(1); 106 92 } ··· 282 262 void *buf = fh->buf; 283 263 int err; 284 264 265 + if (!inode) 266 + goto out; 267 + 285 268 dwords = 0; 286 269 err = -ENOENT; 287 270 type = exportfs_encode_inode_fh(inode, NULL, &dwords, NULL); ··· 318 295 type, bytes, err); 319 296 kfree(ext_buf); 320 297 *fanotify_fh_ext_buf_ptr(fh) = NULL; 298 + out: 321 299 /* Report the event without a file identifier on encode error */ 322 300 fh->type = FILEID_INVALID; 323 301 fh->len = 0; ··· 344 320 struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group, 345 321 struct inode *inode, u32 mask, 346 322 const void *data, int data_type, 323 + const struct qstr *file_name, 347 324 __kernel_fsid_t *fsid) 348 325 { 349 326 struct fanotify_event *event = NULL; 350 327 struct fanotify_fid_event *ffe = NULL; 328 + struct fanotify_name_event *fne = NULL; 351 329 gfp_t gfp = GFP_KERNEL_ACCOUNT; 352 330 struct inode *id = fanotify_fid_inode(inode, mask, data, data_type); 353 331 const struct path *path = fsnotify_data_path(data, data_type); ··· 382 356 goto init; 383 357 } 384 358 359 + /* 360 + * For FAN_DIR_MODIFY event, we report the fid of the directory and 361 + * the name of the modified entry. 362 + * Allocate an fanotify_name_event struct and copy the name. 363 + */ 364 + if (mask & FAN_DIR_MODIFY && !(WARN_ON_ONCE(!file_name))) { 365 + fne = kmalloc(sizeof(*fne) + file_name->len + 1, gfp); 366 + if (!fne) 367 + goto out; 368 + 369 + event = &fne->fae; 370 + event->type = FANOTIFY_EVENT_TYPE_FID_NAME; 371 + fne->name_len = file_name->len; 372 + strcpy(fne->name, file_name->name); 373 + goto init; 374 + } 375 + 385 376 if (FAN_GROUP_FLAG(group, FAN_REPORT_FID)) { 386 377 ffe = kmem_cache_alloc(fanotify_fid_event_cachep, gfp); 387 378 if (!ffe) ··· 417 374 event->type = FANOTIFY_EVENT_TYPE_PATH; 418 375 } 419 376 420 - init: __maybe_unused 377 + init: 421 378 /* 422 379 * Use the victim inode instead of the watching inode as the id for 423 380 * event queue, so event reported on parent is merged with event ··· 430 387 else 431 388 event->pid = get_pid(task_tgid(current)); 432 389 433 - if (fanotify_event_object_fh(event)) { 434 - ffe->object_fh.len = 0; 435 - if (fsid) 436 - ffe->fsid = *fsid; 437 - if (id) 438 - fanotify_encode_fh(&ffe->object_fh, id, gfp); 439 - } else if (fanotify_event_has_path(event)) { 390 + if (fsid && fanotify_event_fsid(event)) 391 + *fanotify_event_fsid(event) = *fsid; 392 + 393 + if (fanotify_event_object_fh(event)) 394 + fanotify_encode_fh(fanotify_event_object_fh(event), id, gfp); 395 + 396 + if (fanotify_event_dir_fh(event)) 397 + fanotify_encode_fh(fanotify_event_dir_fh(event), id, gfp); 398 + 399 + if (fanotify_event_has_path(event)) { 440 400 struct path *p = fanotify_event_path(event); 441 401 442 402 if (path) { ··· 547 501 } 548 502 549 503 event = fanotify_alloc_event(group, inode, mask, data, data_type, 550 - &fsid); 504 + file_name, &fsid); 551 505 ret = -ENOMEM; 552 506 if (unlikely(!event)) { 553 507 /* ··· 609 563 kmem_cache_free(fanotify_fid_event_cachep, ffe); 610 564 } 611 565 566 + static void fanotify_free_name_event(struct fanotify_event *event) 567 + { 568 + struct fanotify_name_event *fne = FANOTIFY_NE(event); 569 + 570 + if (fanotify_fh_has_ext_buf(&fne->dir_fh)) 571 + kfree(fanotify_fh_ext_buf(&fne->dir_fh)); 572 + kfree(fne); 573 + } 574 + 612 575 static void fanotify_free_event(struct fsnotify_event *fsn_event) 613 576 { 614 577 struct fanotify_event *event; ··· 633 578 break; 634 579 case FANOTIFY_EVENT_TYPE_FID: 635 580 fanotify_free_fid_event(event); 581 + break; 582 + case FANOTIFY_EVENT_TYPE_FID_NAME: 583 + fanotify_free_name_event(event); 636 584 break; 637 585 default: 638 586 WARN_ON_ONCE(1);
+39 -1
fs/notify/fanotify/fanotify.h
··· 59 59 * be freed and which concrete struct it may be cast to. 60 60 */ 61 61 enum fanotify_event_type { 62 - FANOTIFY_EVENT_TYPE_FID, 62 + FANOTIFY_EVENT_TYPE_FID, /* fixed length */ 63 + FANOTIFY_EVENT_TYPE_FID_NAME, /* variable length */ 63 64 FANOTIFY_EVENT_TYPE_PATH, 64 65 FANOTIFY_EVENT_TYPE_PATH_PERM, 65 66 }; ··· 84 83 return container_of(event, struct fanotify_fid_event, fae); 85 84 } 86 85 86 + struct fanotify_name_event { 87 + struct fanotify_event fae; 88 + __kernel_fsid_t fsid; 89 + struct fanotify_fh dir_fh; 90 + u8 name_len; 91 + char name[0]; 92 + }; 93 + 94 + static inline struct fanotify_name_event * 95 + FANOTIFY_NE(struct fanotify_event *event) 96 + { 97 + return container_of(event, struct fanotify_name_event, fae); 98 + } 99 + 87 100 static inline __kernel_fsid_t *fanotify_event_fsid(struct fanotify_event *event) 88 101 { 89 102 if (event->type == FANOTIFY_EVENT_TYPE_FID) 90 103 return &FANOTIFY_FE(event)->fsid; 104 + else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME) 105 + return &FANOTIFY_NE(event)->fsid; 91 106 else 92 107 return NULL; 93 108 } ··· 117 100 return NULL; 118 101 } 119 102 103 + static inline struct fanotify_fh *fanotify_event_dir_fh( 104 + struct fanotify_event *event) 105 + { 106 + if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME) 107 + return &FANOTIFY_NE(event)->dir_fh; 108 + else 109 + return NULL; 110 + } 111 + 120 112 static inline int fanotify_event_object_fh_len(struct fanotify_event *event) 121 113 { 122 114 struct fanotify_fh *fh = fanotify_event_object_fh(event); 123 115 124 116 return fh ? fh->len : 0; 117 + } 118 + 119 + static inline bool fanotify_event_has_name(struct fanotify_event *event) 120 + { 121 + return event->type == FANOTIFY_EVENT_TYPE_FID_NAME; 122 + } 123 + 124 + static inline int fanotify_event_name_len(struct fanotify_event *event) 125 + { 126 + return fanotify_event_has_name(event) ? 127 + FANOTIFY_NE(event)->name_len : 0; 125 128 } 126 129 127 130 struct fanotify_path_event { ··· 206 169 struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group, 207 170 struct inode *inode, u32 mask, 208 171 const void *data, int data_type, 172 + const struct qstr *file_name, 209 173 __kernel_fsid_t *fsid);
+2 -2
fs/notify/fanotify/fanotify_user.c
··· 210 210 struct fanotify_event_info_fid info = { }; 211 211 struct file_handle handle = { }; 212 212 unsigned char bounce[FANOTIFY_INLINE_FH_LEN], *fh_buf; 213 - size_t fh_len = fh->len; 213 + size_t fh_len = fh ? fh->len : 0; 214 214 size_t len = fanotify_fid_info_len(fh_len); 215 215 216 216 if (!len) ··· 828 828 group->memcg = get_mem_cgroup_from_mm(current->mm); 829 829 830 830 oevent = fanotify_alloc_event(group, NULL, FS_Q_OVERFLOW, NULL, 831 - FSNOTIFY_EVENT_NONE, NULL); 831 + FSNOTIFY_EVENT_NONE, NULL, NULL); 832 832 if (unlikely(!oevent)) { 833 833 fd = -ENOMEM; 834 834 goto out_destroy_group;