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

fanotify: notify on mount attach and detach

Add notifications for attaching and detaching mounts. The following new
event masks are added:

FAN_MNT_ATTACH - Mount was attached
FAN_MNT_DETACH - Mount was detached

If a mount is moved, then the event is reported with (FAN_MNT_ATTACH |
FAN_MNT_DETACH).

These events add an info record of type FAN_EVENT_INFO_TYPE_MNT containing
these fields identifying the affected mounts:

__u64 mnt_id - the ID of the mount (see statmount(2))

FAN_REPORT_MNT must be supplied to fanotify_init() to receive these events
and no other type of event can be received with this report type.

Marks are added with FAN_MARK_MNTNS, which records the mount namespace from
an nsfs file (e.g. /proc/self/ns/mnt).

Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Link: https://lore.kernel.org/r/20250129165803.72138-3-mszeredi@redhat.com
Signed-off-by: Christian Brauner <brauner@kernel.org>

authored by

Miklos Szeredi and committed by
Christian Brauner
0f46d81f b944249b

+164 -24
+2
fs/mount.h
··· 181 181 { 182 182 return container_of(ns, struct mnt_namespace, ns); 183 183 } 184 + 185 + struct mnt_namespace *mnt_ns_from_dentry(struct dentry *dentry);
+11 -3
fs/namespace.c
··· 2145 2145 } 2146 2146 } 2147 2147 2148 + struct mnt_namespace *mnt_ns_from_dentry(struct dentry *dentry) 2149 + { 2150 + if (!is_mnt_ns_file(dentry)) 2151 + return NULL; 2152 + 2153 + return to_mnt_ns(get_proc_ns(dentry->d_inode)); 2154 + } 2155 + 2148 2156 static bool mnt_ns_loop(struct dentry *dentry) 2149 2157 { 2150 2158 /* Could bind mounting the mount namespace inode cause a 2151 2159 * mount namespace loop? 2152 2160 */ 2153 - struct mnt_namespace *mnt_ns; 2154 - if (!is_mnt_ns_file(dentry)) 2161 + struct mnt_namespace *mnt_ns = mnt_ns_from_dentry(dentry); 2162 + 2163 + if (!mnt_ns) 2155 2164 return false; 2156 2165 2157 - mnt_ns = to_mnt_ns(get_proc_ns(dentry->d_inode)); 2158 2166 return current->nsproxy->mnt_ns->seq >= mnt_ns->seq; 2159 2167 } 2160 2168
+35 -3
fs/notify/fanotify/fanotify.c
··· 166 166 case FANOTIFY_EVENT_TYPE_FS_ERROR: 167 167 return fanotify_error_event_equal(FANOTIFY_EE(old), 168 168 FANOTIFY_EE(new)); 169 + case FANOTIFY_EVENT_TYPE_MNT: 170 + return false; 169 171 default: 170 172 WARN_ON_ONCE(1); 171 173 } ··· 314 312 pr_debug("%s: report_mask=%x mask=%x data=%p data_type=%d\n", 315 313 __func__, iter_info->report_mask, event_mask, data, data_type); 316 314 317 - if (!fid_mode) { 315 + if (FAN_GROUP_FLAG(group, FAN_REPORT_MNT)) { 316 + if (data_type != FSNOTIFY_EVENT_MNT) 317 + return 0; 318 + } else if (!fid_mode) { 318 319 /* Do we have path to open a file descriptor? */ 319 320 if (!path) 320 321 return 0; ··· 562 557 return &pevent->fae; 563 558 } 564 559 560 + static struct fanotify_event *fanotify_alloc_mnt_event(u64 mnt_id, gfp_t gfp) 561 + { 562 + struct fanotify_mnt_event *pevent; 563 + 564 + pevent = kmem_cache_alloc(fanotify_mnt_event_cachep, gfp); 565 + if (!pevent) 566 + return NULL; 567 + 568 + pevent->fae.type = FANOTIFY_EVENT_TYPE_MNT; 569 + pevent->mnt_id = mnt_id; 570 + 571 + return &pevent->fae; 572 + } 573 + 565 574 static struct fanotify_event *fanotify_alloc_perm_event(const void *data, 566 575 int data_type, 567 576 gfp_t gfp) ··· 750 731 fid_mode); 751 732 struct inode *dirid = fanotify_dfid_inode(mask, data, data_type, dir); 752 733 const struct path *path = fsnotify_data_path(data, data_type); 734 + u64 mnt_id = fsnotify_data_mnt_id(data, data_type); 753 735 struct mem_cgroup *old_memcg; 754 736 struct dentry *moved = NULL; 755 737 struct inode *child = NULL; ··· 846 826 moved, &hash, gfp); 847 827 } else if (fid_mode) { 848 828 event = fanotify_alloc_fid_event(id, fsid, &hash, gfp); 849 - } else { 829 + } else if (path) { 850 830 event = fanotify_alloc_path_event(path, &hash, gfp); 831 + } else if (mnt_id) { 832 + event = fanotify_alloc_mnt_event(mnt_id, gfp); 833 + } else { 834 + WARN_ON_ONCE(1); 851 835 } 852 836 853 837 if (!event) ··· 951 927 BUILD_BUG_ON(FAN_RENAME != FS_RENAME); 952 928 BUILD_BUG_ON(FAN_PRE_ACCESS != FS_PRE_ACCESS); 953 929 954 - BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 22); 930 + BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 24); 955 931 956 932 mask = fanotify_group_event_mask(group, iter_info, &match_mask, 957 933 mask, data, data_type, dir); ··· 1052 1028 mempool_free(fee, &group->fanotify_data.error_events_pool); 1053 1029 } 1054 1030 1031 + static void fanotify_free_mnt_event(struct fanotify_event *event) 1032 + { 1033 + kmem_cache_free(fanotify_mnt_event_cachep, FANOTIFY_ME(event)); 1034 + } 1035 + 1055 1036 static void fanotify_free_event(struct fsnotify_group *group, 1056 1037 struct fsnotify_event *fsn_event) 1057 1038 { ··· 1082 1053 break; 1083 1054 case FANOTIFY_EVENT_TYPE_FS_ERROR: 1084 1055 fanotify_free_error_event(group, event); 1056 + break; 1057 + case FANOTIFY_EVENT_TYPE_MNT: 1058 + fanotify_free_mnt_event(event); 1085 1059 break; 1086 1060 default: 1087 1061 WARN_ON_ONCE(1);
+18
fs/notify/fanotify/fanotify.h
··· 9 9 extern struct kmem_cache *fanotify_fid_event_cachep; 10 10 extern struct kmem_cache *fanotify_path_event_cachep; 11 11 extern struct kmem_cache *fanotify_perm_event_cachep; 12 + extern struct kmem_cache *fanotify_mnt_event_cachep; 12 13 13 14 /* Possible states of the permission event */ 14 15 enum { ··· 245 244 FANOTIFY_EVENT_TYPE_PATH_PERM, 246 245 FANOTIFY_EVENT_TYPE_OVERFLOW, /* struct fanotify_event */ 247 246 FANOTIFY_EVENT_TYPE_FS_ERROR, /* struct fanotify_error_event */ 247 + FANOTIFY_EVENT_TYPE_MNT, 248 248 __FANOTIFY_EVENT_TYPE_NUM 249 249 }; 250 250 ··· 411 409 struct path path; 412 410 }; 413 411 412 + struct fanotify_mnt_event { 413 + struct fanotify_event fae; 414 + u64 mnt_id; 415 + }; 416 + 414 417 static inline struct fanotify_path_event * 415 418 FANOTIFY_PE(struct fanotify_event *event) 416 419 { 417 420 return container_of(event, struct fanotify_path_event, fae); 421 + } 422 + 423 + static inline struct fanotify_mnt_event * 424 + FANOTIFY_ME(struct fanotify_event *event) 425 + { 426 + return container_of(event, struct fanotify_mnt_event, fae); 418 427 } 419 428 420 429 /* ··· 477 464 static inline bool fanotify_is_error_event(u32 mask) 478 465 { 479 466 return mask & FAN_FS_ERROR; 467 + } 468 + 469 + static inline bool fanotify_is_mnt_event(u32 mask) 470 + { 471 + return mask & (FAN_MNT_ATTACH | FAN_MNT_DETACH); 480 472 } 481 473 482 474 static inline const struct path *fanotify_event_path(struct fanotify_event *event)
+75 -14
fs/notify/fanotify/fanotify_user.c
··· 113 113 struct kmem_cache *fanotify_fid_event_cachep __ro_after_init; 114 114 struct kmem_cache *fanotify_path_event_cachep __ro_after_init; 115 115 struct kmem_cache *fanotify_perm_event_cachep __ro_after_init; 116 + struct kmem_cache *fanotify_mnt_event_cachep __ro_after_init; 116 117 117 118 #define FANOTIFY_EVENT_ALIGN 4 118 119 #define FANOTIFY_FID_INFO_HDR_LEN \ ··· 124 123 (sizeof(struct fanotify_event_info_error)) 125 124 #define FANOTIFY_RANGE_INFO_LEN \ 126 125 (sizeof(struct fanotify_event_info_range)) 126 + #define FANOTIFY_MNT_INFO_LEN \ 127 + (sizeof(struct fanotify_event_info_mnt)) 127 128 128 129 static int fanotify_fid_info_len(int fh_len, int name_len) 129 130 { ··· 181 178 fh_len = fanotify_event_object_fh_len(event); 182 179 event_len += fanotify_fid_info_len(fh_len, dot_len); 183 180 } 181 + if (fanotify_is_mnt_event(event->mask)) 182 + event_len += FANOTIFY_MNT_INFO_LEN; 184 183 185 184 if (info_mode & FAN_REPORT_PIDFD) 186 185 event_len += FANOTIFY_PIDFD_INFO_LEN; ··· 408 403 spin_unlock(&group->notification_lock); 409 404 410 405 return -ENOENT; 406 + } 407 + 408 + static size_t copy_mnt_info_to_user(struct fanotify_event *event, 409 + char __user *buf, int count) 410 + { 411 + struct fanotify_event_info_mnt info = { }; 412 + 413 + info.hdr.info_type = FAN_EVENT_INFO_TYPE_MNT; 414 + info.hdr.len = FANOTIFY_MNT_INFO_LEN; 415 + 416 + if (WARN_ON(count < info.hdr.len)) 417 + return -EFAULT; 418 + 419 + info.mnt_id = FANOTIFY_ME(event)->mnt_id; 420 + 421 + if (copy_to_user(buf, &info, sizeof(info))) 422 + return -EFAULT; 423 + 424 + return info.hdr.len; 411 425 } 412 426 413 427 static size_t copy_error_info_to_user(struct fanotify_event *event, ··· 717 693 718 694 if (fanotify_event_has_access_range(event)) { 719 695 ret = copy_range_info_to_user(event, buf, count); 696 + if (ret < 0) 697 + return ret; 698 + buf += ret; 699 + count -= ret; 700 + total_bytes += ret; 701 + } 702 + 703 + if (fanotify_is_mnt_event(event->mask)) { 704 + ret = copy_mnt_info_to_user(event, buf, count); 720 705 if (ret < 0) 721 706 return ret; 722 707 buf += ret; ··· 1541 1508 if ((flags & FAN_REPORT_PIDFD) && (flags & FAN_REPORT_TID)) 1542 1509 return -EINVAL; 1543 1510 1511 + /* Don't allow mixing mnt events with inode events for now */ 1512 + if (flags & FAN_REPORT_MNT) { 1513 + if (class != FAN_CLASS_NOTIF) 1514 + return -EINVAL; 1515 + if (flags & (FANOTIFY_FID_BITS | FAN_REPORT_FD_ERROR)) 1516 + return -EINVAL; 1517 + } 1518 + 1544 1519 if (event_f_flags & ~FANOTIFY_INIT_ALL_EVENT_F_BITS) 1545 1520 return -EINVAL; 1546 1521 ··· 1808 1767 int dfd, const char __user *pathname) 1809 1768 { 1810 1769 struct inode *inode = NULL; 1811 - struct vfsmount *mnt = NULL; 1812 1770 struct fsnotify_group *group; 1813 1771 struct path path; 1814 1772 struct fan_fsid __fsid, *fsid = NULL; ··· 1816 1776 unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS; 1817 1777 unsigned int ignore = flags & FANOTIFY_MARK_IGNORE_BITS; 1818 1778 unsigned int obj_type, fid_mode; 1819 - void *obj; 1779 + void *obj = NULL; 1820 1780 u32 umask = 0; 1821 1781 int ret; 1822 1782 ··· 1839 1799 break; 1840 1800 case FAN_MARK_FILESYSTEM: 1841 1801 obj_type = FSNOTIFY_OBJ_TYPE_SB; 1802 + break; 1803 + case FAN_MARK_MNTNS: 1804 + obj_type = FSNOTIFY_OBJ_TYPE_MNTNS; 1842 1805 break; 1843 1806 default: 1844 1807 return -EINVAL; ··· 1890 1847 return -EINVAL; 1891 1848 group = fd_file(f)->private_data; 1892 1849 1850 + /* Only report mount events on mnt namespace */ 1851 + if (FAN_GROUP_FLAG(group, FAN_REPORT_MNT)) { 1852 + if (mask & ~FANOTIFY_MOUNT_EVENTS) 1853 + return -EINVAL; 1854 + if (mark_type != FAN_MARK_MNTNS) 1855 + return -EINVAL; 1856 + } else { 1857 + if (mask & FANOTIFY_MOUNT_EVENTS) 1858 + return -EINVAL; 1859 + if (mark_type == FAN_MARK_MNTNS) 1860 + return -EINVAL; 1861 + } 1862 + 1893 1863 /* 1894 1864 * An unprivileged user is not allowed to setup mount nor filesystem 1895 1865 * marks. This also includes setting up such marks by a group that ··· 1944 1888 * point. 1945 1889 */ 1946 1890 fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS); 1947 - if (mask & ~(FANOTIFY_FD_EVENTS|FANOTIFY_EVENT_FLAGS) && 1891 + if (mask & ~(FANOTIFY_FD_EVENTS|FANOTIFY_MOUNT_EVENTS|FANOTIFY_EVENT_FLAGS) && 1948 1892 (!fid_mode || mark_type == FAN_MARK_MOUNT)) 1949 1893 return -EINVAL; 1950 1894 ··· 1994 1938 } 1995 1939 1996 1940 /* inode held in place by reference to path; group by fget on fd */ 1997 - if (mark_type == FAN_MARK_INODE) { 1941 + if (obj_type == FSNOTIFY_OBJ_TYPE_INODE) { 1998 1942 inode = path.dentry->d_inode; 1999 1943 obj = inode; 2000 - } else { 2001 - mnt = path.mnt; 2002 - if (mark_type == FAN_MARK_MOUNT) 2003 - obj = mnt; 2004 - else 2005 - obj = mnt->mnt_sb; 1944 + } else if (obj_type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) { 1945 + obj = path.mnt; 1946 + } else if (obj_type == FSNOTIFY_OBJ_TYPE_SB) { 1947 + obj = path.mnt->mnt_sb; 1948 + } else if (obj_type == FSNOTIFY_OBJ_TYPE_MNTNS) { 1949 + obj = mnt_ns_from_dentry(path.dentry); 2006 1950 } 1951 + 1952 + ret = -EINVAL; 1953 + if (!obj) 1954 + goto path_put_and_out; 2007 1955 2008 1956 /* 2009 1957 * If some other task has this inode open for write we should not add ··· 2016 1956 */ 2017 1957 if (mark_cmd == FAN_MARK_ADD && (flags & FANOTIFY_MARK_IGNORE_BITS) && 2018 1958 !(flags & FAN_MARK_IGNORED_SURV_MODIFY)) { 2019 - ret = mnt ? -EINVAL : -EISDIR; 1959 + ret = !inode ? -EINVAL : -EISDIR; 2020 1960 /* FAN_MARK_IGNORE requires SURV_MODIFY for sb/mount/dir marks */ 2021 1961 if (ignore == FAN_MARK_IGNORE && 2022 - (mnt || S_ISDIR(inode->i_mode))) 1962 + (!inode || S_ISDIR(inode->i_mode))) 2023 1963 goto path_put_and_out; 2024 1964 2025 1965 ret = 0; ··· 2028 1968 } 2029 1969 2030 1970 /* Mask out FAN_EVENT_ON_CHILD flag for sb/mount/non-dir marks */ 2031 - if (mnt || !S_ISDIR(inode->i_mode)) { 1971 + if (!inode || !S_ISDIR(inode->i_mode)) { 2032 1972 mask &= ~FAN_EVENT_ON_CHILD; 2033 1973 umask = FAN_EVENT_ON_CHILD; 2034 1974 /* ··· 2102 2042 FANOTIFY_DEFAULT_MAX_USER_MARKS); 2103 2043 2104 2044 BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS); 2105 - BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 13); 2045 + BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 14); 2106 2046 BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 11); 2107 2047 2108 2048 fanotify_mark_cache = KMEM_CACHE(fanotify_mark, ··· 2115 2055 fanotify_perm_event_cachep = 2116 2056 KMEM_CACHE(fanotify_perm_event, SLAB_PANIC); 2117 2057 } 2058 + fanotify_mnt_event_cachep = KMEM_CACHE(fanotify_mnt_event, SLAB_PANIC); 2118 2059 2119 2060 fanotify_max_queued_events = FANOTIFY_DEFAULT_MAX_EVENTS; 2120 2061 init_user_ns.ucount_max[UCOUNT_FANOTIFY_GROUPS] =
+5
fs/notify/fdinfo.c
··· 121 121 122 122 seq_printf(m, "fanotify sdev:%x mflags:%x mask:%x ignored_mask:%x\n", 123 123 sb->s_dev, mflags, mark->mask, mark->ignore_mask); 124 + } else if (mark->connector->type == FSNOTIFY_OBJ_TYPE_MNTNS) { 125 + struct mnt_namespace *mnt_ns = fsnotify_conn_mntns(mark->connector); 126 + 127 + seq_printf(m, "fanotify mnt_ns:%u mflags:%x mask:%x ignored_mask:%x\n", 128 + mnt_ns->ns.inum, mflags, mark->mask, mark->ignore_mask); 124 129 } 125 130 } 126 131
+8 -4
include/linux/fanotify.h
··· 25 25 26 26 #define FANOTIFY_FID_BITS (FAN_REPORT_DFID_NAME_TARGET) 27 27 28 - #define FANOTIFY_INFO_MODES (FANOTIFY_FID_BITS | FAN_REPORT_PIDFD) 28 + #define FANOTIFY_INFO_MODES (FANOTIFY_FID_BITS | FAN_REPORT_PIDFD | FAN_REPORT_MNT) 29 29 30 30 /* 31 31 * fanotify_init() flags that require CAP_SYS_ADMIN. ··· 38 38 FAN_REPORT_PIDFD | \ 39 39 FAN_REPORT_FD_ERROR | \ 40 40 FAN_UNLIMITED_QUEUE | \ 41 - FAN_UNLIMITED_MARKS) 41 + FAN_UNLIMITED_MARKS | \ 42 + FAN_REPORT_MNT) 42 43 43 44 /* 44 45 * fanotify_init() flags that are allowed for user without CAP_SYS_ADMIN. ··· 59 58 #define FANOTIFY_INTERNAL_GROUP_FLAGS (FANOTIFY_UNPRIV) 60 59 61 60 #define FANOTIFY_MARK_TYPE_BITS (FAN_MARK_INODE | FAN_MARK_MOUNT | \ 62 - FAN_MARK_FILESYSTEM) 61 + FAN_MARK_FILESYSTEM | FAN_MARK_MNTNS) 63 62 64 63 #define FANOTIFY_MARK_CMD_BITS (FAN_MARK_ADD | FAN_MARK_REMOVE | \ 65 64 FAN_MARK_FLUSH) ··· 110 109 /* Events that can only be reported with data type FSNOTIFY_EVENT_ERROR */ 111 110 #define FANOTIFY_ERROR_EVENTS (FAN_FS_ERROR) 112 111 112 + #define FANOTIFY_MOUNT_EVENTS (FAN_MNT_ATTACH | FAN_MNT_DETACH) 113 + 113 114 /* Events that user can request to be notified on */ 114 115 #define FANOTIFY_EVENTS (FANOTIFY_PATH_EVENTS | \ 115 116 FANOTIFY_INODE_EVENTS | \ 116 - FANOTIFY_ERROR_EVENTS) 117 + FANOTIFY_ERROR_EVENTS | \ 118 + FANOTIFY_MOUNT_EVENTS) 117 119 118 120 /* Extra flags that may be reported with event or control handling of events */ 119 121 #define FANOTIFY_EVENT_FLAGS (FAN_EVENT_ON_CHILD | FAN_ONDIR)
+10
include/uapi/linux/fanotify.h
··· 28 28 /* #define FAN_DIR_MODIFY 0x00080000 */ /* Deprecated (reserved) */ 29 29 30 30 #define FAN_PRE_ACCESS 0x00100000 /* Pre-content access hook */ 31 + #define FAN_MNT_ATTACH 0x01000000 /* Mount was attached */ 32 + #define FAN_MNT_DETACH 0x02000000 /* Mount was detached */ 31 33 32 34 #define FAN_EVENT_ON_CHILD 0x08000000 /* Interested in child events */ 33 35 ··· 66 64 #define FAN_REPORT_NAME 0x00000800 /* Report events with name */ 67 65 #define FAN_REPORT_TARGET_FID 0x00001000 /* Report dirent target id */ 68 66 #define FAN_REPORT_FD_ERROR 0x00002000 /* event->fd can report error */ 67 + #define FAN_REPORT_MNT 0x00004000 /* Report mount events */ 69 68 70 69 /* Convenience macro - FAN_REPORT_NAME requires FAN_REPORT_DIR_FID */ 71 70 #define FAN_REPORT_DFID_NAME (FAN_REPORT_DIR_FID | FAN_REPORT_NAME) ··· 97 94 #define FAN_MARK_INODE 0x00000000 98 95 #define FAN_MARK_MOUNT 0x00000010 99 96 #define FAN_MARK_FILESYSTEM 0x00000100 97 + #define FAN_MARK_MNTNS 0x00000110 100 98 101 99 /* 102 100 * Convenience macro - FAN_MARK_IGNORE requires FAN_MARK_IGNORED_SURV_MODIFY ··· 151 147 #define FAN_EVENT_INFO_TYPE_PIDFD 4 152 148 #define FAN_EVENT_INFO_TYPE_ERROR 5 153 149 #define FAN_EVENT_INFO_TYPE_RANGE 6 150 + #define FAN_EVENT_INFO_TYPE_MNT 7 154 151 155 152 /* Special info types for FAN_RENAME */ 156 153 #define FAN_EVENT_INFO_TYPE_OLD_DFID_NAME 10 ··· 203 198 __u32 pad; 204 199 __u64 offset; 205 200 __u64 count; 201 + }; 202 + 203 + struct fanotify_event_info_mnt { 204 + struct fanotify_event_info_header hdr; 205 + __u64 mnt_id; 206 206 }; 207 207 208 208 /*