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

Merge tag 'fsnotify_for_v5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs

Pull fsnotify updates from Jan Kara:
"Support for reporting filesystem errors through fanotify so that
system health monitoring daemons can watch for these and act instead
of scraping system logs"

* tag 'fsnotify_for_v5.16-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs: (34 commits)
samples: remove duplicate include in fs-monitor.c
samples: Fix warning in fsnotify sample
docs: Fix formatting of literal sections in fanotify docs
samples: Make fs-monitor depend on libc and headers
docs: Document the FAN_FS_ERROR event
samples: Add fs error monitoring example
ext4: Send notifications on error
fanotify: Allow users to request FAN_FS_ERROR events
fanotify: Emit generic error info for error event
fanotify: Report fid info for file related file system errors
fanotify: WARN_ON against too large file handles
fanotify: Add helpers to decide whether to report FID/DFID
fanotify: Wrap object_fh inline space in a creator macro
fanotify: Support merging of error events
fanotify: Support enqueueing of error events
fanotify: Pre-allocate pool of error events
fanotify: Reserve UAPI bits for FAN_FS_ERROR
fsnotify: Support FS_ERROR event type
fanotify: Require fid_mode for any non-fd event
fanotify: Encode empty file handle when no inode is provided
...

+690 -99
+78
Documentation/admin-guide/filesystem-monitoring.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + ==================================== 4 + File system Monitoring with fanotify 5 + ==================================== 6 + 7 + File system Error Reporting 8 + =========================== 9 + 10 + Fanotify supports the FAN_FS_ERROR event type for file system-wide error 11 + reporting. It is meant to be used by file system health monitoring 12 + daemons, which listen for these events and take actions (notify 13 + sysadmin, start recovery) when a file system problem is detected. 14 + 15 + By design, a FAN_FS_ERROR notification exposes sufficient information 16 + for a monitoring tool to know a problem in the file system has happened. 17 + It doesn't necessarily provide a user space application with semantics 18 + to verify an IO operation was successfully executed. That is out of 19 + scope for this feature. Instead, it is only meant as a framework for 20 + early file system problem detection and reporting recovery tools. 21 + 22 + When a file system operation fails, it is common for dozens of kernel 23 + errors to cascade after the initial failure, hiding the original failure 24 + log, which is usually the most useful debug data to troubleshoot the 25 + problem. For this reason, FAN_FS_ERROR tries to report only the first 26 + error that occurred for a file system since the last notification, and 27 + it simply counts additional errors. This ensures that the most 28 + important pieces of information are never lost. 29 + 30 + FAN_FS_ERROR requires the fanotify group to be setup with the 31 + FAN_REPORT_FID flag. 32 + 33 + At the time of this writing, the only file system that emits FAN_FS_ERROR 34 + notifications is Ext4. 35 + 36 + A FAN_FS_ERROR Notification has the following format:: 37 + 38 + :: 39 + 40 + [ Notification Metadata (Mandatory) ] 41 + [ Generic Error Record (Mandatory) ] 42 + [ FID record (Mandatory) ] 43 + 44 + The order of records is not guaranteed, and new records might be added 45 + in the future. Therefore, applications must not rely on the order and 46 + must be prepared to skip over unknown records. Please refer to 47 + ``samples/fanotify/fs-monitor.c`` for an example parser. 48 + 49 + Generic error record 50 + -------------------- 51 + 52 + The generic error record provides enough information for a file system 53 + agnostic tool to learn about a problem in the file system, without 54 + providing any additional details about the problem. This record is 55 + identified by ``struct fanotify_event_info_header.info_type`` being set 56 + to FAN_EVENT_INFO_TYPE_ERROR. 57 + 58 + :: 59 + 60 + struct fanotify_event_info_error { 61 + struct fanotify_event_info_header hdr; 62 + __s32 error; 63 + __u32 error_count; 64 + }; 65 + 66 + The `error` field identifies the type of error using errno values. 67 + `error_count` tracks the number of errors that occurred and were 68 + suppressed to preserve the original error information, since the last 69 + notification. 70 + 71 + FID record 72 + ---------- 73 + 74 + The FID record can be used to uniquely identify the inode that triggered 75 + the error through the combination of fsid and file handle. A file system 76 + specific application can use that information to attempt a recovery 77 + procedure. Errors that are not related to an inode are reported with an 78 + empty file handle of type FILEID_INVALID.
+1
Documentation/admin-guide/index.rst
··· 82 82 edid 83 83 efi-stub 84 84 ext4 85 + filesystem-monitoring 85 86 nfs/index 86 87 gpio/index 87 88 highuid
+8
fs/ext4/super.c
··· 46 46 #include <linux/part_stat.h> 47 47 #include <linux/kthread.h> 48 48 #include <linux/freezer.h> 49 + #include <linux/fsnotify.h> 49 50 50 51 #include "ext4.h" 51 52 #include "ext4_extents.h" /* Needed for trace points definition */ ··· 760 759 sb->s_id, function, line, current->comm, &vaf); 761 760 va_end(args); 762 761 } 762 + fsnotify_sb_error(sb, NULL, error ? error : EFSCORRUPTED); 763 + 763 764 ext4_handle_error(sb, force_ro, error, 0, block, function, line); 764 765 } 765 766 ··· 792 789 current->comm, &vaf); 793 790 va_end(args); 794 791 } 792 + fsnotify_sb_error(inode->i_sb, inode, error ? error : EFSCORRUPTED); 793 + 795 794 ext4_handle_error(inode->i_sb, false, error, inode->i_ino, block, 796 795 function, line); 797 796 } ··· 832 827 current->comm, path, &vaf); 833 828 va_end(args); 834 829 } 830 + fsnotify_sb_error(inode->i_sb, inode, EFSCORRUPTED); 831 + 835 832 ext4_handle_error(inode->i_sb, false, EFSCORRUPTED, inode->i_ino, block, 836 833 function, line); 837 834 } ··· 901 894 printk(KERN_CRIT "EXT4-fs error (device %s) in %s:%d: %s\n", 902 895 sb->s_id, function, line, errstr); 903 896 } 897 + fsnotify_sb_error(sb, NULL, errno ? errno : EFSCORRUPTED); 904 898 905 899 ext4_handle_error(sb, false, -errno, 0, 0, function, line); 906 900 }
+3
fs/nfsd/filecache.c
··· 602 602 struct inode *inode, struct inode *dir, 603 603 const struct qstr *name, u32 cookie) 604 604 { 605 + if (WARN_ON_ONCE(!inode)) 606 + return 0; 607 + 605 608 trace_nfsd_file_fsnotify_handle_event(inode, mask); 606 609 607 610 /* Should be no marks on non-regular files */
+107 -10
fs/notify/fanotify/fanotify.c
··· 111 111 return fanotify_info_equal(info1, info2); 112 112 } 113 113 114 + static bool fanotify_error_event_equal(struct fanotify_error_event *fee1, 115 + struct fanotify_error_event *fee2) 116 + { 117 + /* Error events against the same file system are always merged. */ 118 + if (!fanotify_fsid_equal(&fee1->fsid, &fee2->fsid)) 119 + return false; 120 + 121 + return true; 122 + } 123 + 114 124 static bool fanotify_should_merge(struct fanotify_event *old, 115 125 struct fanotify_event *new) 116 126 { ··· 151 141 case FANOTIFY_EVENT_TYPE_FID_NAME: 152 142 return fanotify_name_event_equal(FANOTIFY_NE(old), 153 143 FANOTIFY_NE(new)); 144 + case FANOTIFY_EVENT_TYPE_FS_ERROR: 145 + return fanotify_error_event_equal(FANOTIFY_EE(old), 146 + FANOTIFY_EE(new)); 154 147 default: 155 148 WARN_ON_ONCE(1); 156 149 } ··· 189 176 break; 190 177 if (fanotify_should_merge(old, new)) { 191 178 old->mask |= new->mask; 179 + 180 + if (fanotify_is_error_event(old->mask)) 181 + FANOTIFY_EE(old)->err_count++; 182 + 192 183 return 1; 193 184 } 194 185 } ··· 360 343 static int fanotify_encode_fh_len(struct inode *inode) 361 344 { 362 345 int dwords = 0; 346 + int fh_len; 363 347 364 348 if (!inode) 365 349 return 0; 366 350 367 351 exportfs_encode_inode_fh(inode, NULL, &dwords, NULL); 352 + fh_len = dwords << 2; 368 353 369 - return dwords << 2; 354 + /* 355 + * struct fanotify_error_event might be preallocated and is 356 + * limited to MAX_HANDLE_SZ. This should never happen, but 357 + * safeguard by forcing an invalid file handle. 358 + */ 359 + if (WARN_ON_ONCE(fh_len > MAX_HANDLE_SZ)) 360 + return 0; 361 + 362 + return fh_len; 370 363 } 371 364 372 365 /* ··· 397 370 fh->type = FILEID_ROOT; 398 371 fh->len = 0; 399 372 fh->flags = 0; 373 + 374 + /* 375 + * Invalid FHs are used by FAN_FS_ERROR for errors not 376 + * linked to any inode. The f_handle won't be reported 377 + * back to userspace. 378 + */ 400 379 if (!inode) 401 - return 0; 380 + goto out; 402 381 403 382 /* 404 383 * !gpf means preallocated variable size fh, but fh_len could ··· 436 403 fh->type = type; 437 404 fh->len = fh_len; 438 405 439 - /* Mix fh into event merge key */ 440 - *hash ^= fanotify_hash_fh(fh); 406 + out: 407 + /* 408 + * Mix fh into event merge key. Hash might be NULL in case of 409 + * unhashed FID events (i.e. FAN_FS_ERROR). 410 + */ 411 + if (hash) 412 + *hash ^= fanotify_hash_fh(fh); 441 413 442 414 return FANOTIFY_FH_HDR_LEN + fh_len; 443 415 ··· 490 452 if (event_mask & ALL_FSNOTIFY_DIRENT_EVENTS) 491 453 return dir; 492 454 493 - if (S_ISDIR(inode->i_mode)) 455 + if (inode && S_ISDIR(inode->i_mode)) 494 456 return inode; 495 457 496 458 return dir; ··· 601 563 return &fne->fae; 602 564 } 603 565 566 + static struct fanotify_event *fanotify_alloc_error_event( 567 + struct fsnotify_group *group, 568 + __kernel_fsid_t *fsid, 569 + const void *data, int data_type, 570 + unsigned int *hash) 571 + { 572 + struct fs_error_report *report = 573 + fsnotify_data_error_report(data, data_type); 574 + struct inode *inode; 575 + struct fanotify_error_event *fee; 576 + int fh_len; 577 + 578 + if (WARN_ON_ONCE(!report)) 579 + return NULL; 580 + 581 + fee = mempool_alloc(&group->fanotify_data.error_events_pool, GFP_NOFS); 582 + if (!fee) 583 + return NULL; 584 + 585 + fee->fae.type = FANOTIFY_EVENT_TYPE_FS_ERROR; 586 + fee->error = report->error; 587 + fee->err_count = 1; 588 + fee->fsid = *fsid; 589 + 590 + inode = report->inode; 591 + fh_len = fanotify_encode_fh_len(inode); 592 + 593 + /* Bad fh_len. Fallback to using an invalid fh. Should never happen. */ 594 + if (!fh_len && inode) 595 + inode = NULL; 596 + 597 + fanotify_encode_fh(&fee->object_fh, inode, fh_len, NULL, 0); 598 + 599 + *hash ^= fanotify_hash_fsid(fsid); 600 + 601 + return &fee->fae; 602 + } 603 + 604 604 static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group, 605 605 u32 mask, const void *data, 606 606 int data_type, struct inode *dir, ··· 706 630 707 631 if (fanotify_is_perm_event(mask)) { 708 632 event = fanotify_alloc_perm_event(path, gfp); 633 + } else if (fanotify_is_error_event(mask)) { 634 + event = fanotify_alloc_error_event(group, fsid, data, 635 + data_type, &hash); 709 636 } else if (name_event && (file_name || child)) { 710 637 event = fanotify_alloc_name_event(id, fsid, file_name, child, 711 638 &hash, gfp); ··· 781 702 782 703 assert_spin_locked(&group->notification_lock); 783 704 705 + if (!fanotify_is_hashed_event(event->mask)) 706 + return; 707 + 784 708 pr_debug("%s: group=%p event=%p bucket=%u\n", __func__, 785 709 group, event, bucket); 786 710 ··· 820 738 BUILD_BUG_ON(FAN_ONDIR != FS_ISDIR); 821 739 BUILD_BUG_ON(FAN_OPEN_EXEC != FS_OPEN_EXEC); 822 740 BUILD_BUG_ON(FAN_OPEN_EXEC_PERM != FS_OPEN_EXEC_PERM); 741 + BUILD_BUG_ON(FAN_FS_ERROR != FS_ERROR); 823 742 824 - BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 19); 743 + BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 20); 825 744 826 745 mask = fanotify_group_event_mask(group, iter_info, mask, data, 827 746 data_type, dir); ··· 861 778 } 862 779 863 780 fsn_event = &event->fse; 864 - ret = fsnotify_add_event(group, fsn_event, fanotify_merge, 865 - fanotify_is_hashed_event(mask) ? 866 - fanotify_insert_event : NULL); 781 + ret = fsnotify_insert_event(group, fsn_event, fanotify_merge, 782 + fanotify_insert_event); 867 783 if (ret) { 868 784 /* Permission events shouldn't be merged */ 869 785 BUG_ON(ret == 1 && mask & FANOTIFY_PERM_EVENTS); ··· 887 805 if (group->fanotify_data.ucounts) 888 806 dec_ucount(group->fanotify_data.ucounts, 889 807 UCOUNT_FANOTIFY_GROUPS); 808 + 809 + if (mempool_initialized(&group->fanotify_data.error_events_pool)) 810 + mempool_exit(&group->fanotify_data.error_events_pool); 890 811 } 891 812 892 813 static void fanotify_free_path_event(struct fanotify_event *event) ··· 918 833 kfree(FANOTIFY_NE(event)); 919 834 } 920 835 921 - static void fanotify_free_event(struct fsnotify_event *fsn_event) 836 + static void fanotify_free_error_event(struct fsnotify_group *group, 837 + struct fanotify_event *event) 838 + { 839 + struct fanotify_error_event *fee = FANOTIFY_EE(event); 840 + 841 + mempool_free(fee, &group->fanotify_data.error_events_pool); 842 + } 843 + 844 + static void fanotify_free_event(struct fsnotify_group *group, 845 + struct fsnotify_event *fsn_event) 922 846 { 923 847 struct fanotify_event *event; 924 848 ··· 948 854 break; 949 855 case FANOTIFY_EVENT_TYPE_OVERFLOW: 950 856 kfree(event); 857 + break; 858 + case FANOTIFY_EVENT_TYPE_FS_ERROR: 859 + fanotify_free_error_event(group, event); 951 860 break; 952 861 default: 953 862 WARN_ON_ONCE(1);
+50 -4
fs/notify/fanotify/fanotify.h
··· 141 141 FANOTIFY_EVENT_TYPE_PATH, 142 142 FANOTIFY_EVENT_TYPE_PATH_PERM, 143 143 FANOTIFY_EVENT_TYPE_OVERFLOW, /* struct fanotify_event */ 144 + FANOTIFY_EVENT_TYPE_FS_ERROR, /* struct fanotify_error_event */ 144 145 __FANOTIFY_EVENT_TYPE_NUM 145 146 }; 146 147 ··· 171 170 event->pid = NULL; 172 171 } 173 172 173 + #define FANOTIFY_INLINE_FH(name, size) \ 174 + struct { \ 175 + struct fanotify_fh (name); \ 176 + /* Space for object_fh.buf[] - access with fanotify_fh_buf() */ \ 177 + unsigned char _inline_fh_buf[(size)]; \ 178 + } 179 + 174 180 struct fanotify_fid_event { 175 181 struct fanotify_event fae; 176 182 __kernel_fsid_t fsid; 177 - struct fanotify_fh object_fh; 178 - /* Reserve space in object_fh.buf[] - access with fanotify_fh_buf() */ 179 - unsigned char _inline_fh_buf[FANOTIFY_INLINE_FH_LEN]; 183 + 184 + FANOTIFY_INLINE_FH(object_fh, FANOTIFY_INLINE_FH_LEN); 180 185 }; 181 186 182 187 static inline struct fanotify_fid_event * ··· 203 196 return container_of(event, struct fanotify_name_event, fae); 204 197 } 205 198 199 + struct fanotify_error_event { 200 + struct fanotify_event fae; 201 + s32 error; /* Error reported by the Filesystem. */ 202 + u32 err_count; /* Suppressed errors count */ 203 + 204 + __kernel_fsid_t fsid; /* FSID this error refers to. */ 205 + 206 + FANOTIFY_INLINE_FH(object_fh, MAX_HANDLE_SZ); 207 + }; 208 + 209 + static inline struct fanotify_error_event * 210 + FANOTIFY_EE(struct fanotify_event *event) 211 + { 212 + return container_of(event, struct fanotify_error_event, fae); 213 + } 214 + 206 215 static inline __kernel_fsid_t *fanotify_event_fsid(struct fanotify_event *event) 207 216 { 208 217 if (event->type == FANOTIFY_EVENT_TYPE_FID) 209 218 return &FANOTIFY_FE(event)->fsid; 210 219 else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME) 211 220 return &FANOTIFY_NE(event)->fsid; 221 + else if (event->type == FANOTIFY_EVENT_TYPE_FS_ERROR) 222 + return &FANOTIFY_EE(event)->fsid; 212 223 else 213 224 return NULL; 214 225 } ··· 238 213 return &FANOTIFY_FE(event)->object_fh; 239 214 else if (event->type == FANOTIFY_EVENT_TYPE_FID_NAME) 240 215 return fanotify_info_file_fh(&FANOTIFY_NE(event)->info); 216 + else if (event->type == FANOTIFY_EVENT_TYPE_FS_ERROR) 217 + return &FANOTIFY_EE(event)->object_fh; 241 218 else 242 219 return NULL; 243 220 } ··· 269 242 struct fanotify_info *info = fanotify_event_info(event); 270 243 271 244 return info ? fanotify_info_dir_fh_len(info) : 0; 245 + } 246 + 247 + static inline bool fanotify_event_has_object_fh(struct fanotify_event *event) 248 + { 249 + /* For error events, even zeroed fh are reported. */ 250 + if (event->type == FANOTIFY_EVENT_TYPE_FS_ERROR) 251 + return true; 252 + return fanotify_event_object_fh_len(event) > 0; 253 + } 254 + 255 + static inline bool fanotify_event_has_dir_fh(struct fanotify_event *event) 256 + { 257 + return fanotify_event_dir_fh_len(event) > 0; 272 258 } 273 259 274 260 struct fanotify_path_event { ··· 327 287 return container_of(fse, struct fanotify_event, fse); 328 288 } 329 289 290 + static inline bool fanotify_is_error_event(u32 mask) 291 + { 292 + return mask & FAN_FS_ERROR; 293 + } 294 + 330 295 static inline bool fanotify_event_has_path(struct fanotify_event *event) 331 296 { 332 297 return event->type == FANOTIFY_EVENT_TYPE_PATH || ··· 360 315 */ 361 316 static inline bool fanotify_is_hashed_event(u32 mask) 362 317 { 363 - return !fanotify_is_perm_event(mask) && !(mask & FS_Q_OVERFLOW); 318 + return !(fanotify_is_perm_event(mask) || 319 + fsnotify_is_overflow_event(mask)); 364 320 } 365 321 366 322 static inline unsigned int fanotify_event_hash_bucket(
+119 -38
fs/notify/fanotify/fanotify_user.c
··· 30 30 #define FANOTIFY_DEFAULT_MAX_EVENTS 16384 31 31 #define FANOTIFY_OLD_DEFAULT_MAX_MARKS 8192 32 32 #define FANOTIFY_DEFAULT_MAX_GROUPS 128 33 + #define FANOTIFY_DEFAULT_FEE_POOL_SIZE 32 33 34 34 35 /* 35 36 * Legacy fanotify marks limits (8192) is per group and we introduced a tunable ··· 115 114 (sizeof(struct fanotify_event_info_fid) + sizeof(struct file_handle)) 116 115 #define FANOTIFY_PIDFD_INFO_HDR_LEN \ 117 116 sizeof(struct fanotify_event_info_pidfd) 117 + #define FANOTIFY_ERROR_INFO_LEN \ 118 + (sizeof(struct fanotify_event_info_error)) 118 119 119 120 static int fanotify_fid_info_len(int fh_len, int name_len) 120 121 { ··· 129 126 FANOTIFY_EVENT_ALIGN); 130 127 } 131 128 132 - static int fanotify_event_info_len(unsigned int info_mode, 133 - struct fanotify_event *event) 129 + static size_t fanotify_event_len(unsigned int info_mode, 130 + struct fanotify_event *event) 134 131 { 135 - struct fanotify_info *info = fanotify_event_info(event); 136 - int dir_fh_len = fanotify_event_dir_fh_len(event); 137 - int fh_len = fanotify_event_object_fh_len(event); 138 - int info_len = 0; 132 + size_t event_len = FAN_EVENT_METADATA_LEN; 133 + struct fanotify_info *info; 134 + int dir_fh_len; 135 + int fh_len; 139 136 int dot_len = 0; 140 137 141 - if (dir_fh_len) { 142 - info_len += fanotify_fid_info_len(dir_fh_len, info->name_len); 138 + if (!info_mode) 139 + return event_len; 140 + 141 + if (fanotify_is_error_event(event->mask)) 142 + event_len += FANOTIFY_ERROR_INFO_LEN; 143 + 144 + info = fanotify_event_info(event); 145 + 146 + if (fanotify_event_has_dir_fh(event)) { 147 + dir_fh_len = fanotify_event_dir_fh_len(event); 148 + event_len += fanotify_fid_info_len(dir_fh_len, info->name_len); 143 149 } else if ((info_mode & FAN_REPORT_NAME) && 144 150 (event->mask & FAN_ONDIR)) { 145 151 /* ··· 159 147 } 160 148 161 149 if (info_mode & FAN_REPORT_PIDFD) 162 - info_len += FANOTIFY_PIDFD_INFO_HDR_LEN; 150 + event_len += FANOTIFY_PIDFD_INFO_HDR_LEN; 163 151 164 - if (fh_len) 165 - info_len += fanotify_fid_info_len(fh_len, dot_len); 152 + if (fanotify_event_has_object_fh(event)) { 153 + fh_len = fanotify_event_object_fh_len(event); 154 + event_len += fanotify_fid_info_len(fh_len, dot_len); 155 + } 166 156 167 - return info_len; 157 + return event_len; 168 158 } 169 159 170 160 /* ··· 195 181 static struct fanotify_event *get_one_event(struct fsnotify_group *group, 196 182 size_t count) 197 183 { 198 - size_t event_size = FAN_EVENT_METADATA_LEN; 184 + size_t event_size; 199 185 struct fanotify_event *event = NULL; 200 186 struct fsnotify_event *fsn_event; 201 187 unsigned int info_mode = FAN_GROUP_FLAG(group, FANOTIFY_INFO_MODES); ··· 208 194 goto out; 209 195 210 196 event = FANOTIFY_E(fsn_event); 211 - if (info_mode) 212 - event_size += fanotify_event_info_len(info_mode, event); 197 + event_size = fanotify_event_len(info_mode, event); 213 198 214 199 if (event_size > count) { 215 200 event = ERR_PTR(-EINVAL); ··· 329 316 return -ENOENT; 330 317 } 331 318 319 + static size_t copy_error_info_to_user(struct fanotify_event *event, 320 + char __user *buf, int count) 321 + { 322 + struct fanotify_event_info_error info; 323 + struct fanotify_error_event *fee = FANOTIFY_EE(event); 324 + 325 + info.hdr.info_type = FAN_EVENT_INFO_TYPE_ERROR; 326 + info.hdr.pad = 0; 327 + info.hdr.len = FANOTIFY_ERROR_INFO_LEN; 328 + 329 + if (WARN_ON(count < info.hdr.len)) 330 + return -EFAULT; 331 + 332 + info.error = fee->error; 333 + info.error_count = fee->err_count; 334 + 335 + if (copy_to_user(buf, &info, sizeof(info))) 336 + return -EFAULT; 337 + 338 + return info.hdr.len; 339 + } 340 + 332 341 static int copy_fid_info_to_user(__kernel_fsid_t *fsid, struct fanotify_fh *fh, 333 342 int info_type, const char *name, 334 343 size_t name_len, ··· 365 330 366 331 pr_debug("%s: fh_len=%zu name_len=%zu, info_len=%zu, count=%zu\n", 367 332 __func__, fh_len, name_len, info_len, count); 368 - 369 - if (!fh_len) 370 - return 0; 371 333 372 334 if (WARN_ON_ONCE(len < sizeof(info) || len > count)) 373 335 return -EFAULT; ··· 400 368 401 369 handle.handle_type = fh->type; 402 370 handle.handle_bytes = fh_len; 371 + 372 + /* Mangle handle_type for bad file_handle */ 373 + if (!fh_len) 374 + handle.handle_type = FILEID_INVALID; 375 + 403 376 if (copy_to_user(buf, &handle, sizeof(handle))) 404 377 return -EFAULT; 405 378 ··· 481 444 /* 482 445 * Event info records order is as follows: dir fid + name, child fid. 483 446 */ 484 - if (fanotify_event_dir_fh_len(event)) { 447 + if (fanotify_event_has_dir_fh(event)) { 485 448 info_type = info->name_len ? FAN_EVENT_INFO_TYPE_DFID_NAME : 486 449 FAN_EVENT_INFO_TYPE_DFID; 487 450 ret = copy_fid_info_to_user(fanotify_event_fsid(event), ··· 497 460 total_bytes += ret; 498 461 } 499 462 500 - if (fanotify_event_object_fh_len(event)) { 463 + if (fanotify_event_has_object_fh(event)) { 501 464 const char *dot = NULL; 502 465 int dot_len = 0; 503 466 ··· 557 520 total_bytes += ret; 558 521 } 559 522 523 + if (fanotify_is_error_event(event->mask)) { 524 + ret = copy_error_info_to_user(event, buf, count); 525 + if (ret < 0) 526 + return ret; 527 + buf += ret; 528 + count -= ret; 529 + total_bytes += ret; 530 + } 531 + 560 532 return total_bytes; 561 533 } 562 534 ··· 583 537 584 538 pr_debug("%s: group=%p event=%p\n", __func__, group, event); 585 539 586 - metadata.event_len = FAN_EVENT_METADATA_LEN + 587 - fanotify_event_info_len(info_mode, event); 540 + metadata.event_len = fanotify_event_len(info_mode, event); 588 541 metadata.metadata_len = FAN_EVENT_METADATA_LEN; 589 542 metadata.vers = FANOTIFY_METADATA_VERSION; 590 543 metadata.reserved = 0; ··· 1094 1049 return ERR_PTR(ret); 1095 1050 } 1096 1051 1052 + static int fanotify_group_init_error_pool(struct fsnotify_group *group) 1053 + { 1054 + if (mempool_initialized(&group->fanotify_data.error_events_pool)) 1055 + return 0; 1056 + 1057 + return mempool_init_kmalloc_pool(&group->fanotify_data.error_events_pool, 1058 + FANOTIFY_DEFAULT_FEE_POOL_SIZE, 1059 + sizeof(struct fanotify_error_event)); 1060 + } 1097 1061 1098 1062 static int fanotify_add_mark(struct fsnotify_group *group, 1099 1063 fsnotify_connp_t *connp, unsigned int type, ··· 1111 1057 { 1112 1058 struct fsnotify_mark *fsn_mark; 1113 1059 __u32 added; 1060 + int ret = 0; 1114 1061 1115 1062 mutex_lock(&group->mark_mutex); 1116 1063 fsn_mark = fsnotify_find_mark(connp, group); ··· 1122 1067 return PTR_ERR(fsn_mark); 1123 1068 } 1124 1069 } 1070 + 1071 + /* 1072 + * Error events are pre-allocated per group, only if strictly 1073 + * needed (i.e. FAN_FS_ERROR was requested). 1074 + */ 1075 + if (!(flags & FAN_MARK_IGNORED_MASK) && (mask & FAN_FS_ERROR)) { 1076 + ret = fanotify_group_init_error_pool(group); 1077 + if (ret) 1078 + goto out; 1079 + } 1080 + 1125 1081 added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); 1126 1082 if (added & ~fsnotify_conn_mask(fsn_mark->connector)) 1127 1083 fsnotify_recalc_mask(fsn_mark->connector); 1084 + 1085 + out: 1128 1086 mutex_unlock(&group->mark_mutex); 1129 1087 1130 1088 fsnotify_put_mark(fsn_mark); 1131 - return 0; 1089 + return ret; 1132 1090 } 1133 1091 1134 1092 static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, ··· 1363 1295 return fd; 1364 1296 } 1365 1297 1366 - /* Check if filesystem can encode a unique fid */ 1367 - static int fanotify_test_fid(struct path *path, __kernel_fsid_t *fsid) 1298 + static int fanotify_test_fsid(struct dentry *dentry, __kernel_fsid_t *fsid) 1368 1299 { 1369 1300 __kernel_fsid_t root_fsid; 1370 1301 int err; 1371 1302 1372 1303 /* 1373 - * Make sure path is not in filesystem with zero fsid (e.g. tmpfs). 1304 + * Make sure dentry is not of a filesystem with zero fsid (e.g. fuse). 1374 1305 */ 1375 - err = vfs_get_fsid(path->dentry, fsid); 1306 + err = vfs_get_fsid(dentry, fsid); 1376 1307 if (err) 1377 1308 return err; 1378 1309 ··· 1379 1312 return -ENODEV; 1380 1313 1381 1314 /* 1382 - * Make sure path is not inside a filesystem subvolume (e.g. btrfs) 1315 + * Make sure dentry is not of a filesystem subvolume (e.g. btrfs) 1383 1316 * which uses a different fsid than sb root. 1384 1317 */ 1385 - err = vfs_get_fsid(path->dentry->d_sb->s_root, &root_fsid); 1318 + err = vfs_get_fsid(dentry->d_sb->s_root, &root_fsid); 1386 1319 if (err) 1387 1320 return err; 1388 1321 ··· 1390 1323 root_fsid.val[1] != fsid->val[1]) 1391 1324 return -EXDEV; 1392 1325 1326 + return 0; 1327 + } 1328 + 1329 + /* Check if filesystem can encode a unique fid */ 1330 + static int fanotify_test_fid(struct dentry *dentry) 1331 + { 1393 1332 /* 1394 1333 * We need to make sure that the file system supports at least 1395 1334 * encoding a file handle so user can use name_to_handle_at() to ··· 1403 1330 * objects. However, name_to_handle_at() requires that the 1404 1331 * filesystem also supports decoding file handles. 1405 1332 */ 1406 - if (!path->dentry->d_sb->s_export_op || 1407 - !path->dentry->d_sb->s_export_op->fh_to_dentry) 1333 + if (!dentry->d_sb->s_export_op || 1334 + !dentry->d_sb->s_export_op->fh_to_dentry) 1408 1335 return -EOPNOTSUPP; 1409 1336 1410 1337 return 0; ··· 1520 1447 group->priority == FS_PRIO_0) 1521 1448 goto fput_and_out; 1522 1449 1450 + if (mask & FAN_FS_ERROR && 1451 + mark_type != FAN_MARK_FILESYSTEM) 1452 + goto fput_and_out; 1453 + 1523 1454 /* 1524 - * Events with data type inode do not carry enough information to report 1525 - * event->fd, so we do not allow setting a mask for inode events unless 1526 - * group supports reporting fid. 1527 - * inode events are not supported on a mount mark, because they do not 1528 - * carry enough information (i.e. path) to be filtered by mount point. 1455 + * Events that do not carry enough information to report 1456 + * event->fd require a group that supports reporting fid. Those 1457 + * events are not supported on a mount mark, because they do not 1458 + * carry enough information (i.e. path) to be filtered by mount 1459 + * point. 1529 1460 */ 1530 1461 fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS); 1531 - if (mask & FANOTIFY_INODE_EVENTS && 1462 + if (mask & ~(FANOTIFY_FD_EVENTS|FANOTIFY_EVENT_FLAGS) && 1532 1463 (!fid_mode || mark_type == FAN_MARK_MOUNT)) 1533 1464 goto fput_and_out; 1534 1465 ··· 1559 1482 } 1560 1483 1561 1484 if (fid_mode) { 1562 - ret = fanotify_test_fid(&path, &__fsid); 1485 + ret = fanotify_test_fsid(path.dentry, &__fsid); 1486 + if (ret) 1487 + goto path_put_and_out; 1488 + 1489 + ret = fanotify_test_fid(path.dentry); 1563 1490 if (ret) 1564 1491 goto path_put_and_out; 1565 1492
+6 -4
fs/notify/fsnotify.c
··· 252 252 if (WARN_ON_ONCE(!ops->handle_inode_event)) 253 253 return 0; 254 254 255 + if (WARN_ON_ONCE(!inode && !dir)) 256 + return 0; 257 + 255 258 if ((inode_mark->mask & FS_EXCL_UNLINK) && 256 259 path && d_unlinked(path->dentry)) 257 260 return 0; ··· 458 455 * @file_name is relative to 459 456 * @file_name: optional file name associated with event 460 457 * @inode: optional inode associated with event - 461 - * either @dir or @inode must be non-NULL. 462 - * if both are non-NULL event may be reported to both. 458 + * If @dir and @inode are both non-NULL, event may be 459 + * reported to both. 463 460 * @cookie: inotify rename cookie 464 461 */ 465 462 int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir, 466 463 const struct qstr *file_name, struct inode *inode, u32 cookie) 467 464 { 468 465 const struct path *path = fsnotify_data_path(data, data_type); 466 + struct super_block *sb = fsnotify_data_sb(data, data_type); 469 467 struct fsnotify_iter_info iter_info = {}; 470 - struct super_block *sb; 471 468 struct mount *mnt = NULL; 472 469 struct inode *parent = NULL; 473 470 int ret = 0; ··· 486 483 */ 487 484 parent = dir; 488 485 } 489 - sb = inode->i_sb; 490 486 491 487 /* 492 488 * Optimization: srcu_read_lock() has a memory barrier which can
+1 -1
fs/notify/group.c
··· 88 88 * that deliberately ignores overflow events. 89 89 */ 90 90 if (group->overflow_event) 91 - group->ops->free_event(group->overflow_event); 91 + group->ops->free_event(group, group->overflow_event); 92 92 93 93 fsnotify_put_group(group); 94 94 }
+3 -2
fs/notify/inotify/inotify_fsnotify.c
··· 116 116 if (len) 117 117 strcpy(event->name, name->name); 118 118 119 - ret = fsnotify_add_event(group, fsn_event, inotify_merge, NULL); 119 + ret = fsnotify_add_event(group, fsn_event, inotify_merge); 120 120 if (ret) { 121 121 /* Our event wasn't used in the end. Free it. */ 122 122 fsnotify_destroy_event(group, fsn_event); ··· 177 177 dec_inotify_instances(group->inotify_data.ucounts); 178 178 } 179 179 180 - static void inotify_free_event(struct fsnotify_event *fsn_event) 180 + static void inotify_free_event(struct fsnotify_group *group, 181 + struct fsnotify_event *fsn_event) 181 182 { 182 183 kfree(INOTIFY_E(fsn_event)); 183 184 }
+3 -3
fs/notify/inotify/inotify_user.c
··· 94 94 __u32 mask; 95 95 96 96 /* 97 - * Everything should accept their own ignored and should receive events 98 - * when the inode is unmounted. All directories care about children. 97 + * Everything should receive events when the inode is unmounted. 98 + * All directories care about children. 99 99 */ 100 - mask = (FS_IN_IGNORED | FS_UNMOUNT); 100 + mask = (FS_UNMOUNT); 101 101 if (S_ISDIR(inode->i_mode)) 102 102 mask |= FS_EVENT_ON_CHILD; 103 103
+7 -7
fs/notify/notification.c
··· 64 64 WARN_ON(!list_empty(&event->list)); 65 65 spin_unlock(&group->notification_lock); 66 66 } 67 - group->ops->free_event(event); 67 + group->ops->free_event(group, event); 68 68 } 69 69 70 70 /* ··· 78 78 * 2 if the event was not queued - either the queue of events has overflown 79 79 * or the group is shutting down. 80 80 */ 81 - int fsnotify_add_event(struct fsnotify_group *group, 82 - struct fsnotify_event *event, 83 - int (*merge)(struct fsnotify_group *, 84 - struct fsnotify_event *), 85 - void (*insert)(struct fsnotify_group *, 86 - struct fsnotify_event *)) 81 + int fsnotify_insert_event(struct fsnotify_group *group, 82 + struct fsnotify_event *event, 83 + int (*merge)(struct fsnotify_group *, 84 + struct fsnotify_event *), 85 + void (*insert)(struct fsnotify_group *, 86 + struct fsnotify_event *)) 87 87 { 88 88 int ret = 0; 89 89 struct list_head *list = &group->notification_list;
+8 -1
include/linux/fanotify.h
··· 84 84 */ 85 85 #define FANOTIFY_DIRENT_EVENTS (FAN_MOVE | FAN_CREATE | FAN_DELETE) 86 86 87 + /* Events that can be reported with event->fd */ 88 + #define FANOTIFY_FD_EVENTS (FANOTIFY_PATH_EVENTS | FANOTIFY_PERM_EVENTS) 89 + 87 90 /* Events that can only be reported with data type FSNOTIFY_EVENT_INODE */ 88 91 #define FANOTIFY_INODE_EVENTS (FANOTIFY_DIRENT_EVENTS | \ 89 92 FAN_ATTRIB | FAN_MOVE_SELF | FAN_DELETE_SELF) 90 93 94 + /* Events that can only be reported with data type FSNOTIFY_EVENT_ERROR */ 95 + #define FANOTIFY_ERROR_EVENTS (FAN_FS_ERROR) 96 + 91 97 /* Events that user can request to be notified on */ 92 98 #define FANOTIFY_EVENTS (FANOTIFY_PATH_EVENTS | \ 93 - FANOTIFY_INODE_EVENTS) 99 + FANOTIFY_INODE_EVENTS | \ 100 + FANOTIFY_ERROR_EVENTS) 94 101 95 102 /* Events that require a permission response from user */ 96 103 #define FANOTIFY_PERM_EVENTS (FAN_OPEN_PERM | FAN_ACCESS_PERM | \
+42 -16
include/linux/fsnotify.h
··· 26 26 * FS_EVENT_ON_CHILD mask on the parent inode and will not be reported if only 27 27 * the child is interested and not the parent. 28 28 */ 29 - static inline void fsnotify_name(struct inode *dir, __u32 mask, 30 - struct inode *child, 31 - const struct qstr *name, u32 cookie) 29 + static inline int fsnotify_name(__u32 mask, const void *data, int data_type, 30 + struct inode *dir, const struct qstr *name, 31 + u32 cookie) 32 32 { 33 33 if (atomic_long_read(&dir->i_sb->s_fsnotify_connectors) == 0) 34 - return; 34 + return 0; 35 35 36 - fsnotify(mask, child, FSNOTIFY_EVENT_INODE, dir, name, NULL, cookie); 36 + return fsnotify(mask, data, data_type, dir, name, NULL, cookie); 37 37 } 38 38 39 39 static inline void fsnotify_dirent(struct inode *dir, struct dentry *dentry, 40 40 __u32 mask) 41 41 { 42 - fsnotify_name(dir, mask, d_inode(dentry), &dentry->d_name, 0); 42 + fsnotify_name(mask, dentry, FSNOTIFY_EVENT_DENTRY, dir, &dentry->d_name, 0); 43 43 } 44 44 45 45 static inline void fsnotify_inode(struct inode *inode, __u32 mask) ··· 86 86 */ 87 87 static inline void fsnotify_dentry(struct dentry *dentry, __u32 mask) 88 88 { 89 - fsnotify_parent(dentry, mask, d_inode(dentry), FSNOTIFY_EVENT_INODE); 89 + fsnotify_parent(dentry, mask, dentry, FSNOTIFY_EVENT_DENTRY); 90 90 } 91 91 92 92 static inline int fsnotify_file(struct file *file, __u32 mask) ··· 154 154 new_dir_mask |= FS_ISDIR; 155 155 } 156 156 157 - fsnotify_name(old_dir, old_dir_mask, source, old_name, fs_cookie); 158 - fsnotify_name(new_dir, new_dir_mask, source, new_name, fs_cookie); 157 + fsnotify_name(old_dir_mask, source, FSNOTIFY_EVENT_INODE, 158 + old_dir, old_name, fs_cookie); 159 + fsnotify_name(new_dir_mask, source, FSNOTIFY_EVENT_INODE, 160 + new_dir, new_name, fs_cookie); 159 161 160 162 if (target) 161 163 fsnotify_link_count(target); ··· 192 190 193 191 /* 194 192 * fsnotify_create - 'name' was linked in 193 + * 194 + * Caller must make sure that dentry->d_name is stable. 195 + * Note: some filesystems (e.g. kernfs) leave @dentry negative and instantiate 196 + * ->d_inode later 195 197 */ 196 - static inline void fsnotify_create(struct inode *inode, struct dentry *dentry) 198 + static inline void fsnotify_create(struct inode *dir, struct dentry *dentry) 197 199 { 198 - audit_inode_child(inode, dentry, AUDIT_TYPE_CHILD_CREATE); 200 + audit_inode_child(dir, dentry, AUDIT_TYPE_CHILD_CREATE); 199 201 200 - fsnotify_dirent(inode, dentry, FS_CREATE); 202 + fsnotify_dirent(dir, dentry, FS_CREATE); 201 203 } 202 204 203 205 /* 204 206 * fsnotify_link - new hardlink in 'inode' directory 207 + * 208 + * Caller must make sure that new_dentry->d_name is stable. 205 209 * Note: We have to pass also the linked inode ptr as some filesystems leave 206 210 * new_dentry->d_inode NULL and instantiate inode pointer later 207 211 */ ··· 217 209 fsnotify_link_count(inode); 218 210 audit_inode_child(dir, new_dentry, AUDIT_TYPE_CHILD_CREATE); 219 211 220 - fsnotify_name(dir, FS_CREATE, inode, &new_dentry->d_name, 0); 212 + fsnotify_name(FS_CREATE, inode, FSNOTIFY_EVENT_INODE, 213 + dir, &new_dentry->d_name, 0); 221 214 } 222 215 223 216 /* ··· 236 227 237 228 /* 238 229 * fsnotify_mkdir - directory 'name' was created 230 + * 231 + * Caller must make sure that dentry->d_name is stable. 232 + * Note: some filesystems (e.g. kernfs) leave @dentry negative and instantiate 233 + * ->d_inode later 239 234 */ 240 - static inline void fsnotify_mkdir(struct inode *inode, struct dentry *dentry) 235 + static inline void fsnotify_mkdir(struct inode *dir, struct dentry *dentry) 241 236 { 242 - audit_inode_child(inode, dentry, AUDIT_TYPE_CHILD_CREATE); 237 + audit_inode_child(dir, dentry, AUDIT_TYPE_CHILD_CREATE); 243 238 244 - fsnotify_dirent(inode, dentry, FS_CREATE | FS_ISDIR); 239 + fsnotify_dirent(dir, dentry, FS_CREATE | FS_ISDIR); 245 240 } 246 241 247 242 /* ··· 337 324 338 325 if (mask) 339 326 fsnotify_dentry(dentry, mask); 327 + } 328 + 329 + static inline int fsnotify_sb_error(struct super_block *sb, struct inode *inode, 330 + int error) 331 + { 332 + struct fs_error_report report = { 333 + .error = error, 334 + .inode = inode, 335 + .sb = sb, 336 + }; 337 + 338 + return fsnotify(FS_ERROR, &report, FSNOTIFY_EVENT_ERROR, 339 + NULL, NULL, NULL, 0); 340 340 } 341 341 342 342 #endif /* _LINUX_FS_NOTIFY_H */
+87 -9
include/linux/fsnotify_backend.h
··· 19 19 #include <linux/atomic.h> 20 20 #include <linux/user_namespace.h> 21 21 #include <linux/refcount.h> 22 + #include <linux/mempool.h> 22 23 23 24 /* 24 25 * IN_* from inotfy.h lines up EXACTLY with FS_*, this is so we can easily ··· 43 42 44 43 #define FS_UNMOUNT 0x00002000 /* inode on umount fs */ 45 44 #define FS_Q_OVERFLOW 0x00004000 /* Event queued overflowed */ 45 + #define FS_ERROR 0x00008000 /* Filesystem Error (fanotify) */ 46 + 47 + /* 48 + * FS_IN_IGNORED overloads FS_ERROR. It is only used internally by inotify 49 + * which does not support FS_ERROR. 50 + */ 46 51 #define FS_IN_IGNORED 0x00008000 /* last inotify event here */ 47 52 48 53 #define FS_OPEN_PERM 0x00010000 /* open event in an permission hook */ ··· 102 95 #define ALL_FSNOTIFY_EVENTS (ALL_FSNOTIFY_DIRENT_EVENTS | \ 103 96 FS_EVENTS_POSS_ON_CHILD | \ 104 97 FS_DELETE_SELF | FS_MOVE_SELF | FS_DN_RENAME | \ 105 - FS_UNMOUNT | FS_Q_OVERFLOW | FS_IN_IGNORED) 98 + FS_UNMOUNT | FS_Q_OVERFLOW | FS_IN_IGNORED | \ 99 + FS_ERROR) 106 100 107 101 /* Extra flags that may be reported with event or control handling of events */ 108 102 #define ALL_FSNOTIFY_FLAGS (FS_EXCL_UNLINK | FS_ISDIR | FS_IN_ONESHOT | \ ··· 144 136 * @dir: optional directory associated with event - 145 137 * if @file_name is not NULL, this is the directory that 146 138 * @file_name is relative to. 139 + * Either @inode or @dir must be non-NULL. 147 140 * @file_name: optional file name associated with event 148 141 * @cookie: inotify rename cookie 149 142 * ··· 164 155 const struct qstr *file_name, u32 cookie); 165 156 void (*free_group_priv)(struct fsnotify_group *group); 166 157 void (*freeing_mark)(struct fsnotify_mark *mark, struct fsnotify_group *group); 167 - void (*free_event)(struct fsnotify_event *event); 158 + void (*free_event)(struct fsnotify_group *group, struct fsnotify_event *event); 168 159 /* called on final put+free to free memory */ 169 160 void (*free_mark)(struct fsnotify_mark *mark); 170 161 }; ··· 247 238 int flags; /* flags from fanotify_init() */ 248 239 int f_flags; /* event_f_flags from fanotify_init() */ 249 240 struct ucounts *ucounts; 241 + mempool_t error_events_pool; 250 242 } fanotify_data; 251 243 #endif /* CONFIG_FANOTIFY */ 252 244 }; ··· 258 248 FSNOTIFY_EVENT_NONE, 259 249 FSNOTIFY_EVENT_PATH, 260 250 FSNOTIFY_EVENT_INODE, 251 + FSNOTIFY_EVENT_DENTRY, 252 + FSNOTIFY_EVENT_ERROR, 253 + }; 254 + 255 + struct fs_error_report { 256 + int error; 257 + struct inode *inode; 258 + struct super_block *sb; 261 259 }; 262 260 263 261 static inline struct inode *fsnotify_data_inode(const void *data, int data_type) ··· 273 255 switch (data_type) { 274 256 case FSNOTIFY_EVENT_INODE: 275 257 return (struct inode *)data; 258 + case FSNOTIFY_EVENT_DENTRY: 259 + return d_inode(data); 276 260 case FSNOTIFY_EVENT_PATH: 277 261 return d_inode(((const struct path *)data)->dentry); 262 + case FSNOTIFY_EVENT_ERROR: 263 + return ((struct fs_error_report *)data)->inode; 264 + default: 265 + return NULL; 266 + } 267 + } 268 + 269 + static inline struct dentry *fsnotify_data_dentry(const void *data, int data_type) 270 + { 271 + switch (data_type) { 272 + case FSNOTIFY_EVENT_DENTRY: 273 + /* Non const is needed for dget() */ 274 + return (struct dentry *)data; 275 + case FSNOTIFY_EVENT_PATH: 276 + return ((const struct path *)data)->dentry; 278 277 default: 279 278 return NULL; 280 279 } ··· 303 268 switch (data_type) { 304 269 case FSNOTIFY_EVENT_PATH: 305 270 return data; 271 + default: 272 + return NULL; 273 + } 274 + } 275 + 276 + static inline struct super_block *fsnotify_data_sb(const void *data, 277 + int data_type) 278 + { 279 + switch (data_type) { 280 + case FSNOTIFY_EVENT_INODE: 281 + return ((struct inode *)data)->i_sb; 282 + case FSNOTIFY_EVENT_DENTRY: 283 + return ((struct dentry *)data)->d_sb; 284 + case FSNOTIFY_EVENT_PATH: 285 + return ((const struct path *)data)->dentry->d_sb; 286 + case FSNOTIFY_EVENT_ERROR: 287 + return ((struct fs_error_report *) data)->sb; 288 + default: 289 + return NULL; 290 + } 291 + } 292 + 293 + static inline struct fs_error_report *fsnotify_data_error_report( 294 + const void *data, 295 + int data_type) 296 + { 297 + switch (data_type) { 298 + case FSNOTIFY_EVENT_ERROR: 299 + return (struct fs_error_report *) data; 306 300 default: 307 301 return NULL; 308 302 } ··· 546 482 extern void fsnotify_destroy_event(struct fsnotify_group *group, 547 483 struct fsnotify_event *event); 548 484 /* attach the event to the group notification queue */ 549 - extern int fsnotify_add_event(struct fsnotify_group *group, 550 - struct fsnotify_event *event, 551 - int (*merge)(struct fsnotify_group *, 552 - struct fsnotify_event *), 553 - void (*insert)(struct fsnotify_group *, 554 - struct fsnotify_event *)); 485 + extern int fsnotify_insert_event(struct fsnotify_group *group, 486 + struct fsnotify_event *event, 487 + int (*merge)(struct fsnotify_group *, 488 + struct fsnotify_event *), 489 + void (*insert)(struct fsnotify_group *, 490 + struct fsnotify_event *)); 491 + 492 + static inline int fsnotify_add_event(struct fsnotify_group *group, 493 + struct fsnotify_event *event, 494 + int (*merge)(struct fsnotify_group *, 495 + struct fsnotify_event *)) 496 + { 497 + return fsnotify_insert_event(group, event, merge, NULL); 498 + } 499 + 555 500 /* Queue overflow event to a notification group */ 556 501 static inline void fsnotify_queue_overflow(struct fsnotify_group *group) 557 502 { 558 - fsnotify_add_event(group, group->overflow_event, NULL, NULL); 503 + fsnotify_add_event(group, group->overflow_event, NULL); 504 + } 505 + 506 + static inline bool fsnotify_is_overflow_event(u32 mask) 507 + { 508 + return mask & FS_Q_OVERFLOW; 559 509 } 560 510 561 511 static inline bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group)
+8
include/uapi/linux/fanotify.h
··· 20 20 #define FAN_OPEN_EXEC 0x00001000 /* File was opened for exec */ 21 21 22 22 #define FAN_Q_OVERFLOW 0x00004000 /* Event queued overflowed */ 23 + #define FAN_FS_ERROR 0x00008000 /* Filesystem error */ 23 24 24 25 #define FAN_OPEN_PERM 0x00010000 /* File open in perm check */ 25 26 #define FAN_ACCESS_PERM 0x00020000 /* File accessed in perm check */ ··· 126 125 #define FAN_EVENT_INFO_TYPE_DFID_NAME 2 127 126 #define FAN_EVENT_INFO_TYPE_DFID 3 128 127 #define FAN_EVENT_INFO_TYPE_PIDFD 4 128 + #define FAN_EVENT_INFO_TYPE_ERROR 5 129 129 130 130 /* Variable length info record following event metadata */ 131 131 struct fanotify_event_info_header { ··· 159 157 struct fanotify_event_info_pidfd { 160 158 struct fanotify_event_info_header hdr; 161 159 __s32 pidfd; 160 + }; 161 + 162 + struct fanotify_event_info_error { 163 + struct fanotify_event_info_header hdr; 164 + __s32 error; 165 + __u32 error_count; 162 166 }; 163 167 164 168 struct fanotify_response {
+1 -2
kernel/audit_fsnotify.c
··· 160 160 161 161 audit_mark = container_of(inode_mark, struct audit_fsnotify_mark, mark); 162 162 163 - if (WARN_ON_ONCE(inode_mark->group != audit_fsnotify_group) || 164 - WARN_ON_ONCE(!inode)) 163 + if (WARN_ON_ONCE(inode_mark->group != audit_fsnotify_group)) 165 164 return 0; 166 165 167 166 if (mask & (FS_CREATE|FS_MOVED_TO|FS_DELETE|FS_MOVED_FROM)) {
+1 -2
kernel/audit_watch.c
··· 473 473 474 474 parent = container_of(inode_mark, struct audit_parent, mark); 475 475 476 - if (WARN_ON_ONCE(inode_mark->group != audit_watch_group) || 477 - WARN_ON_ONCE(!inode)) 476 + if (WARN_ON_ONCE(inode_mark->group != audit_watch_group)) 478 477 return 0; 479 478 480 479 if (mask & (FS_CREATE|FS_MOVED_TO) && inode)
+9
samples/Kconfig
··· 120 120 with it. 121 121 See also Documentation/driver-api/connector.rst 122 122 123 + config SAMPLE_FANOTIFY_ERROR 124 + bool "Build fanotify error monitoring sample" 125 + depends on FANOTIFY && CC_CAN_LINK && HEADERS_INSTALL 126 + help 127 + When enabled, this builds an example code that uses the 128 + FAN_FS_ERROR fanotify mechanism to monitor filesystem 129 + errors. 130 + See also Documentation/admin-guide/filesystem-monitoring.rst. 131 + 123 132 config SAMPLE_HIDRAW 124 133 bool "hidraw sample" 125 134 depends on CC_CAN_LINK && HEADERS_INSTALL
+1
samples/Makefile
··· 5 5 subdir-$(CONFIG_SAMPLE_ANDROID_BINDERFS) += binderfs 6 6 obj-$(CONFIG_SAMPLE_CONFIGFS) += configfs/ 7 7 obj-$(CONFIG_SAMPLE_CONNECTOR) += connector/ 8 + obj-$(CONFIG_SAMPLE_FANOTIFY_ERROR) += fanotify/ 8 9 subdir-$(CONFIG_SAMPLE_HIDRAW) += hidraw 9 10 obj-$(CONFIG_SAMPLE_HW_BREAKPOINT) += hw_breakpoint/ 10 11 obj-$(CONFIG_SAMPLE_KDB) += kdb/
+5
samples/fanotify/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + userprogs-always-y += fs-monitor 3 + 4 + userccflags += -I usr/include -Wall 5 +
+142
samples/fanotify/fs-monitor.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright 2021, Collabora Ltd. 4 + */ 5 + 6 + #define _GNU_SOURCE 7 + #include <errno.h> 8 + #include <err.h> 9 + #include <stdlib.h> 10 + #include <stdio.h> 11 + #include <fcntl.h> 12 + #include <sys/fanotify.h> 13 + #include <sys/types.h> 14 + #include <unistd.h> 15 + 16 + #ifndef FAN_FS_ERROR 17 + #define FAN_FS_ERROR 0x00008000 18 + #define FAN_EVENT_INFO_TYPE_ERROR 5 19 + 20 + struct fanotify_event_info_error { 21 + struct fanotify_event_info_header hdr; 22 + __s32 error; 23 + __u32 error_count; 24 + }; 25 + #endif 26 + 27 + #ifndef FILEID_INO32_GEN 28 + #define FILEID_INO32_GEN 1 29 + #endif 30 + 31 + #ifndef FILEID_INVALID 32 + #define FILEID_INVALID 0xff 33 + #endif 34 + 35 + static void print_fh(struct file_handle *fh) 36 + { 37 + int i; 38 + uint32_t *h = (uint32_t *) fh->f_handle; 39 + 40 + printf("\tfh: "); 41 + for (i = 0; i < fh->handle_bytes; i++) 42 + printf("%hhx", fh->f_handle[i]); 43 + printf("\n"); 44 + 45 + printf("\tdecoded fh: "); 46 + if (fh->handle_type == FILEID_INO32_GEN) 47 + printf("inode=%u gen=%u\n", h[0], h[1]); 48 + else if (fh->handle_type == FILEID_INVALID && !fh->handle_bytes) 49 + printf("Type %d (Superblock error)\n", fh->handle_type); 50 + else 51 + printf("Type %d (Unknown)\n", fh->handle_type); 52 + 53 + } 54 + 55 + static void handle_notifications(char *buffer, int len) 56 + { 57 + struct fanotify_event_metadata *event = 58 + (struct fanotify_event_metadata *) buffer; 59 + struct fanotify_event_info_header *info; 60 + struct fanotify_event_info_error *err; 61 + struct fanotify_event_info_fid *fid; 62 + int off; 63 + 64 + for (; FAN_EVENT_OK(event, len); event = FAN_EVENT_NEXT(event, len)) { 65 + 66 + if (event->mask != FAN_FS_ERROR) { 67 + printf("unexpected FAN MARK: %llx\n", 68 + (unsigned long long)event->mask); 69 + goto next_event; 70 + } 71 + 72 + if (event->fd != FAN_NOFD) { 73 + printf("Unexpected fd (!= FAN_NOFD)\n"); 74 + goto next_event; 75 + } 76 + 77 + printf("FAN_FS_ERROR (len=%d)\n", event->event_len); 78 + 79 + for (off = sizeof(*event) ; off < event->event_len; 80 + off += info->len) { 81 + info = (struct fanotify_event_info_header *) 82 + ((char *) event + off); 83 + 84 + switch (info->info_type) { 85 + case FAN_EVENT_INFO_TYPE_ERROR: 86 + err = (struct fanotify_event_info_error *) info; 87 + 88 + printf("\tGeneric Error Record: len=%d\n", 89 + err->hdr.len); 90 + printf("\terror: %d\n", err->error); 91 + printf("\terror_count: %d\n", err->error_count); 92 + break; 93 + 94 + case FAN_EVENT_INFO_TYPE_FID: 95 + fid = (struct fanotify_event_info_fid *) info; 96 + 97 + printf("\tfsid: %x%x\n", 98 + fid->fsid.val[0], fid->fsid.val[1]); 99 + print_fh((struct file_handle *) &fid->handle); 100 + break; 101 + 102 + default: 103 + printf("\tUnknown info type=%d len=%d:\n", 104 + info->info_type, info->len); 105 + } 106 + } 107 + next_event: 108 + printf("---\n\n"); 109 + } 110 + } 111 + 112 + int main(int argc, char **argv) 113 + { 114 + int fd; 115 + 116 + char buffer[BUFSIZ]; 117 + 118 + if (argc < 2) { 119 + printf("Missing path argument\n"); 120 + return 1; 121 + } 122 + 123 + fd = fanotify_init(FAN_CLASS_NOTIF|FAN_REPORT_FID, O_RDONLY); 124 + if (fd < 0) 125 + errx(1, "fanotify_init"); 126 + 127 + if (fanotify_mark(fd, FAN_MARK_ADD|FAN_MARK_FILESYSTEM, 128 + FAN_FS_ERROR, AT_FDCWD, argv[1])) { 129 + errx(1, "fanotify_mark"); 130 + } 131 + 132 + while (1) { 133 + int n = read(fd, buffer, BUFSIZ); 134 + 135 + if (n < 0) 136 + errx(1, "read"); 137 + 138 + handle_notifications(buffer, n); 139 + } 140 + 141 + return 0; 142 + }