Pull vfs mount updates from Christian Brauner: - Mount notifications The day has come where we finally provide a new api to listen for mount topology changes outside of /proc/<pid>/mountinfo. A mount namespace file descriptor can be supplied and registered with fanotify to listen for mount topology changes. Currently notifications for mount, umount and moving mounts are generated. The generated notification record contains the unique mount id of the mount. The listmount() and statmount() api can be used to query detailed information about the mount using the received unique mount id. This allows userspace to figure out exactly how the mount topology changed without having to generating diffs of /proc/<pid>/mountinfo in userspace. - Support O_PATH file descriptors with FSCONFIG_SET_FD in the new mount api - Support detached mounts in overlayfs Since last cycle we support specifying overlayfs layers via file descriptors. However, we don't allow detached mounts which means userspace cannot user file descriptors received via open_tree(OPEN_TREE_CLONE) and fsmount() directly. They have to attach them to a mount namespace via move_mount() first. This is cumbersome and means they have to undo mounts via umount(). Allow them to directly use detached mounts. - Allow to retrieve idmappings with statmount Currently it isn't possible to figure out what idmapping has been attached to an idmapped mount. Add an extension to statmount() which allows to read the idmapping from the mount. - Allow creating idmapped mounts from mounts that are already idmapped So far it isn't possible to allow the creation of idmapped mounts from already idmapped mounts as this has significant lifetime implications. Make the creation of idmapped mounts atomic by allow to pass struct mount_attr together with the open_tree_attr() system call allowing to solve these issues without complicating VFS lookup in any way. The system call has in general the benefit that creating a detached mount and applying mount attributes to it becomes an atomic operation for userspace. - Add a way to query statmount() for supported options Allow userspace to query which mount information can be retrieved through statmount(). - Allow superblock owners to force unmount * tag 'vfs-6.15-rc1.mount' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs: (21 commits) umount: Allow superblock owners to force umount selftests: add tests for mount notification selinux: add FILE__WATCH_MOUNTNS samples/vfs: fix printf format string for size_t fs: allow changing idmappings fs: add kflags member to struct mount_kattr fs: add open_tree_attr() fs: add copy_mount_setattr() helper fs: add vfs_open_tree() helper statmount: add a new supported_mask field samples/vfs: add STATMOUNT_MNT_{G,U}IDMAP selftests: add tests for using detached mount with overlayfs samples/vfs: check whether flag was raised statmount: allow to retrieve idmappings uidgid: add map_id_range_up() fs: allow detached mounts in clone_private_mount() selftests/overlayfs: test specifying layers as O_PATH file descriptors fs: support O_PATH fds with FSCONFIG_SET_FD vfs: add notifications for mount attach and detach fanotify: notify on mount attach and detach ...
Unified · Split
| @@ -506,3 +506,4 @@ · 505 unmodified lines | |||
| 506 | 574 common getxattrat sys_getxattrat | 506 | 574 common getxattrat sys_getxattrat |
| 507 | 575 common listxattrat sys_listxattrat | 507 | 575 common listxattrat sys_listxattrat |
| 508 | 576 common removexattrat sys_removexattrat | 508 | 576 common removexattrat sys_removexattrat |
| 509 | 577 common open_tree_attr sys_open_tree_attr | ||
| @@ -481,3 +481,4 @@ · 480 unmodified lines | |||
| 481 | 464 common getxattrat sys_getxattrat | 481 | 464 common getxattrat sys_getxattrat |
| 482 | 465 common listxattrat sys_listxattrat | 482 | 465 common listxattrat sys_listxattrat |
| 483 | 466 common removexattrat sys_removexattrat | 483 | 466 common removexattrat sys_removexattrat |
| 484 | 467 common open_tree_attr sys_open_tree_attr | ||
| @@ -478,3 +478,4 @@ · 477 unmodified lines | |||
| 478 | 464 common getxattrat sys_getxattrat | 478 | 464 common getxattrat sys_getxattrat |
| 479 | 465 common listxattrat sys_listxattrat | 479 | 465 common listxattrat sys_listxattrat |
| 480 | 466 common removexattrat sys_removexattrat | 480 | 466 common removexattrat sys_removexattrat |
| 481 | 467 common open_tree_attr sys_open_tree_attr | ||
| @@ -466,3 +466,4 @@ · 465 unmodified lines | |||
| 466 | 464 common getxattrat sys_getxattrat | 466 | 464 common getxattrat sys_getxattrat |
| 467 | 465 common listxattrat sys_listxattrat | 467 | 465 common listxattrat sys_listxattrat |
| 468 | 466 common removexattrat sys_removexattrat | 468 | 466 common removexattrat sys_removexattrat |
| 469 | 467 common open_tree_attr sys_open_tree_attr | ||
| @@ -472,3 +472,4 @@ · 471 unmodified lines | |||
| 472 | 464 common getxattrat sys_getxattrat | 472 | 464 common getxattrat sys_getxattrat |
| 473 | 465 common listxattrat sys_listxattrat | 473 | 465 common listxattrat sys_listxattrat |
| 474 | 466 common removexattrat sys_removexattrat | 474 | 466 common removexattrat sys_removexattrat |
| 475 | 467 common open_tree_attr sys_open_tree_attr | ||
| @@ -405,3 +405,4 @@ · 404 unmodified lines | |||
| 405 | 464 n32 getxattrat sys_getxattrat | 405 | 464 n32 getxattrat sys_getxattrat |
| 406 | 465 n32 listxattrat sys_listxattrat | 406 | 465 n32 listxattrat sys_listxattrat |
| 407 | 466 n32 removexattrat sys_removexattrat | 407 | 466 n32 removexattrat sys_removexattrat |
| 408 | 467 n32 open_tree_attr sys_open_tree_attr | ||
| @@ -381,3 +381,4 @@ · 380 unmodified lines | |||
| 381 | 464 n64 getxattrat sys_getxattrat | 381 | 464 n64 getxattrat sys_getxattrat |
| 382 | 465 n64 listxattrat sys_listxattrat | 382 | 465 n64 listxattrat sys_listxattrat |
| 383 | 466 n64 removexattrat sys_removexattrat | 383 | 466 n64 removexattrat sys_removexattrat |
| 384 | 467 n64 open_tree_attr sys_open_tree_attr | ||
| @@ -454,3 +454,4 @@ · 453 unmodified lines | |||
| 454 | 464 o32 getxattrat sys_getxattrat | 454 | 464 o32 getxattrat sys_getxattrat |
| 455 | 465 o32 listxattrat sys_listxattrat | 455 | 465 o32 listxattrat sys_listxattrat |
| 456 | 466 o32 removexattrat sys_removexattrat | 456 | 466 o32 removexattrat sys_removexattrat |
| 457 | 467 o32 open_tree_attr sys_open_tree_attr | ||
| @@ -465,3 +465,4 @@ · 464 unmodified lines | |||
| 465 | 464 common getxattrat sys_getxattrat | 465 | 464 common getxattrat sys_getxattrat |
| 466 | 465 common listxattrat sys_listxattrat | 466 | 465 common listxattrat sys_listxattrat |
| 467 | 466 common removexattrat sys_removexattrat | 467 | 466 common removexattrat sys_removexattrat |
| 468 | 467 common open_tree_attr sys_open_tree_attr | ||
| @@ -557,3 +557,4 @@ · 556 unmodified lines | |||
| 557 | 464 common getxattrat sys_getxattrat | 557 | 464 common getxattrat sys_getxattrat |
| 558 | 465 common listxattrat sys_listxattrat | 558 | 465 common listxattrat sys_listxattrat |
| 559 | 466 common removexattrat sys_removexattrat | 559 | 466 common removexattrat sys_removexattrat |
| 560 | 467 common open_tree_attr sys_open_tree_attr | ||
| @@ -469,3 +469,4 @@ · 468 unmodified lines | |||
| 469 | 464 common getxattrat sys_getxattrat sys_getxattrat | 469 | 464 common getxattrat sys_getxattrat sys_getxattrat |
| 470 | 465 common listxattrat sys_listxattrat sys_listxattrat | 470 | 465 common listxattrat sys_listxattrat sys_listxattrat |
| 471 | 466 common removexattrat sys_removexattrat sys_removexattrat | 471 | 466 common removexattrat sys_removexattrat sys_removexattrat |
| 472 | 467 common open_tree_attr sys_open_tree_attr sys_open_tree_attr | ||
| @@ -470,3 +470,4 @@ · 469 unmodified lines | |||
| 470 | 464 common getxattrat sys_getxattrat | 470 | 464 common getxattrat sys_getxattrat |
| 471 | 465 common listxattrat sys_listxattrat | 471 | 465 common listxattrat sys_listxattrat |
| 472 | 466 common removexattrat sys_removexattrat | 472 | 466 common removexattrat sys_removexattrat |
| 473 | 467 common open_tree_attr sys_open_tree_attr | ||
| @@ -512,3 +512,4 @@ · 511 unmodified lines | |||
| 512 | 464 common getxattrat sys_getxattrat | 512 | 464 common getxattrat sys_getxattrat |
| 513 | 465 common listxattrat sys_listxattrat | 513 | 465 common listxattrat sys_listxattrat |
| 514 | 466 common removexattrat sys_removexattrat | 514 | 466 common removexattrat sys_removexattrat |
| 515 | 467 common open_tree_attr sys_open_tree_attr | ||
| @@ -472,3 +472,4 @@ · 471 unmodified lines | |||
| 472 | 464 i386 getxattrat sys_getxattrat | 472 | 464 i386 getxattrat sys_getxattrat |
| 473 | 465 i386 listxattrat sys_listxattrat | 473 | 465 i386 listxattrat sys_listxattrat |
| 474 | 466 i386 removexattrat sys_removexattrat | 474 | 466 i386 removexattrat sys_removexattrat |
| 475 | 467 i386 open_tree_attr sys_open_tree_attr | ||
| @@ -390,6 +390,7 @@ · 389 unmodified lines | |||
| 390 | 464 common getxattrat sys_getxattrat | 390 | 464 common getxattrat sys_getxattrat |
| 391 | 465 common listxattrat sys_listxattrat | 391 | 465 common listxattrat sys_listxattrat |
| 392 | 466 common removexattrat sys_removexattrat | 392 | 466 common removexattrat sys_removexattrat |
| 393 | 467 common open_tree_attr sys_open_tree_attr | ||
| 393 | 394 | ||
| 394 | # | 395 | # |
| 395 | # Due to a historical design error, certain syscalls are numbered differently | 396 | # Due to a historical design error, certain syscalls are numbered differently |
| @@ -437,3 +437,4 @@ · 436 unmodified lines | |||
| 437 | 464 common getxattrat sys_getxattrat | 437 | 464 common getxattrat sys_getxattrat |
| 438 | 465 common listxattrat sys_listxattrat | 438 | 465 common listxattrat sys_listxattrat |
| 439 | 466 common removexattrat sys_removexattrat | 439 | 466 common removexattrat sys_removexattrat |
| 440 | 467 common open_tree_attr sys_open_tree_attr | ||
| @@ -218,6 +218,8 @@ void autofs_clean_ino(struct autofs_info *); · 217 unmodified lines | |||
| 218 | 218 | ||
| 219 | static inline int autofs_check_pipe(struct file *pipe) | 219 | static inline int autofs_check_pipe(struct file *pipe) |
| 220 | { | 220 | { |
| 221 | if (pipe->f_mode & FMODE_PATH) | ||
| 222 | return -EINVAL; | ||
| 221 | if (!(pipe->f_mode & FMODE_CAN_WRITE)) | 223 | if (!(pipe->f_mode & FMODE_CAN_WRITE)) |
| 222 | return -EINVAL; | 224 | return -EINVAL; |
| 223 | if (!S_ISFIFO(file_inode(pipe)->i_mode)) | 225 | if (!S_ISFIFO(file_inode(pipe)->i_mode)) |
| @@ -453,7 +453,7 @@ SYSCALL_DEFINE5(fsconfig, · 452 unmodified lines | |||
| 453 | case FSCONFIG_SET_FD: | 453 | case FSCONFIG_SET_FD: |
| 454 | param.type = fs_value_is_file; | 454 | param.type = fs_value_is_file; |
| 455 | ret = -EBADF; | 455 | ret = -EBADF; |
| 456 | param.file = fget(aux); | 456 | param.file = fget_raw(aux); |
| 457 | if (!param.file) | 457 | if (!param.file) |
| 458 | goto out_key; | 458 | goto out_key; |
| 459 | param.dirfd = aux; | 459 | param.dirfd = aux; |
| @@ -338,3 +338,4 @@ static inline bool path_mounted(const struct path *path) · 337 unmodified lines | |||
| 338 | } | 338 | } |
| 339 | void file_f_owner_release(struct file *file); | 339 | void file_f_owner_release(struct file *file); |
| 340 | bool file_seek_cur_needs_f_lock(struct file *file); | 340 | bool file_seek_cur_needs_f_lock(struct file *file); |
| 341 | int statmount_mnt_idmap(struct mnt_idmap *idmap, struct seq_file *seq, bool uid_map); | ||
| @@ -6,6 +6,7 @@ · 5 unmodified lines | |||
| 6 | #include <linux/mnt_idmapping.h> | 6 | #include <linux/mnt_idmapping.h> |
| 7 | #include <linux/slab.h> | 7 | #include <linux/slab.h> |
| 8 | #include <linux/user_namespace.h> | 8 | #include <linux/user_namespace.h> |
| 9 | #include <linux/seq_file.h> | ||
| 9 | 10 | ||
| 10 | #include "internal.h" | 11 | #include "internal.h" |
| 11 | 12 | ||
| @@ -334,3 +335,53 @@ void mnt_idmap_put(struct mnt_idmap *idmap) · 322 unmodified lines | |||
| 334 | free_mnt_idmap(idmap); | 335 | free_mnt_idmap(idmap); |
| 335 | } | 336 | } |
| 336 | EXPORT_SYMBOL_GPL(mnt_idmap_put); | 337 | EXPORT_SYMBOL_GPL(mnt_idmap_put); |
| 338 | |||
| 339 | int statmount_mnt_idmap(struct mnt_idmap *idmap, struct seq_file *seq, bool uid_map) | ||
| 340 | { | ||
| 341 | struct uid_gid_map *map, *map_up; | ||
| 342 | u32 idx, nr_mappings; | ||
| 343 | |||
| 344 | if (!is_valid_mnt_idmap(idmap)) | ||
| 345 | return 0; | ||
| 346 | |||
| 347 | /* | ||
| 348 | * Idmappings are shown relative to the caller's idmapping. | ||
| 349 | * This is both the most intuitive and most useful solution. | ||
| 350 | */ | ||
| 351 | if (uid_map) { | ||
| 352 | map = &idmap->uid_map; | ||
| 353 | map_up = ¤t_user_ns()->uid_map; | ||
| 354 | } else { | ||
| 355 | map = &idmap->gid_map; | ||
| 356 | map_up = ¤t_user_ns()->gid_map; | ||
| 357 | } | ||
| 358 | |||
| 359 | for (idx = 0, nr_mappings = 0; idx < map->nr_extents; idx++) { | ||
| 360 | uid_t lower; | ||
| 361 | struct uid_gid_extent *extent; | ||
| 362 | |||
| 363 | if (map->nr_extents <= UID_GID_MAP_MAX_BASE_EXTENTS) | ||
| 364 | extent = &map->extent[idx]; | ||
| 365 | else | ||
| 366 | extent = &map->forward[idx]; | ||
| 367 | |||
| 368 | /* | ||
| 369 | * Verify that the whole range of the mapping can be | ||
| 370 | * resolved in the caller's idmapping. If it cannot be | ||
| 371 | * resolved skip the mapping. | ||
| 372 | */ | ||
| 373 | lower = map_id_range_up(map_up, extent->lower_first, extent->count); | ||
| 374 | if (lower == (uid_t) -1) | ||
| 375 | continue; | ||
| 376 | |||
| 377 | seq_printf(seq, "%u %u %u", extent->first, lower, extent->count); | ||
| 378 | |||
| 379 | seq->count++; /* mappings are separated by \0 */ | ||
| 380 | if (seq_has_overflowed(seq)) | ||
| 381 | return -EAGAIN; | ||
| 382 | |||
| 383 | nr_mappings++; | ||
| 384 | } | ||
| 385 | |||
| 386 | return nr_mappings; | ||
| 387 | } | ||
| @@ -5,6 +5,8 @@ · 4 unmodified lines | |||
| 5 | #include <linux/ns_common.h> | 5 | #include <linux/ns_common.h> |
| 6 | #include <linux/fs_pin.h> | 6 | #include <linux/fs_pin.h> |
| 7 | 7 | ||
| 8 | extern struct list_head notify_list; | ||
| 9 | |||
| 8 | struct mnt_namespace { | 10 | struct mnt_namespace { |
| 9 | struct ns_common ns; | 11 | struct ns_common ns; |
| 10 | struct mount * root; | 12 | struct mount * root; |
| @@ -21,6 +23,10 @@ struct mnt_namespace { · 10 unmodified lines | |||
| 21 | struct rcu_head mnt_ns_rcu; | 23 | struct rcu_head mnt_ns_rcu; |
| 22 | }; | 24 | }; |
| 23 | u64 event; | 25 | u64 event; |
| 26 | #ifdef CONFIG_FSNOTIFY | ||
| 27 | __u32 n_fsnotify_mask; | ||
| 28 | struct fsnotify_mark_connector __rcu *n_fsnotify_marks; | ||
| 29 | #endif | ||
| 24 | unsigned int nr_mounts; /* # of mounts in the namespace */ | 30 | unsigned int nr_mounts; /* # of mounts in the namespace */ |
| 25 | unsigned int pending_mounts; | 31 | unsigned int pending_mounts; |
| 26 | struct rb_node mnt_ns_tree_node; /* node in the mnt_ns_tree */ | 32 | struct rb_node mnt_ns_tree_node; /* node in the mnt_ns_tree */ |
| @@ -76,6 +82,8 @@ struct mount { · 49 unmodified lines | |||
| 76 | #ifdef CONFIG_FSNOTIFY | 82 | #ifdef CONFIG_FSNOTIFY |
| 77 | struct fsnotify_mark_connector __rcu *mnt_fsnotify_marks; | 83 | struct fsnotify_mark_connector __rcu *mnt_fsnotify_marks; |
| 78 | __u32 mnt_fsnotify_mask; | 84 | __u32 mnt_fsnotify_mask; |
| 85 | struct list_head to_notify; /* need to queue notification */ | ||
| 86 | struct mnt_namespace *prev_ns; /* previous namespace (NULL if none) */ | ||
| 79 | #endif | 87 | #endif |
| 80 | int mnt_id; /* mount identifier, reused */ | 88 | int mnt_id; /* mount identifier, reused */ |
| 81 | u64 mnt_id_unique; /* mount ID unique until reboot */ | 89 | u64 mnt_id_unique; /* mount ID unique until reboot */ |
| @@ -177,3 +185,21 @@ static inline struct mnt_namespace *to_mnt_ns(struct ns_common *ns) · 95 unmodified lines | |||
| 177 | { | 185 | { |
| 178 | return container_of(ns, struct mnt_namespace, ns); | 186 | return container_of(ns, struct mnt_namespace, ns); |
| 179 | } | 187 | } |
| 188 | |||
| 189 | #ifdef CONFIG_FSNOTIFY | ||
| 190 | static inline void mnt_notify_add(struct mount *m) | ||
| 191 | { | ||
| 192 | /* Optimize the case where there are no watches */ | ||
| 193 | if ((m->mnt_ns && m->mnt_ns->n_fsnotify_marks) || | ||
| 194 | (m->prev_ns && m->prev_ns->n_fsnotify_marks)) | ||
| 195 | list_add_tail(&m->to_notify, ¬ify_list); | ||
| 196 | else | ||
| 197 | m->prev_ns = m->mnt_ns; | ||
| 198 | } | ||
| 199 | #else | ||
| 200 | static inline void mnt_notify_add(struct mount *m) | ||
| 201 | { | ||
| 202 | } | ||
| 203 | #endif | ||
| 204 | |||
| 205 | struct mnt_namespace *mnt_ns_from_dentry(struct dentry *dentry); | ||
| @@ -81,15 +81,23 @@ static HLIST_HEAD(unmounted); /* protected by namespace_sem */ · 80 unmodified lines | |||
| 81 | static LIST_HEAD(ex_mountpoints); /* protected by namespace_sem */ | 81 | static LIST_HEAD(ex_mountpoints); /* protected by namespace_sem */ |
| 82 | static DEFINE_SEQLOCK(mnt_ns_tree_lock); | 82 | static DEFINE_SEQLOCK(mnt_ns_tree_lock); |
| 83 | 83 | ||
| 84 | #ifdef CONFIG_FSNOTIFY | ||
| 85 | LIST_HEAD(notify_list); /* protected by namespace_sem */ | ||
| 86 | #endif | ||
| 84 | static struct rb_root mnt_ns_tree = RB_ROOT; /* protected by mnt_ns_tree_lock */ | 87 | static struct rb_root mnt_ns_tree = RB_ROOT; /* protected by mnt_ns_tree_lock */ |
| 85 | static LIST_HEAD(mnt_ns_list); /* protected by mnt_ns_tree_lock */ | 88 | static LIST_HEAD(mnt_ns_list); /* protected by mnt_ns_tree_lock */ |
| 86 | 89 | ||
| 90 | enum mount_kattr_flags_t { | ||
| 91 | MOUNT_KATTR_RECURSE = (1 << 0), | ||
| 92 | MOUNT_KATTR_IDMAP_REPLACE = (1 << 1), | ||
| 93 | }; | ||
| 94 | |||
| 87 | struct mount_kattr { | 95 | struct mount_kattr { |
| 88 | unsigned int attr_set; | 96 | unsigned int attr_set; |
| 89 | unsigned int attr_clr; | 97 | unsigned int attr_clr; |
| 90 | unsigned int propagation; | 98 | unsigned int propagation; |
| 91 | unsigned int lookup_flags; | 99 | unsigned int lookup_flags; |
| 92 | bool recurse; | 100 | enum mount_kattr_flags_t kflags; |
| 93 | struct user_namespace *mnt_userns; | 101 | struct user_namespace *mnt_userns; |
| 94 | struct mnt_idmap *mnt_idmap; | 102 | struct mnt_idmap *mnt_idmap; |
| 95 | }; | 103 | }; |
| @@ -163,6 +171,7 @@ static void mnt_ns_release(struct mnt_namespace *ns) · 67 unmodified lines | |||
| 163 | { | 171 | { |
| 164 | /* keep alive for {list,stat}mount() */ | 172 | /* keep alive for {list,stat}mount() */ |
| 165 | if (refcount_dec_and_test(&ns->passive)) { | 173 | if (refcount_dec_and_test(&ns->passive)) { |
| 174 | fsnotify_mntns_delete(ns); | ||
| 166 | put_user_ns(ns->user_ns); | 175 | put_user_ns(ns->user_ns); |
| 167 | kfree(ns); | 176 | kfree(ns); |
| 168 | } | 177 | } |
| @@ -1176,6 +1185,8 @@ static void mnt_add_to_ns(struct mnt_namespace *ns, struct mount *mnt) · 1007 unmodified lines | |||
| 1176 | ns->mnt_first_node = &mnt->mnt_node; | 1185 | ns->mnt_first_node = &mnt->mnt_node; |
| 1177 | rb_link_node(&mnt->mnt_node, parent, link); | 1186 | rb_link_node(&mnt->mnt_node, parent, link); |
| 1178 | rb_insert_color(&mnt->mnt_node, &ns->mounts); | 1187 | rb_insert_color(&mnt->mnt_node, &ns->mounts); |
| 1188 | |||
| 1189 | mnt_notify_add(mnt); | ||
| 1179 | } | 1190 | } |
| 1180 | 1191 | ||
| 1181 | /* | 1192 | /* |
| @@ -1723,6 +1734,50 @@ int may_umount(struct vfsmount *mnt) · 541 unmodified lines | |||
| 1723 | 1734 | ||
| 1724 | EXPORT_SYMBOL(may_umount); | 1735 | EXPORT_SYMBOL(may_umount); |
| 1725 | 1736 | ||
| 1737 | #ifdef CONFIG_FSNOTIFY | ||
| 1738 | static void mnt_notify(struct mount *p) | ||
| 1739 | { | ||
| 1740 | if (!p->prev_ns && p->mnt_ns) { | ||
| 1741 | fsnotify_mnt_attach(p->mnt_ns, &p->mnt); | ||
| 1742 | } else if (p->prev_ns && !p->mnt_ns) { | ||
| 1743 | fsnotify_mnt_detach(p->prev_ns, &p->mnt); | ||
| 1744 | } else if (p->prev_ns == p->mnt_ns) { | ||
| 1745 | fsnotify_mnt_move(p->mnt_ns, &p->mnt); | ||
| 1746 | } else { | ||
| 1747 | fsnotify_mnt_detach(p->prev_ns, &p->mnt); | ||
| 1748 | fsnotify_mnt_attach(p->mnt_ns, &p->mnt); | ||
| 1749 | } | ||
| 1750 | p->prev_ns = p->mnt_ns; | ||
| 1751 | } | ||
| 1752 | |||
| 1753 | static void notify_mnt_list(void) | ||
| 1754 | { | ||
| 1755 | struct mount *m, *tmp; | ||
| 1756 | /* | ||
| 1757 | * Notify about mounts that were added/reparented/detached/remain | ||
| 1758 | * connected after unmount. | ||
| 1759 | */ | ||
| 1760 | list_for_each_entry_safe(m, tmp, ¬ify_list, to_notify) { | ||
| 1761 | mnt_notify(m); | ||
| 1762 | list_del_init(&m->to_notify); | ||
| 1763 | } | ||
| 1764 | } | ||
| 1765 | |||
| 1766 | static bool need_notify_mnt_list(void) | ||
| 1767 | { | ||
| 1768 | return !list_empty(¬ify_list); | ||
| 1769 | } | ||
| 1770 | #else | ||
| 1771 | static void notify_mnt_list(void) | ||
| 1772 | { | ||
| 1773 | } | ||
| 1774 | |||
| 1775 | static bool need_notify_mnt_list(void) | ||
| 1776 | { | ||
| 1777 | return false; | ||
| 1778 | } | ||
| 1779 | #endif | ||
| 1780 | |||
| 1726 | static void namespace_unlock(void) | 1781 | static void namespace_unlock(void) |
| 1727 | { | 1782 | { |
| 1728 | struct hlist_head head; | 1783 | struct hlist_head head; |
| @@ -1733,7 +1788,18 @@ static void namespace_unlock(void) · 4 unmodified lines | |||
| 1733 | hlist_move_list(&unmounted, &head); | 1788 | hlist_move_list(&unmounted, &head); |
| 1734 | list_splice_init(&ex_mountpoints, &list); | 1789 | list_splice_init(&ex_mountpoints, &list); |
| 1735 | 1790 | ||
| 1736 | up_write(&namespace_sem); | 1791 | if (need_notify_mnt_list()) { |
| 1792 | /* | ||
| 1793 | * No point blocking out concurrent readers while notifications | ||
| 1794 | * are sent. This will also allow statmount()/listmount() to run | ||
| 1795 | * concurrently. | ||
| 1796 | */ | ||
| 1797 | downgrade_write(&namespace_sem); | ||
| 1798 | notify_mnt_list(); | ||
| 1799 | up_read(&namespace_sem); | ||
| 1800 | } else { | ||
| 1801 | up_write(&namespace_sem); | ||
| 1802 | } | ||
| 1737 | 1803 | ||
| 1738 | shrink_dentry_list(&list); | 1804 | shrink_dentry_list(&list); |
| 1739 | 1805 | ||
| @@ -1846,6 +1912,19 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how) · 106 unmodified lines | |||
| 1846 | change_mnt_propagation(p, MS_PRIVATE); | 1912 | change_mnt_propagation(p, MS_PRIVATE); |
| 1847 | if (disconnect) | 1913 | if (disconnect) |
| 1848 | hlist_add_head(&p->mnt_umount, &unmounted); | 1914 | hlist_add_head(&p->mnt_umount, &unmounted); |
| 1915 | |||
| 1916 | /* | ||
| 1917 | * At this point p->mnt_ns is NULL, notification will be queued | ||
| 1918 | * only if | ||
| 1919 | * | ||
| 1920 | * - p->prev_ns is non-NULL *and* | ||
| 1921 | * - p->prev_ns->n_fsnotify_marks is non-NULL | ||
| 1922 | * | ||
| 1923 | * This will preclude queuing the mount if this is a cleanup | ||
| 1924 | * after a failed copy_tree() or destruction of an anonymous | ||
| 1925 | * namespace, etc. | ||
| 1926 | */ | ||
| 1927 | mnt_notify_add(p); | ||
| 1849 | } | 1928 | } |
| 1850 | } | 1929 | } |
| 1851 | 1930 | ||
| @@ -2026,6 +2105,7 @@ static void warn_mandlock(void) · 174 unmodified lines | |||
| 2026 | static int can_umount(const struct path *path, int flags) | 2105 | static int can_umount(const struct path *path, int flags) |
| 2027 | { | 2106 | { |
| 2028 | struct mount *mnt = real_mount(path->mnt); | 2107 | struct mount *mnt = real_mount(path->mnt); |
| 2108 | struct super_block *sb = path->dentry->d_sb; | ||
| 2029 | 2109 | ||
| 2030 | if (!may_mount()) | 2110 | if (!may_mount()) |
| 2031 | return -EPERM; | 2111 | return -EPERM; |
| @@ -2035,7 +2115,7 @@ static int can_umount(const struct path *path, int flags) · 3 unmodified lines | |||
| 2035 | return -EINVAL; | 2115 | return -EINVAL; |
| 2036 | if (mnt->mnt.mnt_flags & MNT_LOCKED) /* Check optimistically */ | 2116 | if (mnt->mnt.mnt_flags & MNT_LOCKED) /* Check optimistically */ |
| 2037 | return -EINVAL; | 2117 | return -EINVAL; |
| 2038 | if (flags & MNT_FORCE && !capable(CAP_SYS_ADMIN)) | 2118 | if (flags & MNT_FORCE && !ns_capable(sb->s_user_ns, CAP_SYS_ADMIN)) |
| 2039 | return -EPERM; | 2119 | return -EPERM; |
| 2040 | return 0; | 2120 | return 0; |
| 2041 | } | 2121 | } |
| @@ -2145,16 +2225,24 @@ struct mnt_namespace *get_sequential_mnt_ns(struct mnt_namespace *mntns, bool pr · 103 unmodified lines | |||
| 2145 | } | 2225 | } |
| 2146 | } | 2226 | } |
| 2147 | 2227 | ||
| 2228 | struct mnt_namespace *mnt_ns_from_dentry(struct dentry *dentry) | ||
| 2229 | { | ||
| 2230 | if (!is_mnt_ns_file(dentry)) | ||
| 2231 | return NULL; | ||
| 2232 | |||
| 2233 | return to_mnt_ns(get_proc_ns(dentry->d_inode)); | ||
| 2234 | } | ||
| 2235 | |||
| 2148 | static bool mnt_ns_loop(struct dentry *dentry) | 2236 | static bool mnt_ns_loop(struct dentry *dentry) |
| 2149 | { | 2237 | { |
| 2150 | /* Could bind mounting the mount namespace inode cause a | 2238 | /* Could bind mounting the mount namespace inode cause a |
| 2151 | * mount namespace loop? | 2239 | * mount namespace loop? |
| 2152 | */ | 2240 | */ |
| 2153 | struct mnt_namespace *mnt_ns; | 2241 | struct mnt_namespace *mnt_ns = mnt_ns_from_dentry(dentry); |
| 2154 | if (!is_mnt_ns_file(dentry)) | 2242 | |
| 2243 | if (!mnt_ns) | ||
| 2155 | return false; | 2244 | return false; |
| 2156 | 2245 | ||
| 2157 | mnt_ns = to_mnt_ns(get_proc_ns(dentry->d_inode)); | ||
| 2158 | return current->nsproxy->mnt_ns->seq >= mnt_ns->seq; | 2246 | return current->nsproxy->mnt_ns->seq >= mnt_ns->seq; |
| 2159 | } | 2247 | } |
| 2160 | 2248 | ||
| @@ -2287,6 +2375,28 @@ bool has_locked_children(struct mount *mnt, struct dentry *dentry) · 126 unmodified lines | |||
| 2287 | return false; | 2375 | return false; |
| 2288 | } | 2376 | } |
| 2289 | 2377 | ||
| 2378 | /* | ||
| 2379 | * Check that there aren't references to earlier/same mount namespaces in the | ||
| 2380 | * specified subtree. Such references can act as pins for mount namespaces | ||
| 2381 | * that aren't checked by the mount-cycle checking code, thereby allowing | ||
| 2382 | * cycles to be made. | ||
| 2383 | */ | ||
| 2384 | static bool check_for_nsfs_mounts(struct mount *subtree) | ||
| 2385 | { | ||
| 2386 | struct mount *p; | ||
| 2387 | bool ret = false; | ||
| 2388 | |||
| 2389 | lock_mount_hash(); | ||
| 2390 | for (p = subtree; p; p = next_mnt(p, subtree)) | ||
| 2391 | if (mnt_ns_loop(p->mnt.mnt_root)) | ||
| 2392 | goto out; | ||
| 2393 | |||
| 2394 | ret = true; | ||
| 2395 | out: | ||
| 2396 | unlock_mount_hash(); | ||
| 2397 | return ret; | ||
| 2398 | } | ||
| 2399 | |||
| 2290 | /** | 2400 | /** |
| 2291 | * clone_private_mount - create a private clone of a path | 2401 | * clone_private_mount - create a private clone of a path |
| 2292 | * @path: path to clone | 2402 | * @path: path to clone |
| @@ -2295,6 +2405,8 @@ bool has_locked_children(struct mount *mnt, struct dentry *dentry) · 2 unmodified lines | |||
| 2295 | * will not be attached anywhere in the namespace and will be private (i.e. | 2405 | * will not be attached anywhere in the namespace and will be private (i.e. |
| 2296 | * changes to the originating mount won't be propagated into this). | 2406 | * changes to the originating mount won't be propagated into this). |
| 2297 | * | 2407 | * |
| 2408 | * This assumes caller has called or done the equivalent of may_mount(). | ||
| 2409 | * | ||
| 2298 | * Release with mntput(). | 2410 | * Release with mntput(). |
| 2299 | */ | 2411 | */ |
| 2300 | struct vfsmount *clone_private_mount(const struct path *path) | 2412 | struct vfsmount *clone_private_mount(const struct path *path) |
| @@ -2302,30 +2414,36 @@ struct vfsmount *clone_private_mount(const struct path *path) · 1 unmodified line | |||
| 2302 | struct mount *old_mnt = real_mount(path->mnt); | 2414 | struct mount *old_mnt = real_mount(path->mnt); |
| 2303 | struct mount *new_mnt; | 2415 | struct mount *new_mnt; |
| 2304 | 2416 | ||
| 2305 | down_read(&namespace_sem); | 2417 | scoped_guard(rwsem_read, &namespace_sem) |
| 2306 | if (IS_MNT_UNBINDABLE(old_mnt)) | 2418 | if (IS_MNT_UNBINDABLE(old_mnt)) |
| 2307 | goto invalid; | 2419 | return ERR_PTR(-EINVAL); |
| 2420 | |||
| 2421 | if (mnt_has_parent(old_mnt)) { | ||
| 2422 | if (!check_mnt(old_mnt)) | ||
| 2423 | return ERR_PTR(-EINVAL); | ||
| 2424 | } else { | ||
| 2425 | if (!is_mounted(&old_mnt->mnt)) | ||
| 2426 | return ERR_PTR(-EINVAL); | ||
| 2308 | 2427 | ||
| 2309 | if (!check_mnt(old_mnt)) | 2428 | /* Make sure this isn't something purely kernel internal. */ |
| 2310 | goto invalid; | 2429 | if (!is_anon_ns(old_mnt->mnt_ns)) |
| 2430 | return ERR_PTR(-EINVAL); | ||
| 2431 | |||
| 2432 | /* Make sure we don't create mount namespace loops. */ | ||
| 2433 | if (!check_for_nsfs_mounts(old_mnt)) | ||
| 2434 | return ERR_PTR(-EINVAL); | ||
| 2435 | } | ||
| 2311 | 2436 | ||
| 2312 | if (has_locked_children(old_mnt, path->dentry)) | 2437 | if (has_locked_children(old_mnt, path->dentry)) |
| 2313 | goto invalid; | 2438 | return ERR_PTR(-EINVAL); |
| 2314 | 2439 | ||
| 2315 | new_mnt = clone_mnt(old_mnt, path->dentry, CL_PRIVATE); | 2440 | new_mnt = clone_mnt(old_mnt, path->dentry, CL_PRIVATE); |
| 2316 | up_read(&namespace_sem); | ||
| 2317 | |||
| 2318 | if (IS_ERR(new_mnt)) | 2441 | if (IS_ERR(new_mnt)) |
| 2319 | return ERR_CAST(new_mnt); | 2442 | return ERR_PTR(-EINVAL); |
| 2320 | 2443 | ||
| 2321 | /* Longterm mount to be removed by kern_unmount*() */ | 2444 | /* Longterm mount to be removed by kern_unmount*() */ |
| 2322 | new_mnt->mnt_ns = MNT_NS_INTERNAL; | 2445 | new_mnt->mnt_ns = MNT_NS_INTERNAL; |
| 2323 | |||
| 2324 | return &new_mnt->mnt; | 2446 | return &new_mnt->mnt; |
| 2325 | |||
| 2326 | invalid: | ||
| 2327 | up_read(&namespace_sem); | ||
| 2328 | return ERR_PTR(-EINVAL); | ||
| 2329 | } | 2447 | } |
| 2330 | EXPORT_SYMBOL_GPL(clone_private_mount); | 2448 | EXPORT_SYMBOL_GPL(clone_private_mount); |
| 2331 | 2449 | ||
| @@ -2547,6 +2665,7 @@ static int attach_recursive_mnt(struct mount *source_mnt, · 215 unmodified lines | |||
| 2547 | dest_mp = smp; | 2665 | dest_mp = smp; |
| 2548 | unhash_mnt(source_mnt); | 2666 | unhash_mnt(source_mnt); |
| 2549 | attach_mnt(source_mnt, top_mnt, dest_mp, beneath); | 2667 | attach_mnt(source_mnt, top_mnt, dest_mp, beneath); |
| 2668 | mnt_notify_add(source_mnt); | ||
| 2550 | touch_mnt_namespace(source_mnt->mnt_ns); | 2669 | touch_mnt_namespace(source_mnt->mnt_ns); |
| 2551 | } else { | 2670 | } else { |
| 2552 | if (source_mnt->mnt_ns) { | 2671 | if (source_mnt->mnt_ns) { |
| @@ -2889,24 +3008,22 @@ static struct file *open_detached_copy(struct path *path, bool recursive) · 336 unmodified lines | |||
| 2889 | return file; | 3008 | return file; |
| 2890 | } | 3009 | } |
| 2891 | 3010 | ||
| 2892 | SYSCALL_DEFINE3(open_tree, int, dfd, const char __user *, filename, unsigned, flags) | 3011 | static struct file *vfs_open_tree(int dfd, const char __user *filename, unsigned int flags) |
| 2893 | { | 3012 | { |
| 2894 | struct file *file; | 3013 | int ret; |
| 2895 | struct path path; | 3014 | struct path path __free(path_put) = {}; |
| 2896 | int lookup_flags = LOOKUP_AUTOMOUNT | LOOKUP_FOLLOW; | 3015 | int lookup_flags = LOOKUP_AUTOMOUNT | LOOKUP_FOLLOW; |
| 2897 | bool detached = flags & OPEN_TREE_CLONE; | 3016 | bool detached = flags & OPEN_TREE_CLONE; |
| 2898 | int error; | ||
| 2899 | int fd; | ||
| 2900 | 3017 | ||
| 2901 | BUILD_BUG_ON(OPEN_TREE_CLOEXEC != O_CLOEXEC); | 3018 | BUILD_BUG_ON(OPEN_TREE_CLOEXEC != O_CLOEXEC); |
| 2902 | 3019 | ||
| 2903 | if (flags & ~(AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_RECURSIVE | | 3020 | if (flags & ~(AT_EMPTY_PATH | AT_NO_AUTOMOUNT | AT_RECURSIVE | |
| 2904 | AT_SYMLINK_NOFOLLOW | OPEN_TREE_CLONE | | 3021 | AT_SYMLINK_NOFOLLOW | OPEN_TREE_CLONE | |
| 2905 | OPEN_TREE_CLOEXEC)) | 3022 | OPEN_TREE_CLOEXEC)) |
| 2906 | return -EINVAL; | 3023 | return ERR_PTR(-EINVAL); |
| 2907 | 3024 | ||
| 2908 | if ((flags & (AT_RECURSIVE | OPEN_TREE_CLONE)) == AT_RECURSIVE) | 3025 | if ((flags & (AT_RECURSIVE | OPEN_TREE_CLONE)) == AT_RECURSIVE) |
| 2909 | return -EINVAL; | 3026 | return ERR_PTR(-EINVAL); |
| 2910 | 3027 | ||
| 2911 | if (flags & AT_NO_AUTOMOUNT) | 3028 | if (flags & AT_NO_AUTOMOUNT) |
| 2912 | lookup_flags &= ~LOOKUP_AUTOMOUNT; | 3029 | lookup_flags &= ~LOOKUP_AUTOMOUNT; |
| @@ -2916,27 +3033,32 @@ SYSCALL_DEFINE3(open_tree, int, dfd, const char __user *, filename, unsigned, fl · 3 unmodified lines | |||
| 2916 | lookup_flags |= LOOKUP_EMPTY; | 3033 | lookup_flags |= LOOKUP_EMPTY; |
| 2917 | 3034 | ||
| 2918 | if (detached && !may_mount()) | 3035 | if (detached && !may_mount()) |
| 2919 | return -EPERM; | 3036 | return ERR_PTR(-EPERM); |
| 3037 | |||
| 3038 | ret = user_path_at(dfd, filename, lookup_flags, &path); | ||
| 3039 | if (unlikely(ret)) | ||
| 3040 | return ERR_PTR(ret); | ||
| 3041 | |||
| 3042 | if (detached) | ||
| 3043 | return open_detached_copy(&path, flags & AT_RECURSIVE); | ||
| 3044 | |||
| 3045 | return dentry_open(&path, O_PATH, current_cred()); | ||
| 3046 | } | ||
| 3047 | |||
| 3048 | SYSCALL_DEFINE3(open_tree, int, dfd, const char __user *, filename, unsigned, flags) | ||
| 3049 | { | ||
| 3050 | int fd; | ||
| 3051 | struct file *file __free(fput) = NULL; | ||
| 3052 | |||
| 3053 | file = vfs_open_tree(dfd, filename, flags); | ||
| 3054 | if (IS_ERR(file)) | ||
| 3055 | return PTR_ERR(file); | ||
| 2920 | 3056 | ||
| 2921 | fd = get_unused_fd_flags(flags & O_CLOEXEC); | 3057 | fd = get_unused_fd_flags(flags & O_CLOEXEC); |
| 2922 | if (fd < 0) | 3058 | if (fd < 0) |
| 2923 | return fd; | 3059 | return fd; |
| 2924 | 3060 | ||
| 2925 | error = user_path_at(dfd, filename, lookup_flags, &path); | 3061 | fd_install(fd, no_free_ptr(file)); |
| 2926 | if (unlikely(error)) { | ||
| 2927 | file = ERR_PTR(error); | ||
| 2928 | } else { | ||
| 2929 | if (detached) | ||
| 2930 | file = open_detached_copy(&path, flags & AT_RECURSIVE); | ||
| 2931 | else | ||
| 2932 | file = dentry_open(&path, O_PATH, current_cred()); | ||
| 2933 | path_put(&path); | ||
| 2934 | } | ||
| 2935 | if (IS_ERR(file)) { | ||
| 2936 | put_unused_fd(fd); | ||
| 2937 | return PTR_ERR(file); | ||
| 2938 | } | ||
| 2939 | fd_install(fd, file); | ||
| 2940 | return fd; | 3062 | return fd; |
| 2941 | } | 3063 | } |
| 2942 | 3064 | ||
| @@ -3123,28 +3245,6 @@ static inline int tree_contains_unbindable(struct mount *mnt) · 180 unmodified lines | |||
| 3123 | return 0; | 3245 | return 0; |
| 3124 | } | 3246 | } |
| 3125 | 3247 | ||
| 3126 | /* | ||
| 3127 | * Check that there aren't references to earlier/same mount namespaces in the | ||
| 3128 | * specified subtree. Such references can act as pins for mount namespaces | ||
| 3129 | * that aren't checked by the mount-cycle checking code, thereby allowing | ||
| 3130 | * cycles to be made. | ||
| 3131 | */ | ||
| 3132 | static bool check_for_nsfs_mounts(struct mount *subtree) | ||
| 3133 | { | ||
| 3134 | struct mount *p; | ||
| 3135 | bool ret = false; | ||
| 3136 | |||
| 3137 | lock_mount_hash(); | ||
| 3138 | for (p = subtree; p; p = next_mnt(p, subtree)) | ||
| 3139 | if (mnt_ns_loop(p->mnt.mnt_root)) | ||
| 3140 | goto out; | ||
| 3141 | |||
| 3142 | ret = true; | ||
| 3143 | out: | ||
| 3144 | unlock_mount_hash(); | ||
| 3145 | return ret; | ||
| 3146 | } | ||
| 3147 | |||
| 3148 | static int do_set_group(struct path *from_path, struct path *to_path) | 3248 | static int do_set_group(struct path *from_path, struct path *to_path) |
| 3149 | { | 3249 | { |
| 3150 | struct mount *from, *to; | 3250 | struct mount *from, *to; |
| @@ -4468,6 +4568,8 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root, · 1317 unmodified lines | |||
| 4468 | list_del_init(&new_mnt->mnt_expire); | 4568 | list_del_init(&new_mnt->mnt_expire); |
| 4469 | put_mountpoint(root_mp); | 4569 | put_mountpoint(root_mp); |
| 4470 | unlock_mount_hash(); | 4570 | unlock_mount_hash(); |
| 4571 | mnt_notify_add(root_mnt); | ||
| 4572 | mnt_notify_add(new_mnt); | ||
| 4471 | chroot_fs_refs(&root, &new); | 4573 | chroot_fs_refs(&root, &new); |
| 4472 | error = 0; | 4574 | error = 0; |
| 4473 | out4: | 4575 | out4: |
| @@ -4512,11 +4614,10 @@ static int can_idmap_mount(const struct mount_kattr *kattr, struct mount *mnt) · 38 unmodified lines | |||
| 4512 | return -EINVAL; | 4614 | return -EINVAL; |
| 4513 | 4615 | ||
| 4514 | /* | 4616 | /* |
| 4515 | * Once a mount has been idmapped we don't allow it to change its | 4617 | * We only allow an mount to change it's idmapping if it has |
| 4516 | * mapping. It makes things simpler and callers can just create | 4618 | * never been accessible to userspace. |
| 4517 | * another bind-mount they can idmap if they want to. | ||
| 4518 | */ | 4619 | */ |
| 4519 | if (is_idmapped_mnt(m)) | 4620 | if (!(kattr->kflags & MOUNT_KATTR_IDMAP_REPLACE) && is_idmapped_mnt(m)) |
| 4520 | return -EPERM; | 4621 | return -EPERM; |
| 4521 | 4622 | ||
| 4522 | /* The underlying filesystem doesn't support idmapped mounts yet. */ | 4623 | /* The underlying filesystem doesn't support idmapped mounts yet. */ |
| @@ -4576,7 +4677,7 @@ static int mount_setattr_prepare(struct mount_kattr *kattr, struct mount *mnt) · 53 unmodified lines | |||
| 4576 | break; | 4677 | break; |
| 4577 | } | 4678 | } |
| 4578 | 4679 | ||
| 4579 | if (!kattr->recurse) | 4680 | if (!(kattr->kflags & MOUNT_KATTR_RECURSE)) |
| 4580 | return 0; | 4681 | return 0; |
| 4581 | } | 4682 | } |
| 4582 | 4683 | ||
| @@ -4606,18 +4707,16 @@ static int mount_setattr_prepare(struct mount_kattr *kattr, struct mount *mnt) · 23 unmodified lines | |||
| 4606 | 4707 | ||
| 4607 | static void do_idmap_mount(const struct mount_kattr *kattr, struct mount *mnt) | 4708 | static void do_idmap_mount(const struct mount_kattr *kattr, struct mount *mnt) |
| 4608 | { | 4709 | { |
| 4710 | struct mnt_idmap *old_idmap; | ||
| 4711 | |||
| 4609 | if (!kattr->mnt_idmap) | 4712 | if (!kattr->mnt_idmap) |
| 4610 | return; | 4713 | return; |
| 4611 | 4714 | ||
| 4612 | /* | 4715 | old_idmap = mnt_idmap(&mnt->mnt); |
| 4613 | * Pairs with smp_load_acquire() in mnt_idmap(). | 4716 | |
| 4614 | * | 4717 | /* Pairs with smp_load_acquire() in mnt_idmap(). */ |
| 4615 | * Since we only allow a mount to change the idmapping once and | ||
| 4616 | * verified this in can_idmap_mount() we know that the mount has | ||
| 4617 | * @nop_mnt_idmap attached to it. So there's no need to drop any | ||
| 4618 | * references. | ||
| 4619 | */ | ||
| 4620 | smp_store_release(&mnt->mnt.mnt_idmap, mnt_idmap_get(kattr->mnt_idmap)); | 4718 | smp_store_release(&mnt->mnt.mnt_idmap, mnt_idmap_get(kattr->mnt_idmap)); |
| 4719 | mnt_idmap_put(old_idmap); | ||
| 4621 | } | 4720 | } |
| 4622 | 4721 | ||
| 4623 | static void mount_setattr_commit(struct mount_kattr *kattr, struct mount *mnt) | 4722 | static void mount_setattr_commit(struct mount_kattr *kattr, struct mount *mnt) |
| @@ -4637,7 +4736,7 @@ static void mount_setattr_commit(struct mount_kattr *kattr, struct mount *mnt) · 13 unmodified lines | |||
| 4637 | 4736 | ||
| 4638 | if (kattr->propagation) | 4737 | if (kattr->propagation) |
| 4639 | change_mnt_propagation(m, kattr->propagation); | 4738 | change_mnt_propagation(m, kattr->propagation); |
| 4640 | if (!kattr->recurse) | 4739 | if (!(kattr->kflags & MOUNT_KATTR_RECURSE)) |
| 4641 | break; | 4740 | break; |
| 4642 | } | 4741 | } |
| 4643 | touch_mnt_namespace(mnt->mnt_ns); | 4742 | touch_mnt_namespace(mnt->mnt_ns); |
| @@ -4667,7 +4766,7 @@ static int do_mount_setattr(struct path *path, struct mount_kattr *kattr) · 23 unmodified lines | |||
| 4667 | */ | 4766 | */ |
| 4668 | namespace_lock(); | 4767 | namespace_lock(); |
| 4669 | if (kattr->propagation == MS_SHARED) { | 4768 | if (kattr->propagation == MS_SHARED) { |
| 4670 | err = invent_group_ids(mnt, kattr->recurse); | 4769 | err = invent_group_ids(mnt, kattr->kflags & MOUNT_KATTR_RECURSE); |
| 4671 | if (err) { | 4770 | if (err) { |
| 4672 | namespace_unlock(); | 4771 | namespace_unlock(); |
| 4673 | return err; | 4772 | return err; |
| @@ -4718,7 +4817,7 @@ out: · 44 unmodified lines | |||
| 4718 | } | 4817 | } |
| 4719 | 4818 | ||
| 4720 | static int build_mount_idmapped(const struct mount_attr *attr, size_t usize, | 4819 | static int build_mount_idmapped(const struct mount_attr *attr, size_t usize, |
| 4721 | struct mount_kattr *kattr, unsigned int flags) | 4820 | struct mount_kattr *kattr) |
| 4722 | { | 4821 | { |
| 4723 | struct ns_common *ns; | 4822 | struct ns_common *ns; |
| 4724 | struct user_namespace *mnt_userns; | 4823 | struct user_namespace *mnt_userns; |
| @@ -4726,13 +4825,23 @@ static int build_mount_idmapped(const struct mount_attr *attr, size_t usize, · 1 unmodified line | |||
| 4726 | if (!((attr->attr_set | attr->attr_clr) & MOUNT_ATTR_IDMAP)) | 4825 | if (!((attr->attr_set | attr->attr_clr) & MOUNT_ATTR_IDMAP)) |
| 4727 | return 0; | 4826 | return 0; |
| 4728 | 4827 | ||
| 4729 | /* | 4828 | if (attr->attr_clr & MOUNT_ATTR_IDMAP) { |
| 4730 | * We currently do not support clearing an idmapped mount. If this ever | 4829 | /* |
| 4731 | * is a use-case we can revisit this but for now let's keep it simple | 4830 | * We can only remove an idmapping if it's never been |
| 4732 | * and not allow it. | 4831 | * exposed to userspace. |
| 4733 | */ | 4832 | */ |
| 4734 | if (attr->attr_clr & MOUNT_ATTR_IDMAP) | 4833 | if (!(kattr->kflags & MOUNT_KATTR_IDMAP_REPLACE)) |
| 4735 | return -EINVAL; | 4834 | return -EINVAL; |
| 4835 | |||
| 4836 | /* | ||
| 4837 | * Removal of idmappings is equivalent to setting | ||
| 4838 | * nop_mnt_idmap. | ||
| 4839 | */ | ||
| 4840 | if (!(attr->attr_set & MOUNT_ATTR_IDMAP)) { | ||
| 4841 | kattr->mnt_idmap = &nop_mnt_idmap; | ||
| 4842 | return 0; | ||
| 4843 | } | ||
| 4844 | } | ||
| 4736 | 4845 | ||
| 4737 | if (attr->userns_fd > INT_MAX) | 4846 | if (attr->userns_fd > INT_MAX) |
| 4738 | return -EINVAL; | 4847 | return -EINVAL; |
| @@ -4769,22 +4878,8 @@ static int build_mount_idmapped(const struct mount_attr *attr, size_t usize, · 30 unmodified lines | |||
| 4769 | } | 4878 | } |
| 4770 | 4879 | ||
| 4771 | static int build_mount_kattr(const struct mount_attr *attr, size_t usize, | 4880 | static int build_mount_kattr(const struct mount_attr *attr, size_t usize, |
| 4772 | struct mount_kattr *kattr, unsigned int flags) | 4881 | struct mount_kattr *kattr) |
| 4773 | { | 4882 | { |
| 4774 | unsigned int lookup_flags = LOOKUP_AUTOMOUNT | LOOKUP_FOLLOW; | ||
| 4775 | |||
| 4776 | if (flags & AT_NO_AUTOMOUNT) | ||
| 4777 | lookup_flags &= ~LOOKUP_AUTOMOUNT; | ||
| 4778 | if (flags & AT_SYMLINK_NOFOLLOW) | ||
| 4779 | lookup_flags &= ~LOOKUP_FOLLOW; | ||
| 4780 | if (flags & AT_EMPTY_PATH) | ||
| 4781 | lookup_flags |= LOOKUP_EMPTY; | ||
| 4782 | |||
| 4783 | *kattr = (struct mount_kattr) { | ||
| 4784 | .lookup_flags = lookup_flags, | ||
| 4785 | .recurse = !!(flags & AT_RECURSIVE), | ||
| 4786 | }; | ||
| 4787 | |||
| 4788 | if (attr->propagation & ~MOUNT_SETATTR_PROPAGATION_FLAGS) | 4883 | if (attr->propagation & ~MOUNT_SETATTR_PROPAGATION_FLAGS) |
| 4789 | return -EINVAL; | 4884 | return -EINVAL; |
| 4790 | if (hweight32(attr->propagation & MOUNT_SETATTR_PROPAGATION_FLAGS) > 1) | 4885 | if (hweight32(attr->propagation & MOUNT_SETATTR_PROPAGATION_FLAGS) > 1) |
| @@ -4832,35 +4927,28 @@ static int build_mount_kattr(const struct mount_attr *attr, size_t usize, · 41 unmodified lines | |||
| 4832 | return -EINVAL; | 4927 | return -EINVAL; |
| 4833 | } | 4928 | } |
| 4834 | 4929 | ||
| 4835 | return build_mount_idmapped(attr, usize, kattr, flags); | 4930 | return build_mount_idmapped(attr, usize, kattr); |
| 4836 | } | 4931 | } |
| 4837 | 4932 | ||
| 4838 | static void finish_mount_kattr(struct mount_kattr *kattr) | 4933 | static void finish_mount_kattr(struct mount_kattr *kattr) |
| 4839 | { | 4934 | { |
| 4840 | put_user_ns(kattr->mnt_userns); | 4935 | if (kattr->mnt_userns) { |
| 4841 | kattr->mnt_userns = NULL; | 4936 | put_user_ns(kattr->mnt_userns); |
| 4937 | kattr->mnt_userns = NULL; | ||
| 4938 | } | ||
| 4842 | 4939 | ||
| 4843 | if (kattr->mnt_idmap) | 4940 | if (kattr->mnt_idmap) |
| 4844 | mnt_idmap_put(kattr->mnt_idmap); | 4941 | mnt_idmap_put(kattr->mnt_idmap); |
| 4845 | } | 4942 | } |
| 4846 | 4943 | ||
| 4847 | SYSCALL_DEFINE5(mount_setattr, int, dfd, const char __user *, path, | 4944 | static int copy_mount_setattr(struct mount_attr __user *uattr, size_t usize, |
| 4848 | unsigned int, flags, struct mount_attr __user *, uattr, | 4945 | struct mount_kattr *kattr) |
| 4849 | size_t, usize) | ||
| 4850 | { | 4946 | { |
| 4851 | int err; | 4947 | int ret; |
| 4852 | struct path target; | ||
| 4853 | struct mount_attr attr; | 4948 | struct mount_attr attr; |
| 4854 | struct mount_kattr kattr; | ||
| 4855 | 4949 | ||
| 4856 | BUILD_BUG_ON(sizeof(struct mount_attr) != MOUNT_ATTR_SIZE_VER0); | 4950 | BUILD_BUG_ON(sizeof(struct mount_attr) != MOUNT_ATTR_SIZE_VER0); |
| 4857 | 4951 | ||
| 4858 | if (flags & ~(AT_EMPTY_PATH | | ||
| 4859 | AT_RECURSIVE | | ||
| 4860 | AT_SYMLINK_NOFOLLOW | | ||
| 4861 | AT_NO_AUTOMOUNT)) | ||
| 4862 | return -EINVAL; | ||
| 4863 | |||
| 4864 | if (unlikely(usize > PAGE_SIZE)) | 4952 | if (unlikely(usize > PAGE_SIZE)) |
| 4865 | return -E2BIG; | 4953 | return -E2BIG; |
| 4866 | if (unlikely(usize < MOUNT_ATTR_SIZE_VER0)) | 4954 | if (unlikely(usize < MOUNT_ATTR_SIZE_VER0)) |
| @@ -4869,9 +4957,9 @@ SYSCALL_DEFINE5(mount_setattr, int, dfd, const char __user *, path, · 2 unmodified lines | |||
| 4869 | if (!may_mount()) | 4957 | if (!may_mount()) |
| 4870 | return -EPERM; | 4958 | return -EPERM; |
| 4871 | 4959 | ||
| 4872 | err = copy_struct_from_user(&attr, sizeof(attr), uattr, usize); | 4960 | ret = copy_struct_from_user(&attr, sizeof(attr), uattr, usize); |
| 4873 | if (err) | 4961 | if (ret) |
| 4874 | return err; | 4962 | return ret; |
| 4875 | 4963 | ||
| 4876 | /* Don't bother walking through the mounts if this is a nop. */ | 4964 | /* Don't bother walking through the mounts if this is a nop. */ |
| 4877 | if (attr.attr_set == 0 && | 4965 | if (attr.attr_set == 0 && |
| @@ -4879,7 +4967,39 @@ SYSCALL_DEFINE5(mount_setattr, int, dfd, const char __user *, path, · 1 unmodified line | |||
| 4879 | attr.propagation == 0) | 4967 | attr.propagation == 0) |
| 4880 | return 0; | 4968 | return 0; |
| 4881 | 4969 | ||
| 4882 | err = build_mount_kattr(&attr, usize, &kattr, flags); | 4970 | return build_mount_kattr(&attr, usize, kattr); |
| 4971 | } | ||
| 4972 | |||
| 4973 | SYSCALL_DEFINE5(mount_setattr, int, dfd, const char __user *, path, | ||
| 4974 | unsigned int, flags, struct mount_attr __user *, uattr, | ||
| 4975 | size_t, usize) | ||
| 4976 | { | ||
| 4977 | int err; | ||
| 4978 | struct path target; | ||
| 4979 | struct mount_kattr kattr; | ||
| 4980 | unsigned int lookup_flags = LOOKUP_AUTOMOUNT | LOOKUP_FOLLOW; | ||
| 4981 | |||
| 4982 | if (flags & ~(AT_EMPTY_PATH | | ||
| 4983 | AT_RECURSIVE | | ||
| 4984 | AT_SYMLINK_NOFOLLOW | | ||
| 4985 | AT_NO_AUTOMOUNT)) | ||
| 4986 | return -EINVAL; | ||
| 4987 | |||
| 4988 | if (flags & AT_NO_AUTOMOUNT) | ||
| 4989 | lookup_flags &= ~LOOKUP_AUTOMOUNT; | ||
| 4990 | if (flags & AT_SYMLINK_NOFOLLOW) | ||
| 4991 | lookup_flags &= ~LOOKUP_FOLLOW; | ||
| 4992 | if (flags & AT_EMPTY_PATH) | ||
| 4993 | lookup_flags |= LOOKUP_EMPTY; | ||
| 4994 | |||
| 4995 | kattr = (struct mount_kattr) { | ||
| 4996 | .lookup_flags = lookup_flags, | ||
| 4997 | }; | ||
| 4998 | |||
| 4999 | if (flags & AT_RECURSIVE) | ||
| 5000 | kattr.kflags |= MOUNT_KATTR_RECURSE; | ||
| 5001 | |||
| 5002 | err = copy_mount_setattr(uattr, usize, &kattr); | ||
| 4883 | if (err) | 5003 | if (err) |
| 4884 | return err; | 5004 | return err; |
| 4885 | 5005 | ||
| @@ -4892,6 +5012,47 @@ SYSCALL_DEFINE5(mount_setattr, int, dfd, const char __user *, path, · 6 unmodified lines | |||
| 4892 | return err; | 5012 | return err; |
| 4893 | } | 5013 | } |
| 4894 | 5014 | ||
| 5015 | SYSCALL_DEFINE5(open_tree_attr, int, dfd, const char __user *, filename, | ||
| 5016 | unsigned, flags, struct mount_attr __user *, uattr, | ||
| 5017 | size_t, usize) | ||
| 5018 | { | ||
| 5019 | struct file __free(fput) *file = NULL; | ||
| 5020 | int fd; | ||
| 5021 | |||
| 5022 | if (!uattr && usize) | ||
| 5023 | return -EINVAL; | ||
| 5024 | |||
| 5025 | file = vfs_open_tree(dfd, filename, flags); | ||
| 5026 | if (IS_ERR(file)) | ||
| 5027 | return PTR_ERR(file); | ||
| 5028 | |||
| 5029 | if (uattr) { | ||
| 5030 | int ret; | ||
| 5031 | struct mount_kattr kattr = {}; | ||
| 5032 | |||
| 5033 | kattr.kflags = MOUNT_KATTR_IDMAP_REPLACE; | ||
| 5034 | if (flags & AT_RECURSIVE) | ||
| 5035 | kattr.kflags |= MOUNT_KATTR_RECURSE; | ||
| 5036 | |||
| 5037 | ret = copy_mount_setattr(uattr, usize, &kattr); | ||
| 5038 | if (ret) | ||
| 5039 | return ret; | ||
| 5040 | |||
| 5041 | ret = do_mount_setattr(&file->f_path, &kattr); | ||
| 5042 | if (ret) | ||
| 5043 | return ret; | ||
| 5044 | |||
| 5045 | finish_mount_kattr(&kattr); | ||
| 5046 | } | ||
| 5047 | |||
| 5048 | fd = get_unused_fd_flags(flags & O_CLOEXEC); | ||
| 5049 | if (fd < 0) | ||
| 5050 | return fd; | ||
| 5051 | |||
| 5052 | fd_install(fd, no_free_ptr(file)); | ||
| 5053 | return fd; | ||
| 5054 | } | ||
| 5055 | |||
| 4895 | int show_path(struct seq_file *m, struct dentry *root) | 5056 | int show_path(struct seq_file *m, struct dentry *root) |
| 4896 | { | 5057 | { |
| 4897 | if (root->d_sb->s_op->show_path) | 5058 | if (root->d_sb->s_op->show_path) |
| @@ -4915,6 +5076,7 @@ struct kstatmount { · 17 unmodified lines | |||
| 4915 | struct statmount __user *buf; | 5076 | struct statmount __user *buf; |
| 4916 | size_t bufsize; | 5077 | size_t bufsize; |
| 4917 | struct vfsmount *mnt; | 5078 | struct vfsmount *mnt; |
| 5079 | struct mnt_idmap *idmap; | ||
| 4918 | u64 mask; | 5080 | u64 mask; |
| 4919 | struct path root; | 5081 | struct path root; |
| 4920 | struct statmount sm; | 5082 | struct statmount sm; |
| @@ -5184,6 +5346,46 @@ static int statmount_opt_sec_array(struct kstatmount *s, struct seq_file *seq) · 263 unmodified lines | |||
| 5184 | return 0; | 5346 | return 0; |
| 5185 | } | 5347 | } |
| 5186 | 5348 | ||
| 5349 | static inline int statmount_mnt_uidmap(struct kstatmount *s, struct seq_file *seq) | ||
| 5350 | { | ||
| 5351 | int ret; | ||
| 5352 | |||
| 5353 | ret = statmount_mnt_idmap(s->idmap, seq, true); | ||
| 5354 | if (ret < 0) | ||
| 5355 | return ret; | ||
| 5356 | |||
| 5357 | s->sm.mnt_uidmap_num = ret; | ||
| 5358 | /* | ||
| 5359 | * Always raise STATMOUNT_MNT_UIDMAP even if there are no valid | ||
| 5360 | * mappings. This allows userspace to distinguish between a | ||
| 5361 | * non-idmapped mount and an idmapped mount where none of the | ||
| 5362 | * individual mappings are valid in the caller's idmapping. | ||
| 5363 | */ | ||
| 5364 | if (is_valid_mnt_idmap(s->idmap)) | ||
| 5365 | s->sm.mask |= STATMOUNT_MNT_UIDMAP; | ||
| 5366 | return 0; | ||
| 5367 | } | ||
| 5368 | |||
| 5369 | static inline int statmount_mnt_gidmap(struct kstatmount *s, struct seq_file *seq) | ||
| 5370 | { | ||
| 5371 | int ret; | ||
| 5372 | |||
| 5373 | ret = statmount_mnt_idmap(s->idmap, seq, false); | ||
| 5374 | if (ret < 0) | ||
| 5375 | return ret; | ||
| 5376 | |||
| 5377 | s->sm.mnt_gidmap_num = ret; | ||
| 5378 | /* | ||
| 5379 | * Always raise STATMOUNT_MNT_GIDMAP even if there are no valid | ||
| 5380 | * mappings. This allows userspace to distinguish between a | ||
| 5381 | * non-idmapped mount and an idmapped mount where none of the | ||
| 5382 | * individual mappings are valid in the caller's idmapping. | ||
| 5383 | */ | ||
| 5384 | if (is_valid_mnt_idmap(s->idmap)) | ||
| 5385 | s->sm.mask |= STATMOUNT_MNT_GIDMAP; | ||
| 5386 | return 0; | ||
| 5387 | } | ||
| 5388 | |||
| 5187 | static int statmount_string(struct kstatmount *s, u64 flag) | 5389 | static int statmount_string(struct kstatmount *s, u64 flag) |
| 5188 | { | 5390 | { |
| 5189 | int ret = 0; | 5391 | int ret = 0; |
| @@ -5231,6 +5433,14 @@ static int statmount_string(struct kstatmount *s, u64 flag) · 41 unmodified lines | |||
| 5231 | offp = &sm->sb_source; | 5433 | offp = &sm->sb_source; |
| 5232 | ret = statmount_sb_source(s, seq); | 5434 | ret = statmount_sb_source(s, seq); |
| 5233 | break; | 5435 | break; |
| 5436 | case STATMOUNT_MNT_UIDMAP: | ||
| 5437 | sm->mnt_uidmap = start; | ||
| 5438 | ret = statmount_mnt_uidmap(s, seq); | ||
| 5439 | break; | ||
| 5440 | case STATMOUNT_MNT_GIDMAP: | ||
| 5441 | sm->mnt_gidmap = start; | ||
| 5442 | ret = statmount_mnt_gidmap(s, seq); | ||
| 5443 | break; | ||
| 5234 | default: | 5444 | default: |
| 5235 | WARN_ON_ONCE(true); | 5445 | WARN_ON_ONCE(true); |
| 5236 | return -EINVAL; | 5446 | return -EINVAL; |
| @@ -5323,6 +5533,21 @@ static int grab_requested_root(struct mnt_namespace *ns, struct path *root) · 86 unmodified lines | |||
| 5323 | return 0; | 5533 | return 0; |
| 5324 | } | 5534 | } |
| 5325 | 5535 | ||
| 5536 | /* This must be updated whenever a new flag is added */ | ||
| 5537 | #define STATMOUNT_SUPPORTED (STATMOUNT_SB_BASIC | \ | ||
| 5538 | STATMOUNT_MNT_BASIC | \ | ||
| 5539 | STATMOUNT_PROPAGATE_FROM | \ | ||
| 5540 | STATMOUNT_MNT_ROOT | \ | ||
| 5541 | STATMOUNT_MNT_POINT | \ | ||
| 5542 | STATMOUNT_FS_TYPE | \ | ||
| 5543 | STATMOUNT_MNT_NS_ID | \ | ||
| 5544 | STATMOUNT_MNT_OPTS | \ | ||
| 5545 | STATMOUNT_FS_SUBTYPE | \ | ||
| 5546 | STATMOUNT_SB_SOURCE | \ | ||
| 5547 | STATMOUNT_OPT_ARRAY | \ | ||
| 5548 | STATMOUNT_OPT_SEC_ARRAY | \ | ||
| 5549 | STATMOUNT_SUPPORTED_MASK) | ||
| 5550 | |||
| 5326 | static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id, | 5551 | static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id, |
| 5327 | struct mnt_namespace *ns) | 5552 | struct mnt_namespace *ns) |
| 5328 | { | 5553 | { |
| @@ -5356,6 +5581,7 @@ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id, · 27 unmodified lines | |||
| 5356 | return err; | 5581 | return err; |
| 5357 | 5582 | ||
| 5358 | s->root = root; | 5583 | s->root = root; |
| 5584 | s->idmap = mnt_idmap(s->mnt); | ||
| 5359 | if (s->mask & STATMOUNT_SB_BASIC) | 5585 | if (s->mask & STATMOUNT_SB_BASIC) |
| 5360 | statmount_sb_basic(s); | 5586 | statmount_sb_basic(s); |
| 5361 | 5587 | ||
| @@ -5389,12 +5615,26 @@ static int do_statmount(struct kstatmount *s, u64 mnt_id, u64 mnt_ns_id, · 27 unmodified lines | |||
| 5389 | if (!err && s->mask & STATMOUNT_SB_SOURCE) | 5615 | if (!err && s->mask & STATMOUNT_SB_SOURCE) |
| 5390 | err = statmount_string(s, STATMOUNT_SB_SOURCE); | 5616 | err = statmount_string(s, STATMOUNT_SB_SOURCE); |
| 5391 | 5617 | ||
| 5618 | if (!err && s->mask & STATMOUNT_MNT_UIDMAP) | ||
| 5619 | err = statmount_string(s, STATMOUNT_MNT_UIDMAP); | ||
| 5620 | |||
| 5621 | if (!err && s->mask & STATMOUNT_MNT_GIDMAP) | ||
| 5622 | err = statmount_string(s, STATMOUNT_MNT_GIDMAP); | ||
| 5623 | |||
| 5392 | if (!err && s->mask & STATMOUNT_MNT_NS_ID) | 5624 | if (!err && s->mask & STATMOUNT_MNT_NS_ID) |
| 5393 | statmount_mnt_ns_id(s, ns); | 5625 | statmount_mnt_ns_id(s, ns); |
| 5394 | 5626 | ||
| 5627 | if (!err && s->mask & STATMOUNT_SUPPORTED_MASK) { | ||
| 5628 | s->sm.mask |= STATMOUNT_SUPPORTED_MASK; | ||
| 5629 | s->sm.supported_mask = STATMOUNT_SUPPORTED; | ||
| 5630 | } | ||
| 5631 | |||
| 5395 | if (err) | 5632 | if (err) |
| 5396 | return err; | 5633 | return err; |
| 5397 | 5634 | ||
| 5635 | /* Are there bits in the return mask not present in STATMOUNT_SUPPORTED? */ | ||
| 5636 | WARN_ON_ONCE(~STATMOUNT_SUPPORTED & s->sm.mask); | ||
| 5637 | |||
| 5398 | return 0; | 5638 | return 0; |
| 5399 | } | 5639 | } |
| 5400 | 5640 | ||
| @@ -5412,7 +5652,8 @@ static inline bool retry_statmount(const long ret, size_t *seq_size) · 11 unmodified lines | |||
| 5412 | #define STATMOUNT_STRING_REQ (STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT | \ | 5652 | #define STATMOUNT_STRING_REQ (STATMOUNT_MNT_ROOT | STATMOUNT_MNT_POINT | \ |
| 5413 | STATMOUNT_FS_TYPE | STATMOUNT_MNT_OPTS | \ | 5653 | STATMOUNT_FS_TYPE | STATMOUNT_MNT_OPTS | \ |
| 5414 | STATMOUNT_FS_SUBTYPE | STATMOUNT_SB_SOURCE | \ | 5654 | STATMOUNT_FS_SUBTYPE | STATMOUNT_SB_SOURCE | \ |
| 5415 | STATMOUNT_OPT_ARRAY | STATMOUNT_OPT_SEC_ARRAY) | 5655 | STATMOUNT_OPT_ARRAY | STATMOUNT_OPT_SEC_ARRAY | \ |
| 5656 | STATMOUNT_MNT_UIDMAP | STATMOUNT_MNT_GIDMAP) | ||
| 5416 | 5657 | ||
| 5417 | static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq, | 5658 | static int prepare_kstatmount(struct kstatmount *ks, struct mnt_id_req *kreq, |
| 5418 | struct statmount __user *buf, size_t bufsize, | 5659 | struct statmount __user *buf, size_t bufsize, |
| @@ -166,6 +166,8 @@ static bool fanotify_should_merge(struct fanotify_event *old, · 165 unmodified lines | |||
| 166 | case FANOTIFY_EVENT_TYPE_FS_ERROR: | 166 | case FANOTIFY_EVENT_TYPE_FS_ERROR: |
| 167 | return fanotify_error_event_equal(FANOTIFY_EE(old), | 167 | return fanotify_error_event_equal(FANOTIFY_EE(old), |
| 168 | FANOTIFY_EE(new)); | 168 | FANOTIFY_EE(new)); |
| 169 | case FANOTIFY_EVENT_TYPE_MNT: | ||
| 170 | return false; | ||
| 169 | default: | 171 | default: |
| 170 | WARN_ON_ONCE(1); | 172 | WARN_ON_ONCE(1); |
| 171 | } | 173 | } |
| @@ -312,7 +314,10 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group, · 140 unmodified lines | |||
| 312 | pr_debug("%s: report_mask=%x mask=%x data=%p data_type=%d\n", | 314 | pr_debug("%s: report_mask=%x mask=%x data=%p data_type=%d\n", |
| 313 | __func__, iter_info->report_mask, event_mask, data, data_type); | 315 | __func__, iter_info->report_mask, event_mask, data, data_type); |
| 314 | 316 | ||
| 315 | if (!fid_mode) { | 317 | if (FAN_GROUP_FLAG(group, FAN_REPORT_MNT)) { |
| 318 | if (data_type != FSNOTIFY_EVENT_MNT) | ||
| 319 | return 0; | ||
| 320 | } else if (!fid_mode) { | ||
| 316 | /* Do we have path to open a file descriptor? */ | 321 | /* Do we have path to open a file descriptor? */ |
| 317 | if (!path) | 322 | if (!path) |
| 318 | return 0; | 323 | return 0; |
| @@ -557,6 +562,20 @@ static struct fanotify_event *fanotify_alloc_path_event(const struct path *path, · 238 unmodified lines | |||
| 557 | return &pevent->fae; | 562 | return &pevent->fae; |
| 558 | } | 563 | } |
| 559 | 564 | ||
| 565 | static struct fanotify_event *fanotify_alloc_mnt_event(u64 mnt_id, gfp_t gfp) | ||
| 566 | { | ||
| 567 | struct fanotify_mnt_event *pevent; | ||
| 568 | |||
| 569 | pevent = kmem_cache_alloc(fanotify_mnt_event_cachep, gfp); | ||
| 570 | if (!pevent) | ||
| 571 | return NULL; | ||
| 572 | |||
| 573 | pevent->fae.type = FANOTIFY_EVENT_TYPE_MNT; | ||
| 574 | pevent->mnt_id = mnt_id; | ||
| 575 | |||
| 576 | return &pevent->fae; | ||
| 577 | } | ||
| 578 | |||
| 560 | static struct fanotify_event *fanotify_alloc_perm_event(const void *data, | 579 | static struct fanotify_event *fanotify_alloc_perm_event(const void *data, |
| 561 | int data_type, | 580 | int data_type, |
| 562 | gfp_t gfp) | 581 | gfp_t gfp) |
| @@ -731,6 +750,7 @@ static struct fanotify_event *fanotify_alloc_event( · 168 unmodified lines | |||
| 731 | fid_mode); | 750 | fid_mode); |
| 732 | struct inode *dirid = fanotify_dfid_inode(mask, data, data_type, dir); | 751 | struct inode *dirid = fanotify_dfid_inode(mask, data, data_type, dir); |
| 733 | const struct path *path = fsnotify_data_path(data, data_type); | 752 | const struct path *path = fsnotify_data_path(data, data_type); |
| 753 | u64 mnt_id = fsnotify_data_mnt_id(data, data_type); | ||
| 734 | struct mem_cgroup *old_memcg; | 754 | struct mem_cgroup *old_memcg; |
| 735 | struct dentry *moved = NULL; | 755 | struct dentry *moved = NULL; |
| 736 | struct inode *child = NULL; | 756 | struct inode *child = NULL; |
| @@ -826,8 +846,12 @@ static struct fanotify_event *fanotify_alloc_event( · 89 unmodified lines | |||
| 826 | moved, &hash, gfp); | 846 | moved, &hash, gfp); |
| 827 | } else if (fid_mode) { | 847 | } else if (fid_mode) { |
| 828 | event = fanotify_alloc_fid_event(id, fsid, &hash, gfp); | 848 | event = fanotify_alloc_fid_event(id, fsid, &hash, gfp); |
| 829 | } else { | 849 | } else if (path) { |
| 830 | event = fanotify_alloc_path_event(path, &hash, gfp); | 850 | event = fanotify_alloc_path_event(path, &hash, gfp); |
| 851 | } else if (mnt_id) { | ||
| 852 | event = fanotify_alloc_mnt_event(mnt_id, gfp); | ||
| 853 | } else { | ||
| 854 | WARN_ON_ONCE(1); | ||
| 831 | } | 855 | } |
| 832 | 856 | ||
| 833 | if (!event) | 857 | if (!event) |
| @@ -927,7 +951,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, u32 mask, · 93 unmodified lines | |||
| 927 | BUILD_BUG_ON(FAN_RENAME != FS_RENAME); | 951 | BUILD_BUG_ON(FAN_RENAME != FS_RENAME); |
| 928 | BUILD_BUG_ON(FAN_PRE_ACCESS != FS_PRE_ACCESS); | 952 | BUILD_BUG_ON(FAN_PRE_ACCESS != FS_PRE_ACCESS); |
| 929 | 953 | ||
| 930 | BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 22); | 954 | BUILD_BUG_ON(HWEIGHT32(ALL_FANOTIFY_EVENT_BITS) != 24); |
| 931 | 955 | ||
| 932 | mask = fanotify_group_event_mask(group, iter_info, &match_mask, | 956 | mask = fanotify_group_event_mask(group, iter_info, &match_mask, |
| 933 | mask, data, data_type, dir); | 957 | mask, data, data_type, dir); |
| @@ -1028,6 +1052,11 @@ static void fanotify_free_error_event(struct fsnotify_group *group, · 94 unmodified lines | |||
| 1028 | mempool_free(fee, &group->fanotify_data.error_events_pool); | 1052 | mempool_free(fee, &group->fanotify_data.error_events_pool); |
| 1029 | } | 1053 | } |
| 1030 | 1054 | ||
| 1055 | static void fanotify_free_mnt_event(struct fanotify_event *event) | ||
| 1056 | { | ||
| 1057 | kmem_cache_free(fanotify_mnt_event_cachep, FANOTIFY_ME(event)); | ||
| 1058 | } | ||
| 1059 | |||
| 1031 | static void fanotify_free_event(struct fsnotify_group *group, | 1060 | static void fanotify_free_event(struct fsnotify_group *group, |
| 1032 | struct fsnotify_event *fsn_event) | 1061 | struct fsnotify_event *fsn_event) |
| 1033 | { | 1062 | { |
| @@ -1054,6 +1083,9 @@ static void fanotify_free_event(struct fsnotify_group *group, · 20 unmodified lines | |||
| 1054 | case FANOTIFY_EVENT_TYPE_FS_ERROR: | 1083 | case FANOTIFY_EVENT_TYPE_FS_ERROR: |
| 1055 | fanotify_free_error_event(group, event); | 1084 | fanotify_free_error_event(group, event); |
| 1056 | break; | 1085 | break; |
| 1086 | case FANOTIFY_EVENT_TYPE_MNT: | ||
| 1087 | fanotify_free_mnt_event(event); | ||
| 1088 | break; | ||
| 1057 | default: | 1089 | default: |
| 1058 | WARN_ON_ONCE(1); | 1090 | WARN_ON_ONCE(1); |
| 1059 | } | 1091 | } |
| @@ -9,6 +9,7 @@ extern struct kmem_cache *fanotify_mark_cache; · 8 unmodified lines | |||
| 9 | extern struct kmem_cache *fanotify_fid_event_cachep; | 9 | extern struct kmem_cache *fanotify_fid_event_cachep; |
| 10 | extern struct kmem_cache *fanotify_path_event_cachep; | 10 | extern struct kmem_cache *fanotify_path_event_cachep; |
| 11 | extern struct kmem_cache *fanotify_perm_event_cachep; | 11 | extern struct kmem_cache *fanotify_perm_event_cachep; |
| 12 | extern struct kmem_cache *fanotify_mnt_event_cachep; | ||
| 12 | 13 | ||
| 13 | /* Possible states of the permission event */ | 14 | /* Possible states of the permission event */ |
| 14 | enum { | 15 | enum { |
| @@ -244,6 +245,7 @@ enum fanotify_event_type { · 229 unmodified lines | |||
| 244 | FANOTIFY_EVENT_TYPE_PATH_PERM, | 245 | FANOTIFY_EVENT_TYPE_PATH_PERM, |
| 245 | FANOTIFY_EVENT_TYPE_OVERFLOW, /* struct fanotify_event */ | 246 | FANOTIFY_EVENT_TYPE_OVERFLOW, /* struct fanotify_event */ |
| 246 | FANOTIFY_EVENT_TYPE_FS_ERROR, /* struct fanotify_error_event */ | 247 | FANOTIFY_EVENT_TYPE_FS_ERROR, /* struct fanotify_error_event */ |
| 248 | FANOTIFY_EVENT_TYPE_MNT, | ||
| 247 | __FANOTIFY_EVENT_TYPE_NUM | 249 | __FANOTIFY_EVENT_TYPE_NUM |
| 248 | }; | 250 | }; |
| 249 | 251 | ||
| @@ -409,12 +411,23 @@ struct fanotify_path_event { · 159 unmodified lines | |||
| 409 | struct path path; | 411 | struct path path; |
| 410 | }; | 412 | }; |
| 411 | 413 | ||
| 414 | struct fanotify_mnt_event { | ||
| 415 | struct fanotify_event fae; | ||
| 416 | u64 mnt_id; | ||
| 417 | }; | ||
| 418 | |||
| 412 | static inline struct fanotify_path_event * | 419 | static inline struct fanotify_path_event * |
| 413 | FANOTIFY_PE(struct fanotify_event *event) | 420 | FANOTIFY_PE(struct fanotify_event *event) |
| 414 | { | 421 | { |
| 415 | return container_of(event, struct fanotify_path_event, fae); | 422 | return container_of(event, struct fanotify_path_event, fae); |
| 416 | } | 423 | } |
| 417 | 424 | ||
| 425 | static inline struct fanotify_mnt_event * | ||
| 426 | FANOTIFY_ME(struct fanotify_event *event) | ||
| 427 | { | ||
| 428 | return container_of(event, struct fanotify_mnt_event, fae); | ||
| 429 | } | ||
| 430 | |||
| 418 | /* | 431 | /* |
| 419 | * Structure for permission fanotify events. It gets allocated and freed in | 432 | * Structure for permission fanotify events. It gets allocated and freed in |
| 420 | * fanotify_handle_event() since we wait there for user response. When the | 433 | * fanotify_handle_event() since we wait there for user response. When the |
| @@ -466,6 +479,11 @@ static inline bool fanotify_is_error_event(u32 mask) · 45 unmodified lines | |||
| 466 | return mask & FAN_FS_ERROR; | 479 | return mask & FAN_FS_ERROR; |
| 467 | } | 480 | } |
| 468 | 481 | ||
| 482 | static inline bool fanotify_is_mnt_event(u32 mask) | ||
| 483 | { | ||
| 484 | return mask & (FAN_MNT_ATTACH | FAN_MNT_DETACH); | ||
| 485 | } | ||
| 486 | |||
| 469 | static inline const struct path *fanotify_event_path(struct fanotify_event *event) | 487 | static inline const struct path *fanotify_event_path(struct fanotify_event *event) |
| 470 | { | 488 | { |
| 471 | if (event->type == FANOTIFY_EVENT_TYPE_PATH) | 489 | if (event->type == FANOTIFY_EVENT_TYPE_PATH) |
| @@ -113,6 +113,7 @@ struct kmem_cache *fanotify_mark_cache __ro_after_init; · 112 unmodified lines | |||
| 113 | struct kmem_cache *fanotify_fid_event_cachep __ro_after_init; | 113 | struct kmem_cache *fanotify_fid_event_cachep __ro_after_init; |
| 114 | struct kmem_cache *fanotify_path_event_cachep __ro_after_init; | 114 | struct kmem_cache *fanotify_path_event_cachep __ro_after_init; |
| 115 | struct kmem_cache *fanotify_perm_event_cachep __ro_after_init; | 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 | #define FANOTIFY_EVENT_ALIGN 4 | 118 | #define FANOTIFY_EVENT_ALIGN 4 |
| 118 | #define FANOTIFY_FID_INFO_HDR_LEN \ | 119 | #define FANOTIFY_FID_INFO_HDR_LEN \ |
| @@ -123,6 +124,8 @@ struct kmem_cache *fanotify_perm_event_cachep __ro_after_init; · 4 unmodified lines | |||
| 123 | (sizeof(struct fanotify_event_info_error)) | 124 | (sizeof(struct fanotify_event_info_error)) |
| 124 | #define FANOTIFY_RANGE_INFO_LEN \ | 125 | #define FANOTIFY_RANGE_INFO_LEN \ |
| 125 | (sizeof(struct fanotify_event_info_range)) | 126 | (sizeof(struct fanotify_event_info_range)) |
| 127 | #define FANOTIFY_MNT_INFO_LEN \ | ||
| 128 | (sizeof(struct fanotify_event_info_mnt)) | ||
| 126 | 129 | ||
| 127 | static int fanotify_fid_info_len(int fh_len, int name_len) | 130 | static int fanotify_fid_info_len(int fh_len, int name_len) |
| 128 | { | 131 | { |
| @@ -178,6 +181,8 @@ static size_t fanotify_event_len(unsigned int info_mode, · 49 unmodified lines | |||
| 178 | fh_len = fanotify_event_object_fh_len(event); | 181 | fh_len = fanotify_event_object_fh_len(event); |
| 179 | event_len += fanotify_fid_info_len(fh_len, dot_len); | 182 | event_len += fanotify_fid_info_len(fh_len, dot_len); |
| 180 | } | 183 | } |
| 184 | if (fanotify_is_mnt_event(event->mask)) | ||
| 185 | event_len += FANOTIFY_MNT_INFO_LEN; | ||
| 181 | 186 | ||
| 182 | if (info_mode & FAN_REPORT_PIDFD) | 187 | if (info_mode & FAN_REPORT_PIDFD) |
| 183 | event_len += FANOTIFY_PIDFD_INFO_LEN; | 188 | event_len += FANOTIFY_PIDFD_INFO_LEN; |
| @@ -405,6 +410,25 @@ static int process_access_response(struct fsnotify_group *group, · 221 unmodified lines | |||
| 405 | return -ENOENT; | 410 | return -ENOENT; |
| 406 | } | 411 | } |
| 407 | 412 | ||
| 413 | static size_t copy_mnt_info_to_user(struct fanotify_event *event, | ||
| 414 | char __user *buf, int count) | ||
| 415 | { | ||
| 416 | struct fanotify_event_info_mnt info = { }; | ||
| 417 | |||
| 418 | info.hdr.info_type = FAN_EVENT_INFO_TYPE_MNT; | ||
| 419 | info.hdr.len = FANOTIFY_MNT_INFO_LEN; | ||
| 420 | |||
| 421 | if (WARN_ON(count < info.hdr.len)) | ||
| 422 | return -EFAULT; | ||
| 423 | |||
| 424 | info.mnt_id = FANOTIFY_ME(event)->mnt_id; | ||
| 425 | |||
| 426 | if (copy_to_user(buf, &info, sizeof(info))) | ||
| 427 | return -EFAULT; | ||
| 428 | |||
| 429 | return info.hdr.len; | ||
| 430 | } | ||
| 431 | |||
| 408 | static size_t copy_error_info_to_user(struct fanotify_event *event, | 432 | static size_t copy_error_info_to_user(struct fanotify_event *event, |
| 409 | char __user *buf, int count) | 433 | char __user *buf, int count) |
| 410 | { | 434 | { |
| @@ -700,6 +724,15 @@ static int copy_info_records_to_user(struct fanotify_event *event, · 289 unmodified lines | |||
| 700 | total_bytes += ret; | 724 | total_bytes += ret; |
| 701 | } | 725 | } |
| 702 | 726 | ||
| 727 | if (fanotify_is_mnt_event(event->mask)) { | ||
| 728 | ret = copy_mnt_info_to_user(event, buf, count); | ||
| 729 | if (ret < 0) | ||
| 730 | return ret; | ||
| 731 | buf += ret; | ||
| 732 | count -= ret; | ||
| 733 | total_bytes += ret; | ||
| 734 | } | ||
| 735 | |||
| 703 | return total_bytes; | 736 | return total_bytes; |
| 704 | } | 737 | } |
| 705 | 738 | ||
| @@ -1508,6 +1541,14 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) · 802 unmodified lines | |||
| 1508 | if ((flags & FAN_REPORT_PIDFD) && (flags & FAN_REPORT_TID)) | 1541 | if ((flags & FAN_REPORT_PIDFD) && (flags & FAN_REPORT_TID)) |
| 1509 | return -EINVAL; | 1542 | return -EINVAL; |
| 1510 | 1543 | ||
| 1544 | /* Don't allow mixing mnt events with inode events for now */ | ||
| 1545 | if (flags & FAN_REPORT_MNT) { | ||
| 1546 | if (class != FAN_CLASS_NOTIF) | ||
| 1547 | return -EINVAL; | ||
| 1548 | if (flags & (FANOTIFY_FID_BITS | FAN_REPORT_FD_ERROR)) | ||
| 1549 | return -EINVAL; | ||
| 1550 | } | ||
| 1551 | |||
| 1511 | if (event_f_flags & ~FANOTIFY_INIT_ALL_EVENT_F_BITS) | 1552 | if (event_f_flags & ~FANOTIFY_INIT_ALL_EVENT_F_BITS) |
| 1512 | return -EINVAL; | 1553 | return -EINVAL; |
| 1513 | 1554 | ||
| @@ -1767,7 +1808,6 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, · 253 unmodified lines | |||
| 1767 | int dfd, const char __user *pathname) | 1808 | int dfd, const char __user *pathname) |
| 1768 | { | 1809 | { |
| 1769 | struct inode *inode = NULL; | 1810 | struct inode *inode = NULL; |
| 1770 | struct vfsmount *mnt = NULL; | ||
| 1771 | struct fsnotify_group *group; | 1811 | struct fsnotify_group *group; |
| 1772 | struct path path; | 1812 | struct path path; |
| 1773 | struct fan_fsid __fsid, *fsid = NULL; | 1813 | struct fan_fsid __fsid, *fsid = NULL; |
| @@ -1776,7 +1816,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, · 2 unmodified lines | |||
| 1776 | unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS; | 1816 | unsigned int mark_cmd = flags & FANOTIFY_MARK_CMD_BITS; |
| 1777 | unsigned int ignore = flags & FANOTIFY_MARK_IGNORE_BITS; | 1817 | unsigned int ignore = flags & FANOTIFY_MARK_IGNORE_BITS; |
| 1778 | unsigned int obj_type, fid_mode; | 1818 | unsigned int obj_type, fid_mode; |
| 1779 | void *obj; | 1819 | void *obj = NULL; |
| 1780 | u32 umask = 0; | 1820 | u32 umask = 0; |
| 1781 | int ret; | 1821 | int ret; |
| 1782 | 1822 | ||
| @@ -1800,6 +1840,9 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, · 17 unmodified lines | |||
| 1800 | case FAN_MARK_FILESYSTEM: | 1840 | case FAN_MARK_FILESYSTEM: |
| 1801 | obj_type = FSNOTIFY_OBJ_TYPE_SB; | 1841 | obj_type = FSNOTIFY_OBJ_TYPE_SB; |
| 1802 | break; | 1842 | break; |
| 1843 | case FAN_MARK_MNTNS: | ||
| 1844 | obj_type = FSNOTIFY_OBJ_TYPE_MNTNS; | ||
| 1845 | break; | ||
| 1803 | default: | 1846 | default: |
| 1804 | return -EINVAL; | 1847 | return -EINVAL; |
| 1805 | } | 1848 | } |
| @@ -1847,6 +1890,19 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, · 41 unmodified lines | |||
| 1847 | return -EINVAL; | 1890 | return -EINVAL; |
| 1848 | group = fd_file(f)->private_data; | 1891 | group = fd_file(f)->private_data; |
| 1849 | 1892 | ||
| 1893 | /* Only report mount events on mnt namespace */ | ||
| 1894 | if (FAN_GROUP_FLAG(group, FAN_REPORT_MNT)) { | ||
| 1895 | if (mask & ~FANOTIFY_MOUNT_EVENTS) | ||
| 1896 | return -EINVAL; | ||
| 1897 | if (mark_type != FAN_MARK_MNTNS) | ||
| 1898 | return -EINVAL; | ||
| 1899 | } else { | ||
| 1900 | if (mask & FANOTIFY_MOUNT_EVENTS) | ||
| 1901 | return -EINVAL; | ||
| 1902 | if (mark_type == FAN_MARK_MNTNS) | ||
| 1903 | return -EINVAL; | ||
| 1904 | } | ||
| 1905 | |||
| 1850 | /* | 1906 | /* |
| 1851 | * An unprivileged user is not allowed to setup mount nor filesystem | 1907 | * An unprivileged user is not allowed to setup mount nor filesystem |
| 1852 | * marks. This also includes setting up such marks by a group that | 1908 | * marks. This also includes setting up such marks by a group that |
| @@ -1888,7 +1944,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, · 35 unmodified lines | |||
| 1888 | * point. | 1944 | * point. |
| 1889 | */ | 1945 | */ |
| 1890 | fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS); | 1946 | fid_mode = FAN_GROUP_FLAG(group, FANOTIFY_FID_BITS); |
| 1891 | if (mask & ~(FANOTIFY_FD_EVENTS|FANOTIFY_EVENT_FLAGS) && | 1947 | if (mask & ~(FANOTIFY_FD_EVENTS|FANOTIFY_MOUNT_EVENTS|FANOTIFY_EVENT_FLAGS) && |
| 1892 | (!fid_mode || mark_type == FAN_MARK_MOUNT)) | 1948 | (!fid_mode || mark_type == FAN_MARK_MOUNT)) |
| 1893 | return -EINVAL; | 1949 | return -EINVAL; |
| 1894 | 1950 | ||
| @@ -1938,17 +1994,21 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, · 43 unmodified lines | |||
| 1938 | } | 1994 | } |
| 1939 | 1995 | ||
| 1940 | /* inode held in place by reference to path; group by fget on fd */ | 1996 | /* inode held in place by reference to path; group by fget on fd */ |
| 1941 | if (mark_type == FAN_MARK_INODE) { | 1997 | if (obj_type == FSNOTIFY_OBJ_TYPE_INODE) { |
| 1942 | inode = path.dentry->d_inode; | 1998 | inode = path.dentry->d_inode; |
| 1943 | obj = inode; | 1999 | obj = inode; |
| 1944 | } else { | 2000 | } else if (obj_type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) { |
| 1945 | mnt = path.mnt; | 2001 | obj = path.mnt; |
| 1946 | if (mark_type == FAN_MARK_MOUNT) | 2002 | } else if (obj_type == FSNOTIFY_OBJ_TYPE_SB) { |
| 1947 | obj = mnt; | 2003 | obj = path.mnt->mnt_sb; |
| 1948 | else | 2004 | } else if (obj_type == FSNOTIFY_OBJ_TYPE_MNTNS) { |
| 1949 | obj = mnt->mnt_sb; | 2005 | obj = mnt_ns_from_dentry(path.dentry); |
| 1950 | } | 2006 | } |
| 1951 | 2007 | ||
| 2008 | ret = -EINVAL; | ||
| 2009 | if (!obj) | ||
| 2010 | goto path_put_and_out; | ||
| 2011 | |||
| 1952 | /* | 2012 | /* |
| 1953 | * If some other task has this inode open for write we should not add | 2013 | * If some other task has this inode open for write we should not add |
| 1954 | * an ignore mask, unless that ignore mask is supposed to survive | 2014 | * an ignore mask, unless that ignore mask is supposed to survive |
| @@ -1956,10 +2016,10 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, · 1 unmodified line | |||
| 1956 | */ | 2016 | */ |
| 1957 | if (mark_cmd == FAN_MARK_ADD && (flags & FANOTIFY_MARK_IGNORE_BITS) && | 2017 | if (mark_cmd == FAN_MARK_ADD && (flags & FANOTIFY_MARK_IGNORE_BITS) && |
| 1958 | !(flags & FAN_MARK_IGNORED_SURV_MODIFY)) { | 2018 | !(flags & FAN_MARK_IGNORED_SURV_MODIFY)) { |
| 1959 | ret = mnt ? -EINVAL : -EISDIR; | 2019 | ret = !inode ? -EINVAL : -EISDIR; |
| 1960 | /* FAN_MARK_IGNORE requires SURV_MODIFY for sb/mount/dir marks */ | 2020 | /* FAN_MARK_IGNORE requires SURV_MODIFY for sb/mount/dir marks */ |
| 1961 | if (ignore == FAN_MARK_IGNORE && | 2021 | if (ignore == FAN_MARK_IGNORE && |
| 1962 | (mnt || S_ISDIR(inode->i_mode))) | 2022 | (!inode || S_ISDIR(inode->i_mode))) |
| 1963 | goto path_put_and_out; | 2023 | goto path_put_and_out; |
| 1964 | 2024 | ||
| 1965 | ret = 0; | 2025 | ret = 0; |
| @@ -1968,7 +2028,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask, · 2 unmodified lines | |||
| 1968 | } | 2028 | } |
| 1969 | 2029 | ||
| 1970 | /* Mask out FAN_EVENT_ON_CHILD flag for sb/mount/non-dir marks */ | 2030 | /* Mask out FAN_EVENT_ON_CHILD flag for sb/mount/non-dir marks */ |
| 1971 | if (mnt || !S_ISDIR(inode->i_mode)) { | 2031 | if (!inode || !S_ISDIR(inode->i_mode)) { |
| 1972 | mask &= ~FAN_EVENT_ON_CHILD; | 2032 | mask &= ~FAN_EVENT_ON_CHILD; |
| 1973 | umask = FAN_EVENT_ON_CHILD; | 2033 | umask = FAN_EVENT_ON_CHILD; |
| 1974 | /* | 2034 | /* |
| @@ -2042,7 +2102,7 @@ static int __init fanotify_user_setup(void) · 67 unmodified lines | |||
| 2042 | FANOTIFY_DEFAULT_MAX_USER_MARKS); | 2102 | FANOTIFY_DEFAULT_MAX_USER_MARKS); |
| 2043 | 2103 | ||
| 2044 | BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS); | 2104 | BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS); |
| 2045 | BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 13); | 2105 | BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 14); |
| 2046 | BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 11); | 2106 | BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 11); |
| 2047 | 2107 | ||
| 2048 | fanotify_mark_cache = KMEM_CACHE(fanotify_mark, | 2108 | fanotify_mark_cache = KMEM_CACHE(fanotify_mark, |
| @@ -2055,6 +2115,7 @@ static int __init fanotify_user_setup(void) · 6 unmodified lines | |||
| 2055 | fanotify_perm_event_cachep = | 2115 | fanotify_perm_event_cachep = |
| 2056 | KMEM_CACHE(fanotify_perm_event, SLAB_PANIC); | 2116 | KMEM_CACHE(fanotify_perm_event, SLAB_PANIC); |
| 2057 | } | 2117 | } |
| 2118 | fanotify_mnt_event_cachep = KMEM_CACHE(fanotify_mnt_event, SLAB_PANIC); | ||
| 2058 | 2119 | ||
| 2059 | fanotify_max_queued_events = FANOTIFY_DEFAULT_MAX_EVENTS; | 2120 | fanotify_max_queued_events = FANOTIFY_DEFAULT_MAX_EVENTS; |
| 2060 | init_user_ns.ucount_max[UCOUNT_FANOTIFY_GROUPS] = | 2121 | init_user_ns.ucount_max[UCOUNT_FANOTIFY_GROUPS] = |
| @@ -121,6 +121,11 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark) · 120 unmodified lines | |||
| 121 | 121 | ||
| 122 | seq_printf(m, "fanotify sdev:%x mflags:%x mask:%x ignored_mask:%x\n", | 122 | seq_printf(m, "fanotify sdev:%x mflags:%x mask:%x ignored_mask:%x\n", |
| 123 | sb->s_dev, mflags, mark->mask, mark->ignore_mask); | 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 | ||
| @@ -28,6 +28,11 @@ void __fsnotify_vfsmount_delete(struct vfsmount *mnt) · 27 unmodified lines | |||
| 28 | fsnotify_clear_marks_by_mount(mnt); | 28 | fsnotify_clear_marks_by_mount(mnt); |
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | void __fsnotify_mntns_delete(struct mnt_namespace *mntns) | ||
| 32 | { | ||
| 33 | fsnotify_clear_marks_by_mntns(mntns); | ||
| 34 | } | ||
| 35 | |||
| 31 | /** | 36 | /** |
| 32 | * fsnotify_unmount_inodes - an sb is unmounting. handle any watched inodes. | 37 | * fsnotify_unmount_inodes - an sb is unmounting. handle any watched inodes. |
| 33 | * @sb: superblock being unmounted. | 38 | * @sb: superblock being unmounted. |
| @@ -420,7 +425,7 @@ static int send_to_group(__u32 mask, const void *data, int data_type, · 386 unmodified lines | |||
| 420 | file_name, cookie, iter_info); | 425 | file_name, cookie, iter_info); |
| 421 | } | 426 | } |
| 422 | 427 | ||
| 423 | static struct fsnotify_mark *fsnotify_first_mark(struct fsnotify_mark_connector **connp) | 428 | static struct fsnotify_mark *fsnotify_first_mark(struct fsnotify_mark_connector *const *connp) |
| 424 | { | 429 | { |
| 425 | struct fsnotify_mark_connector *conn; | 430 | struct fsnotify_mark_connector *conn; |
| 426 | struct hlist_node *node = NULL; | 431 | struct hlist_node *node = NULL; |
| @@ -538,14 +543,15 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir, · 111 unmodified lines | |||
| 538 | { | 543 | { |
| 539 | const struct path *path = fsnotify_data_path(data, data_type); | 544 | const struct path *path = fsnotify_data_path(data, data_type); |
| 540 | struct super_block *sb = fsnotify_data_sb(data, data_type); | 545 | struct super_block *sb = fsnotify_data_sb(data, data_type); |
| 541 | struct fsnotify_sb_info *sbinfo = fsnotify_sb_info(sb); | 546 | const struct fsnotify_mnt *mnt_data = fsnotify_data_mnt(data, data_type); |
| 547 | struct fsnotify_sb_info *sbinfo = sb ? fsnotify_sb_info(sb) : NULL; | ||
| 542 | struct fsnotify_iter_info iter_info = {}; | 548 | struct fsnotify_iter_info iter_info = {}; |
| 543 | struct mount *mnt = NULL; | 549 | struct mount *mnt = NULL; |
| 544 | struct inode *inode2 = NULL; | 550 | struct inode *inode2 = NULL; |
| 545 | struct dentry *moved; | 551 | struct dentry *moved; |
| 546 | int inode2_type; | 552 | int inode2_type; |
| 547 | int ret = 0; | 553 | int ret = 0; |
| 548 | __u32 test_mask, marks_mask; | 554 | __u32 test_mask, marks_mask = 0; |
| 549 | 555 | ||
| 550 | if (path) | 556 | if (path) |
| 551 | mnt = real_mount(path->mnt); | 557 | mnt = real_mount(path->mnt); |
| @@ -578,17 +584,20 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir, · 26 unmodified lines | |||
| 578 | if ((!sbinfo || !sbinfo->sb_marks) && | 584 | if ((!sbinfo || !sbinfo->sb_marks) && |
| 579 | (!mnt || !mnt->mnt_fsnotify_marks) && | 585 | (!mnt || !mnt->mnt_fsnotify_marks) && |
| 580 | (!inode || !inode->i_fsnotify_marks) && | 586 | (!inode || !inode->i_fsnotify_marks) && |
| 581 | (!inode2 || !inode2->i_fsnotify_marks)) | 587 | (!inode2 || !inode2->i_fsnotify_marks) && |
| 588 | (!mnt_data || !mnt_data->ns->n_fsnotify_marks)) | ||
| 582 | return 0; | 589 | return 0; |
| 583 | 590 | ||
| 584 | marks_mask = READ_ONCE(sb->s_fsnotify_mask); | 591 | if (sb) |
| 592 | marks_mask |= READ_ONCE(sb->s_fsnotify_mask); | ||
| 585 | if (mnt) | 593 | if (mnt) |
| 586 | marks_mask |= READ_ONCE(mnt->mnt_fsnotify_mask); | 594 | marks_mask |= READ_ONCE(mnt->mnt_fsnotify_mask); |
| 587 | if (inode) | 595 | if (inode) |
| 588 | marks_mask |= READ_ONCE(inode->i_fsnotify_mask); | 596 | marks_mask |= READ_ONCE(inode->i_fsnotify_mask); |
| 589 | if (inode2) | 597 | if (inode2) |
| 590 | marks_mask |= READ_ONCE(inode2->i_fsnotify_mask); | 598 | marks_mask |= READ_ONCE(inode2->i_fsnotify_mask); |
| 591 | 599 | if (mnt_data) | |
| 600 | marks_mask |= READ_ONCE(mnt_data->ns->n_fsnotify_mask); | ||
| 592 | 601 | ||
| 593 | /* | 602 | /* |
| 594 | * If this is a modify event we may need to clear some ignore masks. | 603 | * If this is a modify event we may need to clear some ignore masks. |
| @@ -618,6 +627,10 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir, · 23 unmodified lines | |||
| 618 | iter_info.marks[inode2_type] = | 627 | iter_info.marks[inode2_type] = |
| 619 | fsnotify_first_mark(&inode2->i_fsnotify_marks); | 628 | fsnotify_first_mark(&inode2->i_fsnotify_marks); |
| 620 | } | 629 | } |
| 630 | if (mnt_data) { | ||
| 631 | iter_info.marks[FSNOTIFY_ITER_TYPE_MNTNS] = | ||
| 632 | fsnotify_first_mark(&mnt_data->ns->n_fsnotify_marks); | ||
| 633 | } | ||
| 621 | 634 | ||
| 622 | /* | 635 | /* |
| 623 | * We need to merge inode/vfsmount/sb mark lists so that e.g. inode mark | 636 | * We need to merge inode/vfsmount/sb mark lists so that e.g. inode mark |
| @@ -708,11 +721,31 @@ void file_set_fsnotify_mode_from_watchers(struct file *file) · 84 unmodified lines | |||
| 708 | } | 721 | } |
| 709 | #endif | 722 | #endif |
| 710 | 723 | ||
| 724 | void fsnotify_mnt(__u32 mask, struct mnt_namespace *ns, struct vfsmount *mnt) | ||
| 725 | { | ||
| 726 | struct fsnotify_mnt data = { | ||
| 727 | .ns = ns, | ||
| 728 | .mnt_id = real_mount(mnt)->mnt_id_unique, | ||
| 729 | }; | ||
| 730 | |||
| 731 | if (WARN_ON_ONCE(!ns)) | ||
| 732 | return; | ||
| 733 | |||
| 734 | /* | ||
| 735 | * This is an optimization as well as making sure fsnotify_init() has | ||
| 736 | * been called. | ||
| 737 | */ | ||
| 738 | if (!ns->n_fsnotify_marks) | ||
| 739 | return; | ||
| 740 | |||
| 741 | fsnotify(mask, &data, FSNOTIFY_EVENT_MNT, NULL, NULL, NULL, 0); | ||
| 742 | } | ||
| 743 | |||
| 711 | static __init int fsnotify_init(void) | 744 | static __init int fsnotify_init(void) |
| 712 | { | 745 | { |
| 713 | int ret; | 746 | int ret; |
| 714 | 747 | ||
| 715 | BUILD_BUG_ON(HWEIGHT32(ALL_FSNOTIFY_BITS) != 24); | 748 | BUILD_BUG_ON(HWEIGHT32(ALL_FSNOTIFY_BITS) != 26); |
| 716 | 749 | ||
| 717 | ret = init_srcu_struct(&fsnotify_mark_srcu); | 750 | ret = init_srcu_struct(&fsnotify_mark_srcu); |
| 718 | if (ret) | 751 | if (ret) |
| @@ -33,6 +33,12 @@ static inline struct super_block *fsnotify_conn_sb( · 32 unmodified lines | |||
| 33 | return conn->obj; | 33 | return conn->obj; |
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | static inline struct mnt_namespace *fsnotify_conn_mntns( | ||
| 37 | struct fsnotify_mark_connector *conn) | ||
| 38 | { | ||
| 39 | return conn->obj; | ||
| 40 | } | ||
| 41 | |||
| 36 | static inline struct super_block *fsnotify_object_sb(void *obj, | 42 | static inline struct super_block *fsnotify_object_sb(void *obj, |
| 37 | enum fsnotify_obj_type obj_type) | 43 | enum fsnotify_obj_type obj_type) |
| 38 | { | 44 | { |
| @@ -89,6 +95,11 @@ static inline void fsnotify_clear_marks_by_sb(struct super_block *sb) · 50 unmodified lines | |||
| 89 | fsnotify_destroy_marks(fsnotify_sb_marks(sb)); | 95 | fsnotify_destroy_marks(fsnotify_sb_marks(sb)); |
| 90 | } | 96 | } |
| 91 | 97 | ||
| 98 | static inline void fsnotify_clear_marks_by_mntns(struct mnt_namespace *mntns) | ||
| 99 | { | ||
| 100 | fsnotify_destroy_marks(&mntns->n_fsnotify_marks); | ||
| 101 | } | ||
| 102 | |||
| 92 | /* | 103 | /* |
| 93 | * update the dentry->d_flags of all of inode's children to indicate if inode cares | 104 | * update the dentry->d_flags of all of inode's children to indicate if inode cares |
| 94 | * about events that happen to its children. | 105 | * about events that happen to its children. |
| @@ -107,6 +107,8 @@ static fsnotify_connp_t *fsnotify_object_connp(void *obj, · 106 unmodified lines | |||
| 107 | return &real_mount(obj)->mnt_fsnotify_marks; | 107 | return &real_mount(obj)->mnt_fsnotify_marks; |
| 108 | case FSNOTIFY_OBJ_TYPE_SB: | 108 | case FSNOTIFY_OBJ_TYPE_SB: |
| 109 | return fsnotify_sb_marks(obj); | 109 | return fsnotify_sb_marks(obj); |
| 110 | case FSNOTIFY_OBJ_TYPE_MNTNS: | ||
| 111 | return &((struct mnt_namespace *)obj)->n_fsnotify_marks; | ||
| 110 | default: | 112 | default: |
| 111 | return NULL; | 113 | return NULL; |
| 112 | } | 114 | } |
| @@ -120,6 +122,8 @@ static __u32 *fsnotify_conn_mask_p(struct fsnotify_mark_connector *conn) · 7 unmodified lines | |||
| 120 | return &fsnotify_conn_mount(conn)->mnt_fsnotify_mask; | 122 | return &fsnotify_conn_mount(conn)->mnt_fsnotify_mask; |
| 121 | else if (conn->type == FSNOTIFY_OBJ_TYPE_SB) | 123 | else if (conn->type == FSNOTIFY_OBJ_TYPE_SB) |
| 122 | return &fsnotify_conn_sb(conn)->s_fsnotify_mask; | 124 | return &fsnotify_conn_sb(conn)->s_fsnotify_mask; |
| 125 | else if (conn->type == FSNOTIFY_OBJ_TYPE_MNTNS) | ||
| 126 | return &fsnotify_conn_mntns(conn)->n_fsnotify_mask; | ||
| 123 | return NULL; | 127 | return NULL; |
| 124 | } | 128 | } |
| 125 | 129 | ||
| @@ -346,12 +350,15 @@ static void *fsnotify_detach_connector_from_object( · 220 unmodified lines | |||
| 346 | fsnotify_conn_mount(conn)->mnt_fsnotify_mask = 0; | 350 | fsnotify_conn_mount(conn)->mnt_fsnotify_mask = 0; |
| 347 | } else if (conn->type == FSNOTIFY_OBJ_TYPE_SB) { | 351 | } else if (conn->type == FSNOTIFY_OBJ_TYPE_SB) { |
| 348 | fsnotify_conn_sb(conn)->s_fsnotify_mask = 0; | 352 | fsnotify_conn_sb(conn)->s_fsnotify_mask = 0; |
| 353 | } else if (conn->type == FSNOTIFY_OBJ_TYPE_MNTNS) { | ||
| 354 | fsnotify_conn_mntns(conn)->n_fsnotify_mask = 0; | ||
| 349 | } | 355 | } |
| 350 | 356 | ||
| 351 | rcu_assign_pointer(*connp, NULL); | 357 | rcu_assign_pointer(*connp, NULL); |
| 352 | conn->obj = NULL; | 358 | conn->obj = NULL; |
| 353 | conn->type = FSNOTIFY_OBJ_TYPE_DETACHED; | 359 | conn->type = FSNOTIFY_OBJ_TYPE_DETACHED; |
| 354 | fsnotify_update_sb_watchers(sb, conn); | 360 | if (sb) |
| 361 | fsnotify_update_sb_watchers(sb, conn); | ||
| 355 | 362 | ||
| 356 | return inode; | 363 | return inode; |
| 357 | } | 364 | } |
| @@ -724,7 +731,7 @@ static int fsnotify_add_mark_list(struct fsnotify_mark *mark, void *obj, · 366 unmodified lines | |||
| 724 | * Attach the sb info before attaching a connector to any object on sb. | 731 | * Attach the sb info before attaching a connector to any object on sb. |
| 725 | * The sb info will remain attached as long as sb lives. | 732 | * The sb info will remain attached as long as sb lives. |
| 726 | */ | 733 | */ |
| 727 | if (!fsnotify_sb_info(sb)) { | 734 | if (sb && !fsnotify_sb_info(sb)) { |
| 728 | err = fsnotify_attach_info_to_sb(sb); | 735 | err = fsnotify_attach_info_to_sb(sb); |
| 729 | if (err) | 736 | if (err) |
| 730 | return err; | 737 | return err; |
| @@ -770,7 +777,8 @@ restart: · 39 unmodified lines | |||
| 770 | /* mark should be the last entry. last is the current last entry */ | 777 | /* mark should be the last entry. last is the current last entry */ |
| 771 | hlist_add_behind_rcu(&mark->obj_list, &last->obj_list); | 778 | hlist_add_behind_rcu(&mark->obj_list, &last->obj_list); |
| 772 | added: | 779 | added: |
| 773 | fsnotify_update_sb_watchers(sb, conn); | 780 | if (sb) |
| 781 | fsnotify_update_sb_watchers(sb, conn); | ||
| 774 | /* | 782 | /* |
| 775 | * Since connector is attached to object using cmpxchg() we are | 783 | * Since connector is attached to object using cmpxchg() we are |
| 776 | * guaranteed that connector initialization is fully visible by anyone | 784 | * guaranteed that connector initialization is fully visible by anyone |
| @@ -549,8 +549,10 @@ static void restore_mounts(struct list_head *to_restore) · 548 unmodified lines | |||
| 549 | mp = parent->mnt_mp; | 549 | mp = parent->mnt_mp; |
| 550 | parent = parent->mnt_parent; | 550 | parent = parent->mnt_parent; |
| 551 | } | 551 | } |
| 552 | if (parent != mnt->mnt_parent) | 552 | if (parent != mnt->mnt_parent) { |
| 553 | mnt_change_mountpoint(parent, mp, mnt); | 553 | mnt_change_mountpoint(parent, mp, mnt); |
| 554 | mnt_notify_add(mnt); | ||
| 555 | } | ||
| 554 | } | 556 | } |
| 555 | } | 557 | } |
| 556 | 558 | ||
| @@ -25,7 +25,7 @@ · 24 unmodified lines | |||
| 25 | 25 | ||
| 26 | #define FANOTIFY_FID_BITS (FAN_REPORT_DFID_NAME_TARGET) | 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 | * fanotify_init() flags that require CAP_SYS_ADMIN. | 31 | * fanotify_init() flags that require CAP_SYS_ADMIN. |
| @@ -38,7 +38,8 @@ · 6 unmodified lines | |||
| 38 | FAN_REPORT_PIDFD | \ | 38 | FAN_REPORT_PIDFD | \ |
| 39 | FAN_REPORT_FD_ERROR | \ | 39 | FAN_REPORT_FD_ERROR | \ |
| 40 | FAN_UNLIMITED_QUEUE | \ | 40 | FAN_UNLIMITED_QUEUE | \ |
| 41 | FAN_UNLIMITED_MARKS) | 41 | FAN_UNLIMITED_MARKS | \ |
| 42 | FAN_REPORT_MNT) | ||
| 42 | 43 | ||
| 43 | /* | 44 | /* |
| 44 | * fanotify_init() flags that are allowed for user without CAP_SYS_ADMIN. | 45 | * fanotify_init() flags that are allowed for user without CAP_SYS_ADMIN. |
| @@ -58,7 +59,7 @@ · 13 unmodified lines | |||
| 58 | #define FANOTIFY_INTERNAL_GROUP_FLAGS (FANOTIFY_UNPRIV) | 59 | #define FANOTIFY_INTERNAL_GROUP_FLAGS (FANOTIFY_UNPRIV) |
| 59 | 60 | ||
| 60 | #define FANOTIFY_MARK_TYPE_BITS (FAN_MARK_INODE | FAN_MARK_MOUNT | \ | 61 | #define FANOTIFY_MARK_TYPE_BITS (FAN_MARK_INODE | FAN_MARK_MOUNT | \ |
| 61 | FAN_MARK_FILESYSTEM) | 62 | FAN_MARK_FILESYSTEM | FAN_MARK_MNTNS) |
| 62 | 63 | ||
| 63 | #define FANOTIFY_MARK_CMD_BITS (FAN_MARK_ADD | FAN_MARK_REMOVE | \ | 64 | #define FANOTIFY_MARK_CMD_BITS (FAN_MARK_ADD | FAN_MARK_REMOVE | \ |
| 64 | FAN_MARK_FLUSH) | 65 | FAN_MARK_FLUSH) |
| @@ -109,10 +110,13 @@ · 44 unmodified lines | |||
| 109 | /* Events that can only be reported with data type FSNOTIFY_EVENT_ERROR */ | 110 | /* Events that can only be reported with data type FSNOTIFY_EVENT_ERROR */ |
| 110 | #define FANOTIFY_ERROR_EVENTS (FAN_FS_ERROR) | 111 | #define FANOTIFY_ERROR_EVENTS (FAN_FS_ERROR) |
| 111 | 112 | ||
| 113 | #define FANOTIFY_MOUNT_EVENTS (FAN_MNT_ATTACH | FAN_MNT_DETACH) | ||
| 114 | |||
| 112 | /* Events that user can request to be notified on */ | 115 | /* Events that user can request to be notified on */ |
| 113 | #define FANOTIFY_EVENTS (FANOTIFY_PATH_EVENTS | \ | 116 | #define FANOTIFY_EVENTS (FANOTIFY_PATH_EVENTS | \ |
| 114 | FANOTIFY_INODE_EVENTS | \ | 117 | FANOTIFY_INODE_EVENTS | \ |
| 115 | FANOTIFY_ERROR_EVENTS) | 118 | FANOTIFY_ERROR_EVENTS | \ |
| 119 | FANOTIFY_MOUNT_EVENTS) | ||
| 116 | 120 | ||
| 117 | /* Extra flags that may be reported with event or control handling of events */ | 121 | /* Extra flags that may be reported with event or control handling of events */ |
| 118 | #define FANOTIFY_EVENT_FLAGS (FAN_EVENT_ON_CHILD | FAN_ONDIR) | 122 | #define FANOTIFY_EVENT_FLAGS (FAN_EVENT_ON_CHILD | FAN_ONDIR) |
| @@ -320,6 +320,11 @@ static inline void fsnotify_vfsmount_delete(struct vfsmount *mnt) · 319 unmodified lines | |||
| 320 | __fsnotify_vfsmount_delete(mnt); | 320 | __fsnotify_vfsmount_delete(mnt); |
| 321 | } | 321 | } |
| 322 | 322 | ||
| 323 | static inline void fsnotify_mntns_delete(struct mnt_namespace *mntns) | ||
| 324 | { | ||
| 325 | __fsnotify_mntns_delete(mntns); | ||
| 326 | } | ||
| 327 | |||
| 323 | /* | 328 | /* |
| 324 | * fsnotify_inoderemove - an inode is going away | 329 | * fsnotify_inoderemove - an inode is going away |
| 325 | */ | 330 | */ |
| @@ -528,4 +533,19 @@ static inline int fsnotify_sb_error(struct super_block *sb, struct inode *inode, · 202 unmodified lines | |||
| 528 | NULL, NULL, NULL, 0); | 533 | NULL, NULL, NULL, 0); |
| 529 | } | 534 | } |
| 530 | 535 | ||
| 536 | static inline void fsnotify_mnt_attach(struct mnt_namespace *ns, struct vfsmount *mnt) | ||
| 537 | { | ||
| 538 | fsnotify_mnt(FS_MNT_ATTACH, ns, mnt); | ||
| 539 | } | ||
| 540 | |||
| 541 | static inline void fsnotify_mnt_detach(struct mnt_namespace *ns, struct vfsmount *mnt) | ||
| 542 | { | ||
| 543 | fsnotify_mnt(FS_MNT_DETACH, ns, mnt); | ||
| 544 | } | ||
| 545 | |||
| 546 | static inline void fsnotify_mnt_move(struct mnt_namespace *ns, struct vfsmount *mnt) | ||
| 547 | { | ||
| 548 | fsnotify_mnt(FS_MNT_MOVE, ns, mnt); | ||
| 549 | } | ||
| 550 | |||
| 531 | #endif /* _LINUX_FS_NOTIFY_H */ | 551 | #endif /* _LINUX_FS_NOTIFY_H */ |
| @@ -59,6 +59,10 @@ · 58 unmodified lines | |||
| 59 | 59 | ||
| 60 | #define FS_PRE_ACCESS 0x00100000 /* Pre-content access hook */ | 60 | #define FS_PRE_ACCESS 0x00100000 /* Pre-content access hook */ |
| 61 | 61 | ||
| 62 | #define FS_MNT_ATTACH 0x01000000 /* Mount was attached */ | ||
| 63 | #define FS_MNT_DETACH 0x02000000 /* Mount was detached */ | ||
| 64 | #define FS_MNT_MOVE (FS_MNT_ATTACH | FS_MNT_DETACH) | ||
| 65 | |||
| 62 | /* | 66 | /* |
| 63 | * Set on inode mark that cares about things that happen to its children. | 67 | * Set on inode mark that cares about things that happen to its children. |
| 64 | * Always set for dnotify and inotify. | 68 | * Always set for dnotify and inotify. |
| @@ -80,6 +84,9 @@ · 15 unmodified lines | |||
| 80 | */ | 84 | */ |
| 81 | #define ALL_FSNOTIFY_DIRENT_EVENTS (FS_CREATE | FS_DELETE | FS_MOVE | FS_RENAME) | 85 | #define ALL_FSNOTIFY_DIRENT_EVENTS (FS_CREATE | FS_DELETE | FS_MOVE | FS_RENAME) |
| 82 | 86 | ||
| 87 | /* Mount namespace events */ | ||
| 88 | #define FSNOTIFY_MNT_EVENTS (FS_MNT_ATTACH | FS_MNT_DETACH) | ||
| 89 | |||
| 83 | /* Content events can be used to inspect file content */ | 90 | /* Content events can be used to inspect file content */ |
| 84 | #define FSNOTIFY_CONTENT_PERM_EVENTS (FS_OPEN_PERM | FS_OPEN_EXEC_PERM | \ | 91 | #define FSNOTIFY_CONTENT_PERM_EVENTS (FS_OPEN_PERM | FS_OPEN_EXEC_PERM | \ |
| 85 | FS_ACCESS_PERM) | 92 | FS_ACCESS_PERM) |
| @@ -108,6 +115,7 @@ · 22 unmodified lines | |||
| 108 | 115 | ||
| 109 | /* Events that can be reported to backends */ | 116 | /* Events that can be reported to backends */ |
| 110 | #define ALL_FSNOTIFY_EVENTS (ALL_FSNOTIFY_DIRENT_EVENTS | \ | 117 | #define ALL_FSNOTIFY_EVENTS (ALL_FSNOTIFY_DIRENT_EVENTS | \ |
| 118 | FSNOTIFY_MNT_EVENTS | \ | ||
| 111 | FS_EVENTS_POSS_ON_CHILD | \ | 119 | FS_EVENTS_POSS_ON_CHILD | \ |
| 112 | FS_DELETE_SELF | FS_MOVE_SELF | \ | 120 | FS_DELETE_SELF | FS_MOVE_SELF | \ |
| 113 | FS_UNMOUNT | FS_Q_OVERFLOW | FS_IN_IGNORED | \ | 121 | FS_UNMOUNT | FS_Q_OVERFLOW | FS_IN_IGNORED | \ |
| @@ -298,6 +306,7 @@ enum fsnotify_data_type { · 184 unmodified lines | |||
| 298 | FSNOTIFY_EVENT_PATH, | 306 | FSNOTIFY_EVENT_PATH, |
| 299 | FSNOTIFY_EVENT_INODE, | 307 | FSNOTIFY_EVENT_INODE, |
| 300 | FSNOTIFY_EVENT_DENTRY, | 308 | FSNOTIFY_EVENT_DENTRY, |
| 309 | FSNOTIFY_EVENT_MNT, | ||
| 301 | FSNOTIFY_EVENT_ERROR, | 310 | FSNOTIFY_EVENT_ERROR, |
| 302 | }; | 311 | }; |
| 303 | 312 | ||
| @@ -318,6 +327,11 @@ static inline const struct path *file_range_path(const struct file_range *range) · 14 unmodified lines | |||
| 318 | return range->path; | 327 | return range->path; |
| 319 | } | 328 | } |
| 320 | 329 | ||
| 330 | struct fsnotify_mnt { | ||
| 331 | const struct mnt_namespace *ns; | ||
| 332 | u64 mnt_id; | ||
| 333 | }; | ||
| 334 | |||
| 321 | static inline struct inode *fsnotify_data_inode(const void *data, int data_type) | 335 | static inline struct inode *fsnotify_data_inode(const void *data, int data_type) |
| 322 | { | 336 | { |
| 323 | switch (data_type) { | 337 | switch (data_type) { |
| @@ -383,6 +397,24 @@ static inline struct super_block *fsnotify_data_sb(const void *data, · 59 unmodified lines | |||
| 383 | } | 397 | } |
| 384 | } | 398 | } |
| 385 | 399 | ||
| 400 | static inline const struct fsnotify_mnt *fsnotify_data_mnt(const void *data, | ||
| 401 | int data_type) | ||
| 402 | { | ||
| 403 | switch (data_type) { | ||
| 404 | case FSNOTIFY_EVENT_MNT: | ||
| 405 | return data; | ||
| 406 | default: | ||
| 407 | return NULL; | ||
| 408 | } | ||
| 409 | } | ||
| 410 | |||
| 411 | static inline u64 fsnotify_data_mnt_id(const void *data, int data_type) | ||
| 412 | { | ||
| 413 | const struct fsnotify_mnt *mnt_data = fsnotify_data_mnt(data, data_type); | ||
| 414 | |||
| 415 | return mnt_data ? mnt_data->mnt_id : 0; | ||
| 416 | } | ||
| 417 | |||
| 386 | static inline struct fs_error_report *fsnotify_data_error_report( | 418 | static inline struct fs_error_report *fsnotify_data_error_report( |
| 387 | const void *data, | 419 | const void *data, |
| 388 | int data_type) | 420 | int data_type) |
| @@ -420,6 +452,7 @@ enum fsnotify_iter_type { · 31 unmodified lines | |||
| 420 | FSNOTIFY_ITER_TYPE_SB, | 452 | FSNOTIFY_ITER_TYPE_SB, |
| 421 | FSNOTIFY_ITER_TYPE_PARENT, | 453 | FSNOTIFY_ITER_TYPE_PARENT, |
| 422 | FSNOTIFY_ITER_TYPE_INODE2, | 454 | FSNOTIFY_ITER_TYPE_INODE2, |
| 455 | FSNOTIFY_ITER_TYPE_MNTNS, | ||
| 423 | FSNOTIFY_ITER_TYPE_COUNT | 456 | FSNOTIFY_ITER_TYPE_COUNT |
| 424 | }; | 457 | }; |
| 425 | 458 | ||
| @@ -429,6 +462,7 @@ enum fsnotify_obj_type { · 3 unmodified lines | |||
| 429 | FSNOTIFY_OBJ_TYPE_INODE, | 462 | FSNOTIFY_OBJ_TYPE_INODE, |
| 430 | FSNOTIFY_OBJ_TYPE_VFSMOUNT, | 463 | FSNOTIFY_OBJ_TYPE_VFSMOUNT, |
| 431 | FSNOTIFY_OBJ_TYPE_SB, | 464 | FSNOTIFY_OBJ_TYPE_SB, |
| 465 | FSNOTIFY_OBJ_TYPE_MNTNS, | ||
| 432 | FSNOTIFY_OBJ_TYPE_COUNT, | 466 | FSNOTIFY_OBJ_TYPE_COUNT, |
| 433 | FSNOTIFY_OBJ_TYPE_DETACHED = FSNOTIFY_OBJ_TYPE_COUNT | 467 | FSNOTIFY_OBJ_TYPE_DETACHED = FSNOTIFY_OBJ_TYPE_COUNT |
| 434 | }; | 468 | }; |
| @@ -613,8 +647,10 @@ extern int __fsnotify_parent(struct dentry *dentry, __u32 mask, const void *data · 178 unmodified lines | |||
| 613 | extern void __fsnotify_inode_delete(struct inode *inode); | 647 | extern void __fsnotify_inode_delete(struct inode *inode); |
| 614 | extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt); | 648 | extern void __fsnotify_vfsmount_delete(struct vfsmount *mnt); |
| 615 | extern void fsnotify_sb_delete(struct super_block *sb); | 649 | extern void fsnotify_sb_delete(struct super_block *sb); |
| 650 | extern void __fsnotify_mntns_delete(struct mnt_namespace *mntns); | ||
| 616 | extern void fsnotify_sb_free(struct super_block *sb); | 651 | extern void fsnotify_sb_free(struct super_block *sb); |
| 617 | extern u32 fsnotify_get_cookie(void); | 652 | extern u32 fsnotify_get_cookie(void); |
| 653 | extern void fsnotify_mnt(__u32 mask, struct mnt_namespace *ns, struct vfsmount *mnt); | ||
| 618 | 654 | ||
| 619 | static inline __u32 fsnotify_parent_needed_mask(__u32 mask) | 655 | static inline __u32 fsnotify_parent_needed_mask(__u32 mask) |
| 620 | { | 656 | { |
| @@ -928,6 +964,9 @@ static inline void __fsnotify_vfsmount_delete(struct vfsmount *mnt) · 307 unmodified lines | |||
| 928 | static inline void fsnotify_sb_delete(struct super_block *sb) | 964 | static inline void fsnotify_sb_delete(struct super_block *sb) |
| 929 | {} | 965 | {} |
| 930 | 966 | ||
| 967 | static inline void __fsnotify_mntns_delete(struct mnt_namespace *mntns) | ||
| 968 | {} | ||
| 969 | |||
| 931 | static inline void fsnotify_sb_free(struct super_block *sb) | 970 | static inline void fsnotify_sb_free(struct super_block *sb) |
| 932 | {} | 971 | {} |
| 933 | 972 | ||
| @@ -942,6 +981,9 @@ static inline u32 fsnotify_get_cookie(void) · 8 unmodified lines | |||
| 942 | static inline void fsnotify_unmount_inodes(struct super_block *sb) | 981 | static inline void fsnotify_unmount_inodes(struct super_block *sb) |
| 943 | {} | 982 | {} |
| 944 | 983 | ||
| 984 | static inline void fsnotify_mnt(__u32 mask, struct mnt_namespace *ns, struct vfsmount *mnt) | ||
| 985 | {} | ||
| 986 | |||
| 945 | #endif /* CONFIG_FSNOTIFY */ | 987 | #endif /* CONFIG_FSNOTIFY */ |
| 946 | 988 | ||
| 947 | #endif /* __KERNEL __ */ | 989 | #endif /* __KERNEL __ */ |
| @@ -25,6 +25,11 @@ static_assert(sizeof(vfsgid_t) == sizeof(kgid_t)); · 24 unmodified lines | |||
| 25 | static_assert(offsetof(vfsuid_t, val) == offsetof(kuid_t, val)); | 25 | static_assert(offsetof(vfsuid_t, val) == offsetof(kuid_t, val)); |
| 26 | static_assert(offsetof(vfsgid_t, val) == offsetof(kgid_t, val)); | 26 | static_assert(offsetof(vfsgid_t, val) == offsetof(kgid_t, val)); |
| 27 | 27 | ||
| 28 | static inline bool is_valid_mnt_idmap(const struct mnt_idmap *idmap) | ||
| 29 | { | ||
| 30 | return idmap != &nop_mnt_idmap && idmap != &invalid_mnt_idmap; | ||
| 31 | } | ||
| 32 | |||
| 28 | #ifdef CONFIG_MULTIUSER | 33 | #ifdef CONFIG_MULTIUSER |
| 29 | static inline uid_t __vfsuid_val(vfsuid_t uid) | 34 | static inline uid_t __vfsuid_val(vfsuid_t uid) |
| 30 | { | 35 | { |
| @@ -951,6 +951,10 @@ asmlinkage long sys_statx(int dfd, const char __user *path, unsigned flags, · 950 unmodified lines | |||
| 951 | asmlinkage long sys_rseq(struct rseq __user *rseq, uint32_t rseq_len, | 951 | asmlinkage long sys_rseq(struct rseq __user *rseq, uint32_t rseq_len, |
| 952 | int flags, uint32_t sig); | 952 | int flags, uint32_t sig); |
| 953 | asmlinkage long sys_open_tree(int dfd, const char __user *path, unsigned flags); | 953 | asmlinkage long sys_open_tree(int dfd, const char __user *path, unsigned flags); |
| 954 | asmlinkage long sys_open_tree_attr(int dfd, const char __user *path, | ||
| 955 | unsigned flags, | ||
| 956 | struct mount_attr __user *uattr, | ||
| 957 | size_t usize); | ||
| 954 | asmlinkage long sys_move_mount(int from_dfd, const char __user *from_path, | 958 | asmlinkage long sys_move_mount(int from_dfd, const char __user *from_path, |
| 955 | int to_dfd, const char __user *to_path, | 959 | int to_dfd, const char __user *to_path, |
| 956 | unsigned int ms_flags); | 960 | unsigned int ms_flags); |
| @@ -132,6 +132,7 @@ static inline bool kgid_has_mapping(struct user_namespace *ns, kgid_t gid) · 131 unmodified lines | |||
| 132 | 132 | ||
| 133 | u32 map_id_down(struct uid_gid_map *map, u32 id); | 133 | u32 map_id_down(struct uid_gid_map *map, u32 id); |
| 134 | u32 map_id_up(struct uid_gid_map *map, u32 id); | 134 | u32 map_id_up(struct uid_gid_map *map, u32 id); |
| 135 | u32 map_id_range_up(struct uid_gid_map *map, u32 id, u32 count); | ||
| 135 | 136 | ||
| 136 | #else | 137 | #else |
| 137 | 138 | ||
| @@ -186,6 +187,11 @@ static inline u32 map_id_down(struct uid_gid_map *map, u32 id) · 48 unmodified lines | |||
| 186 | return id; | 187 | return id; |
| 187 | } | 188 | } |
| 188 | 189 | ||
| 190 | static inline u32 map_id_range_up(struct uid_gid_map *map, u32 id, u32 count) | ||
| 191 | { | ||
| 192 | return id; | ||
| 193 | } | ||
| 194 | |||
| 189 | static inline u32 map_id_up(struct uid_gid_map *map, u32 id) | 195 | static inline u32 map_id_up(struct uid_gid_map *map, u32 id) |
| 190 | { | 196 | { |
| 191 | return id; | 197 | return id; |
| @@ -849,9 +849,11 @@ __SYSCALL(__NR_getxattrat, sys_getxattrat) · 848 unmodified lines | |||
| 849 | __SYSCALL(__NR_listxattrat, sys_listxattrat) | 849 | __SYSCALL(__NR_listxattrat, sys_listxattrat) |
| 850 | #define __NR_removexattrat 466 | 850 | #define __NR_removexattrat 466 |
| 851 | __SYSCALL(__NR_removexattrat, sys_removexattrat) | 851 | __SYSCALL(__NR_removexattrat, sys_removexattrat) |
| 852 | #define __NR_open_tree_attr 467 | ||
| 853 | __SYSCALL(__NR_open_tree_attr, sys_open_tree_attr) | ||
| 852 | 854 | ||
| 853 | #undef __NR_syscalls | 855 | #undef __NR_syscalls |
| 854 | #define __NR_syscalls 467 | 856 | #define __NR_syscalls 468 |
| 855 | 857 | ||
| 856 | /* | 858 | /* |
| 857 | * 32 bit systems traditionally used different | 859 | * 32 bit systems traditionally used different |
| @@ -28,6 +28,8 @@ · 27 unmodified lines | |||
| 28 | /* #define FAN_DIR_MODIFY 0x00080000 */ /* Deprecated (reserved) */ | 28 | /* #define FAN_DIR_MODIFY 0x00080000 */ /* Deprecated (reserved) */ |
| 29 | 29 | ||
| 30 | #define FAN_PRE_ACCESS 0x00100000 /* Pre-content access hook */ | 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 | #define FAN_EVENT_ON_CHILD 0x08000000 /* Interested in child events */ | 34 | #define FAN_EVENT_ON_CHILD 0x08000000 /* Interested in child events */ |
| 33 | 35 | ||
| @@ -64,6 +66,7 @@ · 30 unmodified lines | |||
| 64 | #define FAN_REPORT_NAME 0x00000800 /* Report events with name */ | 66 | #define FAN_REPORT_NAME 0x00000800 /* Report events with name */ |
| 65 | #define FAN_REPORT_TARGET_FID 0x00001000 /* Report dirent target id */ | 67 | #define FAN_REPORT_TARGET_FID 0x00001000 /* Report dirent target id */ |
| 66 | #define FAN_REPORT_FD_ERROR 0x00002000 /* event->fd can report error */ | 68 | #define FAN_REPORT_FD_ERROR 0x00002000 /* event->fd can report error */ |
| 69 | #define FAN_REPORT_MNT 0x00004000 /* Report mount events */ | ||
| 67 | 70 | ||
| 68 | /* Convenience macro - FAN_REPORT_NAME requires FAN_REPORT_DIR_FID */ | 71 | /* Convenience macro - FAN_REPORT_NAME requires FAN_REPORT_DIR_FID */ |
| 69 | #define FAN_REPORT_DFID_NAME (FAN_REPORT_DIR_FID | FAN_REPORT_NAME) | 72 | #define FAN_REPORT_DFID_NAME (FAN_REPORT_DIR_FID | FAN_REPORT_NAME) |
| @@ -94,6 +97,7 @@ · 24 unmodified lines | |||
| 94 | #define FAN_MARK_INODE 0x00000000 | 97 | #define FAN_MARK_INODE 0x00000000 |
| 95 | #define FAN_MARK_MOUNT 0x00000010 | 98 | #define FAN_MARK_MOUNT 0x00000010 |
| 96 | #define FAN_MARK_FILESYSTEM 0x00000100 | 99 | #define FAN_MARK_FILESYSTEM 0x00000100 |
| 100 | #define FAN_MARK_MNTNS 0x00000110 | ||
| 97 | 101 | ||
| 98 | /* | 102 | /* |
| 99 | * Convenience macro - FAN_MARK_IGNORE requires FAN_MARK_IGNORED_SURV_MODIFY | 103 | * Convenience macro - FAN_MARK_IGNORE requires FAN_MARK_IGNORED_SURV_MODIFY |
| @@ -147,6 +151,7 @@ struct fanotify_event_metadata { · 47 unmodified lines | |||
| 147 | #define FAN_EVENT_INFO_TYPE_PIDFD 4 | 151 | #define FAN_EVENT_INFO_TYPE_PIDFD 4 |
| 148 | #define FAN_EVENT_INFO_TYPE_ERROR 5 | 152 | #define FAN_EVENT_INFO_TYPE_ERROR 5 |
| 149 | #define FAN_EVENT_INFO_TYPE_RANGE 6 | 153 | #define FAN_EVENT_INFO_TYPE_RANGE 6 |
| 154 | #define FAN_EVENT_INFO_TYPE_MNT 7 | ||
| 150 | 155 | ||
| 151 | /* Special info types for FAN_RENAME */ | 156 | /* Special info types for FAN_RENAME */ |
| 152 | #define FAN_EVENT_INFO_TYPE_OLD_DFID_NAME 10 | 157 | #define FAN_EVENT_INFO_TYPE_OLD_DFID_NAME 10 |
| @@ -200,6 +205,11 @@ struct fanotify_event_info_range { · 47 unmodified lines | |||
| 200 | __u64 count; | 205 | __u64 count; |
| 201 | }; | 206 | }; |
| 202 | 207 | ||
| 208 | struct fanotify_event_info_mnt { | ||
| 209 | struct fanotify_event_info_header hdr; | ||
| 210 | __u64 mnt_id; | ||
| 211 | }; | ||
| 212 | |||
| 203 | /* | 213 | /* |
| 204 | * User space may need to record additional information about its decision. | 214 | * User space may need to record additional information about its decision. |
| 205 | * The extra information type records what kind of information is included. | 215 | * The extra information type records what kind of information is included. |
| @@ -179,7 +179,12 @@ struct statmount { · 178 unmodified lines | |||
| 179 | __u32 opt_array; /* [str] Array of nul terminated fs options */ | 179 | __u32 opt_array; /* [str] Array of nul terminated fs options */ |
| 180 | __u32 opt_sec_num; /* Number of security options */ | 180 | __u32 opt_sec_num; /* Number of security options */ |
| 181 | __u32 opt_sec_array; /* [str] Array of nul terminated security options */ | 181 | __u32 opt_sec_array; /* [str] Array of nul terminated security options */ |
| 182 | __u64 __spare2[46]; | 182 | __u64 supported_mask; /* Mask flags that this kernel supports */ |
| 183 | __u32 mnt_uidmap_num; /* Number of uid mappings */ | ||
| 184 | __u32 mnt_uidmap; /* [str] Array of uid mappings (as seen from callers namespace) */ | ||
| 185 | __u32 mnt_gidmap_num; /* Number of gid mappings */ | ||
| 186 | __u32 mnt_gidmap; /* [str] Array of gid mappings (as seen from callers namespace) */ | ||
| 187 | __u64 __spare2[43]; | ||
| 183 | char str[]; /* Variable size part containing strings */ | 188 | char str[]; /* Variable size part containing strings */ |
| 184 | }; | 189 | }; |
| 185 | 190 | ||
| @@ -217,6 +222,9 @@ struct mnt_id_req { · 31 unmodified lines | |||
| 217 | #define STATMOUNT_SB_SOURCE 0x00000200U /* Want/got sb_source */ | 222 | #define STATMOUNT_SB_SOURCE 0x00000200U /* Want/got sb_source */ |
| 218 | #define STATMOUNT_OPT_ARRAY 0x00000400U /* Want/got opt_... */ | 223 | #define STATMOUNT_OPT_ARRAY 0x00000400U /* Want/got opt_... */ |
| 219 | #define STATMOUNT_OPT_SEC_ARRAY 0x00000800U /* Want/got opt_sec... */ | 224 | #define STATMOUNT_OPT_SEC_ARRAY 0x00000800U /* Want/got opt_sec... */ |
| 225 | #define STATMOUNT_SUPPORTED_MASK 0x00001000U /* Want/got supported mask flags */ | ||
| 226 | #define STATMOUNT_MNT_UIDMAP 0x00002000U /* Want/got uidmap... */ | ||
| 227 | #define STATMOUNT_MNT_GIDMAP 0x00004000U /* Want/got gidmap... */ | ||
| 220 | 228 | ||
| 221 | /* | 229 | /* |
| 222 | * Special @mnt_id values that can be passed to listmount | 230 | * Special @mnt_id values that can be passed to listmount |
| @@ -238,7 +238,7 @@ EXPORT_SYMBOL(__put_user_ns); · 237 unmodified lines | |||
| 238 | struct idmap_key { | 238 | struct idmap_key { |
| 239 | bool map_up; /* true -> id from kid; false -> kid from id */ | 239 | bool map_up; /* true -> id from kid; false -> kid from id */ |
| 240 | u32 id; /* id to find */ | 240 | u32 id; /* id to find */ |
| 241 | u32 count; /* == 0 unless used with map_id_range_down() */ | 241 | u32 count; |
| 242 | }; | 242 | }; |
| 243 | 243 | ||
| 244 | /* | 244 | /* |
| @@ -343,16 +343,19 @@ u32 map_id_down(struct uid_gid_map *map, u32 id) · 98 unmodified lines | |||
| 343 | * UID_GID_MAP_MAX_BASE_EXTENTS. | 343 | * UID_GID_MAP_MAX_BASE_EXTENTS. |
| 344 | */ | 344 | */ |
| 345 | static struct uid_gid_extent * | 345 | static struct uid_gid_extent * |
| 346 | map_id_up_base(unsigned extents, struct uid_gid_map *map, u32 id) | 346 | map_id_range_up_base(unsigned extents, struct uid_gid_map *map, u32 id, u32 count) |
| 347 | { | 347 | { |
| 348 | unsigned idx; | 348 | unsigned idx; |
| 349 | u32 first, last; | 349 | u32 first, last, id2; |
| 350 | |||
| 351 | id2 = id + count - 1; | ||
| 350 | 352 | ||
| 351 | /* Find the matching extent */ | 353 | /* Find the matching extent */ |
| 352 | for (idx = 0; idx < extents; idx++) { | 354 | for (idx = 0; idx < extents; idx++) { |
| 353 | first = map->extent[idx].lower_first; | 355 | first = map->extent[idx].lower_first; |
| 354 | last = first + map->extent[idx].count - 1; | 356 | last = first + map->extent[idx].count - 1; |
| 355 | if (id >= first && id <= last) | 357 | if (id >= first && id <= last && |
| 358 | (id2 >= first && id2 <= last)) | ||
| 356 | return &map->extent[idx]; | 359 | return &map->extent[idx]; |
| 357 | } | 360 | } |
| 358 | return NULL; | 361 | return NULL; |
| @@ -363,28 +366,28 @@ map_id_up_base(unsigned extents, struct uid_gid_map *map, u32 id) · 4 unmodified lines | |||
| 363 | * Can only be called if number of mappings exceeds UID_GID_MAP_MAX_BASE_EXTENTS. | 366 | * Can only be called if number of mappings exceeds UID_GID_MAP_MAX_BASE_EXTENTS. |
| 364 | */ | 367 | */ |
| 365 | static struct uid_gid_extent * | 368 | static struct uid_gid_extent * |
| 366 | map_id_up_max(unsigned extents, struct uid_gid_map *map, u32 id) | 369 | map_id_range_up_max(unsigned extents, struct uid_gid_map *map, u32 id, u32 count) |
| 367 | { | 370 | { |
| 368 | struct idmap_key key; | 371 | struct idmap_key key; |
| 369 | 372 | ||
| 370 | key.map_up = true; | 373 | key.map_up = true; |
| 371 | key.count = 1; | 374 | key.count = count; |
| 372 | key.id = id; | 375 | key.id = id; |
| 373 | 376 | ||
| 374 | return bsearch(&key, map->reverse, extents, | 377 | return bsearch(&key, map->reverse, extents, |
| 375 | sizeof(struct uid_gid_extent), cmp_map_id); | 378 | sizeof(struct uid_gid_extent), cmp_map_id); |
| 376 | } | 379 | } |
| 377 | 380 | ||
| 378 | u32 map_id_up(struct uid_gid_map *map, u32 id) | 381 | u32 map_id_range_up(struct uid_gid_map *map, u32 id, u32 count) |
| 379 | { | 382 | { |
| 380 | struct uid_gid_extent *extent; | 383 | struct uid_gid_extent *extent; |
| 381 | unsigned extents = map->nr_extents; | 384 | unsigned extents = map->nr_extents; |
| 382 | smp_rmb(); | 385 | smp_rmb(); |
| 383 | 386 | ||
| 384 | if (extents <= UID_GID_MAP_MAX_BASE_EXTENTS) | 387 | if (extents <= UID_GID_MAP_MAX_BASE_EXTENTS) |
| 385 | extent = map_id_up_base(extents, map, id); | 388 | extent = map_id_range_up_base(extents, map, id, count); |
| 386 | else | 389 | else |
| 387 | extent = map_id_up_max(extents, map, id); | 390 | extent = map_id_range_up_max(extents, map, id, count); |
| 388 | 391 | ||
| 389 | /* Map the id or note failure */ | 392 | /* Map the id or note failure */ |
| 390 | if (extent) | 393 | if (extent) |
| @@ -395,6 +398,11 @@ u32 map_id_up(struct uid_gid_map *map, u32 id) · 4 unmodified lines | |||
| 395 | return id; | 398 | return id; |
| 396 | } | 399 | } |
| 397 | 400 | ||
| 401 | u32 map_id_up(struct uid_gid_map *map, u32 id) | ||
| 402 | { | ||
| 403 | return map_id_range_up(map, id, 1); | ||
| 404 | } | ||
| 405 | |||
| 398 | /** | 406 | /** |
| 399 | * make_kuid - Map a user-namespace uid pair into a kuid. | 407 | * make_kuid - Map a user-namespace uid pair into a kuid. |
| 400 | * @ns: User namespace that the uid is in | 408 | * @ns: User namespace that the uid is in |
| @@ -42,7 +42,11 @@ struct statmount { · 41 unmodified lines | |||
| 42 | __u32 opt_array; /* [str] Array of nul terminated fs options */ | 42 | __u32 opt_array; /* [str] Array of nul terminated fs options */ |
| 43 | __u32 opt_sec_num; /* Number of security options */ | 43 | __u32 opt_sec_num; /* Number of security options */ |
| 44 | __u32 opt_sec_array; /* [str] Array of nul terminated security options */ | 44 | __u32 opt_sec_array; /* [str] Array of nul terminated security options */ |
| 45 | __u64 __spare2[46]; | 45 | __u32 mnt_uidmap_num; /* Number of uid mappings */ |
| 46 | __u32 mnt_uidmap; /* [str] Array of uid mappings */ | ||
| 47 | __u32 mnt_gidmap_num; /* Number of gid mappings */ | ||
| 48 | __u32 mnt_gidmap; /* [str] Array of gid mappings */ | ||
| 49 | __u64 __spare2[44]; | ||
| 46 | char str[]; /* Variable size part containing strings */ | 50 | char str[]; /* Variable size part containing strings */ |
| 47 | }; | 51 | }; |
| 48 | 52 | ||
| @@ -158,6 +162,14 @@ struct mnt_ns_info { · 109 unmodified lines | |||
| 158 | #define STATX_MNT_ID_UNIQUE 0x00004000U /* Want/got extended stx_mount_id */ | 162 | #define STATX_MNT_ID_UNIQUE 0x00004000U /* Want/got extended stx_mount_id */ |
| 159 | #endif | 163 | #endif |
| 160 | 164 | ||
| 165 | #ifndef STATMOUNT_MNT_UIDMAP | ||
| 166 | #define STATMOUNT_MNT_UIDMAP 0x00002000U /* Want/got uidmap... */ | ||
| 167 | #endif | ||
| 168 | |||
| 169 | #ifndef STATMOUNT_MNT_GIDMAP | ||
| 170 | #define STATMOUNT_MNT_GIDMAP 0x00004000U /* Want/got gidmap... */ | ||
| 171 | #endif | ||
| 172 | |||
| 161 | #ifndef MOUNT_ATTR_RDONLY | 173 | #ifndef MOUNT_ATTR_RDONLY |
| 162 | #define MOUNT_ATTR_RDONLY 0x00000001 /* Mount read-only */ | 174 | #define MOUNT_ATTR_RDONLY 0x00000001 /* Mount read-only */ |
| 163 | #endif | 175 | #endif |
| @@ -128,20 +128,43 @@ next: · 127 unmodified lines | |||
| 128 | STATMOUNT_MNT_POINT | | 128 | STATMOUNT_MNT_POINT | |
| 129 | STATMOUNT_MNT_NS_ID | | 129 | STATMOUNT_MNT_NS_ID | |
| 130 | STATMOUNT_MNT_OPTS | | 130 | STATMOUNT_MNT_OPTS | |
| 131 | STATMOUNT_FS_TYPE, 0); | 131 | STATMOUNT_FS_TYPE | |
| 132 | STATMOUNT_MNT_UIDMAP | | ||
| 133 | STATMOUNT_MNT_GIDMAP, 0); | ||
| 132 | if (!stmnt) { | 134 | if (!stmnt) { |
| 133 | printf("Failed to statmount(%" PRIu64 ") in mount namespace(%" PRIu64 ")\n", | 135 | printf("Failed to statmount(%" PRIu64 ") in mount namespace(%" PRIu64 ")\n", |
| 134 | (uint64_t)last_mnt_id, (uint64_t)info.mnt_ns_id); | 136 | (uint64_t)last_mnt_id, (uint64_t)info.mnt_ns_id); |
| 135 | continue; | 137 | continue; |
| 136 | } | 138 | } |
| 137 | 139 | ||
| 138 | printf("mnt_id:\t\t%" PRIu64 "\nmnt_parent_id:\t%" PRIu64 "\nfs_type:\t%s\nmnt_root:\t%s\nmnt_point:\t%s\nmnt_opts:\t%s\n\n", | 140 | printf("mnt_id:\t\t%" PRIu64 "\nmnt_parent_id:\t%" PRIu64 "\nfs_type:\t%s\nmnt_root:\t%s\nmnt_point:\t%s\nmnt_opts:\t%s\n", |
| 139 | (uint64_t)stmnt->mnt_id, | 141 | (uint64_t)stmnt->mnt_id, |
| 140 | (uint64_t)stmnt->mnt_parent_id, | 142 | (uint64_t)stmnt->mnt_parent_id, |
| 141 | stmnt->str + stmnt->fs_type, | 143 | (stmnt->mask & STATMOUNT_FS_TYPE) ? stmnt->str + stmnt->fs_type : "", |
| 142 | stmnt->str + stmnt->mnt_root, | 144 | (stmnt->mask & STATMOUNT_MNT_ROOT) ? stmnt->str + stmnt->mnt_root : "", |
| 143 | stmnt->str + stmnt->mnt_point, | 145 | (stmnt->mask & STATMOUNT_MNT_POINT) ? stmnt->str + stmnt->mnt_point : "", |
| 144 | stmnt->str + stmnt->mnt_opts); | 146 | (stmnt->mask & STATMOUNT_MNT_OPTS) ? stmnt->str + stmnt->mnt_opts : ""); |
| 147 | |||
| 148 | if (stmnt->mask & STATMOUNT_MNT_UIDMAP) { | ||
| 149 | const char *idmap = stmnt->str + stmnt->mnt_uidmap; | ||
| 150 | |||
| 151 | for (size_t idx = 0; idx < stmnt->mnt_uidmap_num; idx++) { | ||
| 152 | printf("mnt_uidmap[%zu]:\t%s\n", idx, idmap); | ||
| 153 | idmap += strlen(idmap) + 1; | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | if (stmnt->mask & STATMOUNT_MNT_GIDMAP) { | ||
| 158 | const char *idmap = stmnt->str + stmnt->mnt_gidmap; | ||
| 159 | |||
| 160 | for (size_t idx = 0; idx < stmnt->mnt_gidmap_num; idx++) { | ||
| 161 | printf("mnt_gidmap[%zu]:\t%s\n", idx, idmap); | ||
| 162 | idmap += strlen(idmap) + 1; | ||
| 163 | } | ||
| 164 | } | ||
| 165 | |||
| 166 | printf("\n"); | ||
| 167 | |||
| 145 | free(stmnt); | 168 | free(stmnt); |
| 146 | } | 169 | } |
| 147 | } | 170 | } |
| @@ -407,3 +407,4 @@ · 406 unmodified lines | |||
| 407 | 464 common getxattrat sys_getxattrat | 407 | 464 common getxattrat sys_getxattrat |
| 408 | 465 common listxattrat sys_listxattrat | 408 | 465 common listxattrat sys_listxattrat |
| 409 | 466 common removexattrat sys_removexattrat | 409 | 466 common removexattrat sys_removexattrat |
| 410 | 467 common open_tree_attr sys_open_tree_attr | ||
| @@ -3395,6 +3395,9 @@ static int selinux_path_notify(const struct path *path, u64 mask, · 3394 unmodified lines | |||
| 3395 | case FSNOTIFY_OBJ_TYPE_INODE: | 3395 | case FSNOTIFY_OBJ_TYPE_INODE: |
| 3396 | perm = FILE__WATCH; | 3396 | perm = FILE__WATCH; |
| 3397 | break; | 3397 | break; |
| 3398 | case FSNOTIFY_OBJ_TYPE_MNTNS: | ||
| 3399 | perm = FILE__WATCH_MOUNTNS; | ||
| 3400 | break; | ||
| 3398 | default: | 3401 | default: |
| 3399 | return -EINVAL; | 3402 | return -EINVAL; |
| 3400 | } | 3403 | } |
| @@ -8,7 +8,7 @@ · 7 unmodified lines | |||
| 8 | COMMON_FILE_SOCK_PERMS, "unlink", "link", "rename", "execute", \ | 8 | COMMON_FILE_SOCK_PERMS, "unlink", "link", "rename", "execute", \ |
| 9 | "quotaon", "mounton", "audit_access", "open", "execmod", \ | 9 | "quotaon", "mounton", "audit_access", "open", "execmod", \ |
| 10 | "watch", "watch_mount", "watch_sb", "watch_with_perm", \ | 10 | "watch", "watch_mount", "watch_sb", "watch_with_perm", \ |
| 11 | "watch_reads" | 11 | "watch_reads", "watch_mountns" |
| 12 | 12 | ||
| 13 | #define COMMON_SOCK_PERMS \ | 13 | #define COMMON_SOCK_PERMS \ |
| 14 | COMMON_FILE_SOCK_PERMS, "bind", "connect", "listen", "accept", \ | 14 | COMMON_FILE_SOCK_PERMS, "bind", "connect", "listen", "accept", \ |
| @@ -35,6 +35,7 @@ TARGETS += filesystems/epoll · 34 unmodified lines | |||
| 35 | TARGETS += filesystems/fat | 35 | TARGETS += filesystems/fat |
| 36 | TARGETS += filesystems/overlayfs | 36 | TARGETS += filesystems/overlayfs |
| 37 | TARGETS += filesystems/statmount | 37 | TARGETS += filesystems/statmount |
| 38 | TARGETS += filesystems/mount-notify | ||
| 38 | TARGETS += firmware | 39 | TARGETS += firmware |
| 39 | TARGETS += fpu | 40 | TARGETS += fpu |
| 40 | TARGETS += ftrace | 41 | TARGETS += ftrace |
| @@ -0,0 +1,2 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only | ||
| 2 | /*_test | ||
| @@ -0,0 +1,6 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-or-later | ||
| 2 | |||
| 3 | CFLAGS += -Wall -O2 -g $(KHDR_INCLUDES) | ||
| 4 | TEST_GEN_PROGS := mount-notify_test | ||
| 5 | |||
| 6 | include ../../lib.mk | ||
| @@ -0,0 +1,516 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0-or-later | ||
| 2 | // Copyright (c) 2025 Miklos Szeredi <miklos@szeredi.hu> | ||
| 3 | |||
| 4 | #define _GNU_SOURCE | ||
| 5 | #include <fcntl.h> | ||
| 6 | #include <sched.h> | ||
| 7 | #include <stdio.h> | ||
| 8 | #include <string.h> | ||
| 9 | #include <sys/stat.h> | ||
| 10 | #include <sys/mount.h> | ||
| 11 | #include <linux/fanotify.h> | ||
| 12 | #include <unistd.h> | ||
| 13 | #include <sys/fanotify.h> | ||
| 14 | #include <sys/syscall.h> | ||
| 15 | |||
| 16 | #include "../../kselftest_harness.h" | ||
| 17 | #include "../statmount/statmount.h" | ||
| 18 | |||
| 19 | #ifndef FAN_MNT_ATTACH | ||
| 20 | struct fanotify_event_info_mnt { | ||
| 21 | struct fanotify_event_info_header hdr; | ||
| 22 | __u64 mnt_id; | ||
| 23 | }; | ||
| 24 | #define FAN_MNT_ATTACH 0x01000000 /* Mount was attached */ | ||
| 25 | #endif | ||
| 26 | |||
| 27 | #ifndef FAN_MNT_DETACH | ||
| 28 | #define FAN_MNT_DETACH 0x02000000 /* Mount was detached */ | ||
| 29 | #endif | ||
| 30 | |||
| 31 | #ifndef FAN_REPORT_MNT | ||
| 32 | #define FAN_REPORT_MNT 0x00004000 /* Report mount events */ | ||
| 33 | #endif | ||
| 34 | |||
| 35 | #ifndef FAN_MARK_MNTNS | ||
| 36 | #define FAN_MARK_MNTNS 0x00000110 | ||
| 37 | #endif | ||
| 38 | |||
| 39 | static uint64_t get_mnt_id(struct __test_metadata *const _metadata, | ||
| 40 | const char *path) | ||
| 41 | { | ||
| 42 | struct statx sx; | ||
| 43 | |||
| 44 | ASSERT_EQ(statx(AT_FDCWD, path, 0, STATX_MNT_ID_UNIQUE, &sx), 0); | ||
| 45 | ASSERT_TRUE(!!(sx.stx_mask & STATX_MNT_ID_UNIQUE)); | ||
| 46 | return sx.stx_mnt_id; | ||
| 47 | } | ||
| 48 | |||
| 49 | static const char root_mntpoint_templ[] = "/tmp/mount-notify_test_root.XXXXXX"; | ||
| 50 | |||
| 51 | FIXTURE(fanotify) { | ||
| 52 | int fan_fd; | ||
| 53 | char buf[256]; | ||
| 54 | unsigned int rem; | ||
| 55 | void *next; | ||
| 56 | char root_mntpoint[sizeof(root_mntpoint_templ)]; | ||
| 57 | int orig_root; | ||
| 58 | int ns_fd; | ||
| 59 | uint64_t root_id; | ||
| 60 | }; | ||
| 61 | |||
| 62 | FIXTURE_SETUP(fanotify) | ||
| 63 | { | ||
| 64 | int ret; | ||
| 65 | |||
| 66 | ASSERT_EQ(unshare(CLONE_NEWNS), 0); | ||
| 67 | |||
| 68 | self->ns_fd = open("/proc/self/ns/mnt", O_RDONLY); | ||
| 69 | ASSERT_GE(self->ns_fd, 0); | ||
| 70 | |||
| 71 | ASSERT_EQ(mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL), 0); | ||
| 72 | |||
| 73 | strcpy(self->root_mntpoint, root_mntpoint_templ); | ||
| 74 | ASSERT_NE(mkdtemp(self->root_mntpoint), NULL); | ||
| 75 | |||
| 76 | self->orig_root = open("/", O_PATH | O_CLOEXEC); | ||
| 77 | ASSERT_GE(self->orig_root, 0); | ||
| 78 | |||
| 79 | ASSERT_EQ(mount("tmpfs", self->root_mntpoint, "tmpfs", 0, NULL), 0); | ||
| 80 | |||
| 81 | ASSERT_EQ(chroot(self->root_mntpoint), 0); | ||
| 82 | |||
| 83 | ASSERT_EQ(chdir("/"), 0); | ||
| 84 | |||
| 85 | ASSERT_EQ(mkdir("a", 0700), 0); | ||
| 86 | |||
| 87 | ASSERT_EQ(mkdir("b", 0700), 0); | ||
| 88 | |||
| 89 | self->root_id = get_mnt_id(_metadata, "/"); | ||
| 90 | ASSERT_NE(self->root_id, 0); | ||
| 91 | |||
| 92 | self->fan_fd = fanotify_init(FAN_REPORT_MNT, 0); | ||
| 93 | ASSERT_GE(self->fan_fd, 0); | ||
| 94 | |||
| 95 | ret = fanotify_mark(self->fan_fd, FAN_MARK_ADD | FAN_MARK_MNTNS, | ||
| 96 | FAN_MNT_ATTACH | FAN_MNT_DETACH, self->ns_fd, NULL); | ||
| 97 | ASSERT_EQ(ret, 0); | ||
| 98 | |||
| 99 | self->rem = 0; | ||
| 100 | } | ||
| 101 | |||
| 102 | FIXTURE_TEARDOWN(fanotify) | ||
| 103 | { | ||
| 104 | ASSERT_EQ(self->rem, 0); | ||
| 105 | close(self->fan_fd); | ||
| 106 | |||
| 107 | ASSERT_EQ(fchdir(self->orig_root), 0); | ||
| 108 | |||
| 109 | ASSERT_EQ(chroot("."), 0); | ||
| 110 | |||
| 111 | EXPECT_EQ(umount2(self->root_mntpoint, MNT_DETACH), 0); | ||
| 112 | EXPECT_EQ(chdir(self->root_mntpoint), 0); | ||
| 113 | EXPECT_EQ(chdir("/"), 0); | ||
| 114 | EXPECT_EQ(rmdir(self->root_mntpoint), 0); | ||
| 115 | } | ||
| 116 | |||
| 117 | static uint64_t expect_notify(struct __test_metadata *const _metadata, | ||
| 118 | FIXTURE_DATA(fanotify) *self, | ||
| 119 | uint64_t *mask) | ||
| 120 | { | ||
| 121 | struct fanotify_event_metadata *meta; | ||
| 122 | struct fanotify_event_info_mnt *mnt; | ||
| 123 | unsigned int thislen; | ||
| 124 | |||
| 125 | if (!self->rem) { | ||
| 126 | ssize_t len = read(self->fan_fd, self->buf, sizeof(self->buf)); | ||
| 127 | ASSERT_GT(len, 0); | ||
| 128 | |||
| 129 | self->rem = len; | ||
| 130 | self->next = (void *) self->buf; | ||
| 131 | } | ||
| 132 | |||
| 133 | meta = self->next; | ||
| 134 | ASSERT_TRUE(FAN_EVENT_OK(meta, self->rem)); | ||
| 135 | |||
| 136 | thislen = meta->event_len; | ||
| 137 | self->rem -= thislen; | ||
| 138 | self->next += thislen; | ||
| 139 | |||
| 140 | *mask = meta->mask; | ||
| 141 | thislen -= sizeof(*meta); | ||
| 142 | |||
| 143 | mnt = ((void *) meta) + meta->event_len - thislen; | ||
| 144 | |||
| 145 | ASSERT_EQ(thislen, sizeof(*mnt)); | ||
| 146 | |||
| 147 | return mnt->mnt_id; | ||
| 148 | } | ||
| 149 | |||
| 150 | static void expect_notify_n(struct __test_metadata *const _metadata, | ||
| 151 | FIXTURE_DATA(fanotify) *self, | ||
| 152 | unsigned int n, uint64_t mask[], uint64_t mnts[]) | ||
| 153 | { | ||
| 154 | unsigned int i; | ||
| 155 | |||
| 156 | for (i = 0; i < n; i++) | ||
| 157 | mnts[i] = expect_notify(_metadata, self, &mask[i]); | ||
| 158 | } | ||
| 159 | |||
| 160 | static uint64_t expect_notify_mask(struct __test_metadata *const _metadata, | ||
| 161 | FIXTURE_DATA(fanotify) *self, | ||
| 162 | uint64_t expect_mask) | ||
| 163 | { | ||
| 164 | uint64_t mntid, mask; | ||
| 165 | |||
| 166 | mntid = expect_notify(_metadata, self, &mask); | ||
| 167 | ASSERT_EQ(expect_mask, mask); | ||
| 168 | |||
| 169 | return mntid; | ||
| 170 | } | ||
| 171 | |||
| 172 | |||
| 173 | static void expect_notify_mask_n(struct __test_metadata *const _metadata, | ||
| 174 | FIXTURE_DATA(fanotify) *self, | ||
| 175 | uint64_t mask, unsigned int n, uint64_t mnts[]) | ||
| 176 | { | ||
| 177 | unsigned int i; | ||
| 178 | |||
| 179 | for (i = 0; i < n; i++) | ||
| 180 | mnts[i] = expect_notify_mask(_metadata, self, mask); | ||
| 181 | } | ||
| 182 | |||
| 183 | static void verify_mount_ids(struct __test_metadata *const _metadata, | ||
| 184 | const uint64_t list1[], const uint64_t list2[], | ||
| 185 | size_t num) | ||
| 186 | { | ||
| 187 | unsigned int i, j; | ||
| 188 | |||
| 189 | // Check that neither list has any duplicates | ||
| 190 | for (i = 0; i < num; i++) { | ||
| 191 | for (j = 0; j < num; j++) { | ||
| 192 | if (i != j) { | ||
| 193 | ASSERT_NE(list1[i], list1[j]); | ||
| 194 | ASSERT_NE(list2[i], list2[j]); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | } | ||
| 198 | // Check that all list1 memebers can be found in list2. Together with | ||
| 199 | // the above it means that the list1 and list2 represent the same sets. | ||
| 200 | for (i = 0; i < num; i++) { | ||
| 201 | for (j = 0; j < num; j++) { | ||
| 202 | if (list1[i] == list2[j]) | ||
| 203 | break; | ||
| 204 | } | ||
| 205 | ASSERT_NE(j, num); | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | static void check_mounted(struct __test_metadata *const _metadata, | ||
| 210 | const uint64_t mnts[], size_t num) | ||
| 211 | { | ||
| 212 | ssize_t ret; | ||
| 213 | uint64_t *list; | ||
| 214 | |||
| 215 | list = malloc((num + 1) * sizeof(list[0])); | ||
| 216 | ASSERT_NE(list, NULL); | ||
| 217 | |||
| 218 | ret = listmount(LSMT_ROOT, 0, 0, list, num + 1, 0); | ||
| 219 | ASSERT_EQ(ret, num); | ||
| 220 | |||
| 221 | verify_mount_ids(_metadata, mnts, list, num); | ||
| 222 | |||
| 223 | free(list); | ||
| 224 | } | ||
| 225 | |||
| 226 | static void setup_mount_tree(struct __test_metadata *const _metadata, | ||
| 227 | int log2_num) | ||
| 228 | { | ||
| 229 | int ret, i; | ||
| 230 | |||
| 231 | ret = mount("", "/", NULL, MS_SHARED, NULL); | ||
| 232 | ASSERT_EQ(ret, 0); | ||
| 233 | |||
| 234 | for (i = 0; i < log2_num; i++) { | ||
| 235 | ret = mount("/", "/", NULL, MS_BIND, NULL); | ||
| 236 | ASSERT_EQ(ret, 0); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | |||
| 240 | TEST_F(fanotify, bind) | ||
| 241 | { | ||
| 242 | int ret; | ||
| 243 | uint64_t mnts[2] = { self->root_id }; | ||
| 244 | |||
| 245 | ret = mount("/", "/", NULL, MS_BIND, NULL); | ||
| 246 | ASSERT_EQ(ret, 0); | ||
| 247 | |||
| 248 | mnts[1] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH); | ||
| 249 | ASSERT_NE(mnts[0], mnts[1]); | ||
| 250 | |||
| 251 | check_mounted(_metadata, mnts, 2); | ||
| 252 | |||
| 253 | // Cleanup | ||
| 254 | uint64_t detach_id; | ||
| 255 | ret = umount("/"); | ||
| 256 | ASSERT_EQ(ret, 0); | ||
| 257 | |||
| 258 | detach_id = expect_notify_mask(_metadata, self, FAN_MNT_DETACH); | ||
| 259 | ASSERT_EQ(detach_id, mnts[1]); | ||
| 260 | |||
| 261 | check_mounted(_metadata, mnts, 1); | ||
| 262 | } | ||
| 263 | |||
| 264 | TEST_F(fanotify, move) | ||
| 265 | { | ||
| 266 | int ret; | ||
| 267 | uint64_t mnts[2] = { self->root_id }; | ||
| 268 | uint64_t move_id; | ||
| 269 | |||
| 270 | ret = mount("/", "/a", NULL, MS_BIND, NULL); | ||
| 271 | ASSERT_EQ(ret, 0); | ||
| 272 | |||
| 273 | mnts[1] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH); | ||
| 274 | ASSERT_NE(mnts[0], mnts[1]); | ||
| 275 | |||
| 276 | check_mounted(_metadata, mnts, 2); | ||
| 277 | |||
| 278 | ret = move_mount(AT_FDCWD, "/a", AT_FDCWD, "/b", 0); | ||
| 279 | ASSERT_EQ(ret, 0); | ||
| 280 | |||
| 281 | move_id = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH | FAN_MNT_DETACH); | ||
| 282 | ASSERT_EQ(move_id, mnts[1]); | ||
| 283 | |||
| 284 | // Cleanup | ||
| 285 | ret = umount("/b"); | ||
| 286 | ASSERT_EQ(ret, 0); | ||
| 287 | |||
| 288 | check_mounted(_metadata, mnts, 1); | ||
| 289 | } | ||
| 290 | |||
| 291 | TEST_F(fanotify, propagate) | ||
| 292 | { | ||
| 293 | const unsigned int log2_num = 4; | ||
| 294 | const unsigned int num = (1 << log2_num); | ||
| 295 | uint64_t mnts[num]; | ||
| 296 | |||
| 297 | setup_mount_tree(_metadata, log2_num); | ||
| 298 | |||
| 299 | expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH, num - 1, mnts + 1); | ||
| 300 | |||
| 301 | mnts[0] = self->root_id; | ||
| 302 | check_mounted(_metadata, mnts, num); | ||
| 303 | |||
| 304 | // Cleanup | ||
| 305 | int ret; | ||
| 306 | uint64_t mnts2[num]; | ||
| 307 | ret = umount2("/", MNT_DETACH); | ||
| 308 | ASSERT_EQ(ret, 0); | ||
| 309 | |||
| 310 | ret = mount("", "/", NULL, MS_PRIVATE, NULL); | ||
| 311 | ASSERT_EQ(ret, 0); | ||
| 312 | |||
| 313 | mnts2[0] = self->root_id; | ||
| 314 | expect_notify_mask_n(_metadata, self, FAN_MNT_DETACH, num - 1, mnts2 + 1); | ||
| 315 | verify_mount_ids(_metadata, mnts, mnts2, num); | ||
| 316 | |||
| 317 | check_mounted(_metadata, mnts, 1); | ||
| 318 | } | ||
| 319 | |||
| 320 | TEST_F(fanotify, fsmount) | ||
| 321 | { | ||
| 322 | int ret, fs, mnt; | ||
| 323 | uint64_t mnts[2] = { self->root_id }; | ||
| 324 | |||
| 325 | fs = fsopen("tmpfs", 0); | ||
| 326 | ASSERT_GE(fs, 0); | ||
| 327 | |||
| 328 | ret = fsconfig(fs, FSCONFIG_CMD_CREATE, 0, 0, 0); | ||
| 329 | ASSERT_EQ(ret, 0); | ||
| 330 | |||
| 331 | mnt = fsmount(fs, 0, 0); | ||
| 332 | ASSERT_GE(mnt, 0); | ||
| 333 | |||
| 334 | close(fs); | ||
| 335 | |||
| 336 | ret = move_mount(mnt, "", AT_FDCWD, "/a", MOVE_MOUNT_F_EMPTY_PATH); | ||
| 337 | ASSERT_EQ(ret, 0); | ||
| 338 | |||
| 339 | close(mnt); | ||
| 340 | |||
| 341 | mnts[1] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH); | ||
| 342 | ASSERT_NE(mnts[0], mnts[1]); | ||
| 343 | |||
| 344 | check_mounted(_metadata, mnts, 2); | ||
| 345 | |||
| 346 | // Cleanup | ||
| 347 | uint64_t detach_id; | ||
| 348 | ret = umount("/a"); | ||
| 349 | ASSERT_EQ(ret, 0); | ||
| 350 | |||
| 351 | detach_id = expect_notify_mask(_metadata, self, FAN_MNT_DETACH); | ||
| 352 | ASSERT_EQ(detach_id, mnts[1]); | ||
| 353 | |||
| 354 | check_mounted(_metadata, mnts, 1); | ||
| 355 | } | ||
| 356 | |||
| 357 | TEST_F(fanotify, reparent) | ||
| 358 | { | ||
| 359 | uint64_t mnts[6] = { self->root_id }; | ||
| 360 | uint64_t dmnts[3]; | ||
| 361 | uint64_t masks[3]; | ||
| 362 | unsigned int i; | ||
| 363 | int ret; | ||
| 364 | |||
| 365 | // Create setup with a[1] -> b[2] propagation | ||
| 366 | ret = mount("/", "/a", NULL, MS_BIND, NULL); | ||
| 367 | ASSERT_EQ(ret, 0); | ||
| 368 | |||
| 369 | ret = mount("", "/a", NULL, MS_SHARED, NULL); | ||
| 370 | ASSERT_EQ(ret, 0); | ||
| 371 | |||
| 372 | ret = mount("/a", "/b", NULL, MS_BIND, NULL); | ||
| 373 | ASSERT_EQ(ret, 0); | ||
| 374 | |||
| 375 | ret = mount("", "/b", NULL, MS_SLAVE, NULL); | ||
| 376 | ASSERT_EQ(ret, 0); | ||
| 377 | |||
| 378 | expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH, 2, mnts + 1); | ||
| 379 | |||
| 380 | check_mounted(_metadata, mnts, 3); | ||
| 381 | |||
| 382 | // Mount on a[3], which is propagated to b[4] | ||
| 383 | ret = mount("/", "/a", NULL, MS_BIND, NULL); | ||
| 384 | ASSERT_EQ(ret, 0); | ||
| 385 | |||
| 386 | expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH, 2, mnts + 3); | ||
| 387 | |||
| 388 | check_mounted(_metadata, mnts, 5); | ||
| 389 | |||
| 390 | // Mount on b[5], not propagated | ||
| 391 | ret = mount("/", "/b", NULL, MS_BIND, NULL); | ||
| 392 | ASSERT_EQ(ret, 0); | ||
| 393 | |||
| 394 | mnts[5] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH); | ||
| 395 | |||
| 396 | check_mounted(_metadata, mnts, 6); | ||
| 397 | |||
| 398 | // Umount a[3], which is propagated to b[4], but not b[5] | ||
| 399 | // This will result in b[5] "falling" on b[2] | ||
| 400 | ret = umount("/a"); | ||
| 401 | ASSERT_EQ(ret, 0); | ||
| 402 | |||
| 403 | expect_notify_n(_metadata, self, 3, masks, dmnts); | ||
| 404 | verify_mount_ids(_metadata, mnts + 3, dmnts, 3); | ||
| 405 | |||
| 406 | for (i = 0; i < 3; i++) { | ||
| 407 | if (dmnts[i] == mnts[5]) { | ||
| 408 | ASSERT_EQ(masks[i], FAN_MNT_ATTACH | FAN_MNT_DETACH); | ||
| 409 | } else { | ||
| 410 | ASSERT_EQ(masks[i], FAN_MNT_DETACH); | ||
| 411 | } | ||
| 412 | } | ||
| 413 | |||
| 414 | mnts[3] = mnts[5]; | ||
| 415 | check_mounted(_metadata, mnts, 4); | ||
| 416 | |||
| 417 | // Cleanup | ||
| 418 | ret = umount("/b"); | ||
| 419 | ASSERT_EQ(ret, 0); | ||
| 420 | |||
| 421 | ret = umount("/a"); | ||
| 422 | ASSERT_EQ(ret, 0); | ||
| 423 | |||
| 424 | ret = umount("/b"); | ||
| 425 | ASSERT_EQ(ret, 0); | ||
| 426 | |||
| 427 | expect_notify_mask_n(_metadata, self, FAN_MNT_DETACH, 3, dmnts); | ||
| 428 | verify_mount_ids(_metadata, mnts + 1, dmnts, 3); | ||
| 429 | |||
| 430 | check_mounted(_metadata, mnts, 1); | ||
| 431 | } | ||
| 432 | |||
| 433 | TEST_F(fanotify, rmdir) | ||
| 434 | { | ||
| 435 | uint64_t mnts[3] = { self->root_id }; | ||
| 436 | int ret; | ||
| 437 | |||
| 438 | ret = mount("/", "/a", NULL, MS_BIND, NULL); | ||
| 439 | ASSERT_EQ(ret, 0); | ||
| 440 | |||
| 441 | ret = mount("/", "/a/b", NULL, MS_BIND, NULL); | ||
| 442 | ASSERT_EQ(ret, 0); | ||
| 443 | |||
| 444 | expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH, 2, mnts + 1); | ||
| 445 | |||
| 446 | check_mounted(_metadata, mnts, 3); | ||
| 447 | |||
| 448 | ret = chdir("/a"); | ||
| 449 | ASSERT_EQ(ret, 0); | ||
| 450 | |||
| 451 | ret = fork(); | ||
| 452 | ASSERT_GE(ret, 0); | ||
| 453 | |||
| 454 | if (ret == 0) { | ||
| 455 | chdir("/"); | ||
| 456 | unshare(CLONE_NEWNS); | ||
| 457 | mount("", "/", NULL, MS_REC|MS_PRIVATE, NULL); | ||
| 458 | umount2("/a", MNT_DETACH); | ||
| 459 | // This triggers a detach in the other namespace | ||
| 460 | rmdir("/a"); | ||
| 461 | exit(0); | ||
| 462 | } | ||
| 463 | wait(NULL); | ||
| 464 | |||
| 465 | expect_notify_mask_n(_metadata, self, FAN_MNT_DETACH, 2, mnts + 1); | ||
| 466 | check_mounted(_metadata, mnts, 1); | ||
| 467 | |||
| 468 | // Cleanup | ||
| 469 | ret = chdir("/"); | ||
| 470 | ASSERT_EQ(ret, 0); | ||
| 471 | } | ||
| 472 | |||
| 473 | TEST_F(fanotify, pivot_root) | ||
| 474 | { | ||
| 475 | uint64_t mnts[3] = { self->root_id }; | ||
| 476 | uint64_t mnts2[3]; | ||
| 477 | int ret; | ||
| 478 | |||
| 479 | ret = mount("tmpfs", "/a", "tmpfs", 0, NULL); | ||
| 480 | ASSERT_EQ(ret, 0); | ||
| 481 | |||
| 482 | mnts[2] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH); | ||
| 483 | |||
| 484 | ret = mkdir("/a/new", 0700); | ||
| 485 | ASSERT_EQ(ret, 0); | ||
| 486 | |||
| 487 | ret = mkdir("/a/old", 0700); | ||
| 488 | ASSERT_EQ(ret, 0); | ||
| 489 | |||
| 490 | ret = mount("/a", "/a/new", NULL, MS_BIND, NULL); | ||
| 491 | ASSERT_EQ(ret, 0); | ||
| 492 | |||
| 493 | mnts[1] = expect_notify_mask(_metadata, self, FAN_MNT_ATTACH); | ||
| 494 | check_mounted(_metadata, mnts, 3); | ||
| 495 | |||
| 496 | ret = syscall(SYS_pivot_root, "/a/new", "/a/new/old"); | ||
| 497 | ASSERT_EQ(ret, 0); | ||
| 498 | |||
| 499 | expect_notify_mask_n(_metadata, self, FAN_MNT_ATTACH | FAN_MNT_DETACH, 2, mnts2); | ||
| 500 | verify_mount_ids(_metadata, mnts, mnts2, 2); | ||
| 501 | check_mounted(_metadata, mnts, 3); | ||
| 502 | |||
| 503 | // Cleanup | ||
| 504 | ret = syscall(SYS_pivot_root, "/old", "/old/a/new"); | ||
| 505 | ASSERT_EQ(ret, 0); | ||
| 506 | |||
| 507 | ret = umount("/a/new"); | ||
| 508 | ASSERT_EQ(ret, 0); | ||
| 509 | |||
| 510 | ret = umount("/a"); | ||
| 511 | ASSERT_EQ(ret, 0); | ||
| 512 | |||
| 513 | check_mounted(_metadata, mnts, 1); | ||
| 514 | } | ||
| 515 | |||
| 516 | TEST_HARNESS_MAIN | ||
| @@ -20,12 +20,16 @@ FIXTURE(set_layers_via_fds) { · 19 unmodified lines | |||
| 20 | FIXTURE_SETUP(set_layers_via_fds) | 20 | FIXTURE_SETUP(set_layers_via_fds) |
| 21 | { | 21 | { |
| 22 | ASSERT_EQ(mkdir("/set_layers_via_fds", 0755), 0); | 22 | ASSERT_EQ(mkdir("/set_layers_via_fds", 0755), 0); |
| 23 | ASSERT_EQ(mkdir("/set_layers_via_fds_tmpfs", 0755), 0); | ||
| 23 | } | 24 | } |
| 24 | 25 | ||
| 25 | FIXTURE_TEARDOWN(set_layers_via_fds) | 26 | FIXTURE_TEARDOWN(set_layers_via_fds) |
| 26 | { | 27 | { |
| 27 | umount2("/set_layers_via_fds", 0); | 28 | umount2("/set_layers_via_fds", 0); |
| 28 | ASSERT_EQ(rmdir("/set_layers_via_fds"), 0); | 29 | ASSERT_EQ(rmdir("/set_layers_via_fds"), 0); |
| 30 | |||
| 31 | umount2("/set_layers_via_fds_tmpfs", 0); | ||
| 32 | ASSERT_EQ(rmdir("/set_layers_via_fds_tmpfs"), 0); | ||
| 29 | } | 33 | } |
| 30 | 34 | ||
| 31 | TEST_F(set_layers_via_fds, set_layers_via_fds) | 35 | TEST_F(set_layers_via_fds, set_layers_via_fds) |
| @@ -214,4 +218,195 @@ TEST_F(set_layers_via_fds, set_500_layers_via_fds) · 182 unmodified lines | |||
| 214 | ASSERT_EQ(close(fd_overlay), 0); | 218 | ASSERT_EQ(close(fd_overlay), 0); |
| 215 | } | 219 | } |
| 216 | 220 | ||
| 221 | TEST_F(set_layers_via_fds, set_500_layers_via_opath_fds) | ||
| 222 | { | ||
| 223 | int fd_context, fd_tmpfs, fd_overlay, fd_work, fd_upper, fd_lower; | ||
| 224 | int layer_fds[500] = { [0 ... 499] = -EBADF }; | ||
| 225 | |||
| 226 | ASSERT_EQ(unshare(CLONE_NEWNS), 0); | ||
| 227 | ASSERT_EQ(sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL), 0); | ||
| 228 | |||
| 229 | fd_context = sys_fsopen("tmpfs", 0); | ||
| 230 | ASSERT_GE(fd_context, 0); | ||
| 231 | |||
| 232 | ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0), 0); | ||
| 233 | fd_tmpfs = sys_fsmount(fd_context, 0, 0); | ||
| 234 | ASSERT_GE(fd_tmpfs, 0); | ||
| 235 | ASSERT_EQ(close(fd_context), 0); | ||
| 236 | |||
| 237 | for (int i = 0; i < ARRAY_SIZE(layer_fds); i++) { | ||
| 238 | char path[100]; | ||
| 239 | |||
| 240 | sprintf(path, "l%d", i); | ||
| 241 | ASSERT_EQ(mkdirat(fd_tmpfs, path, 0755), 0); | ||
| 242 | layer_fds[i] = openat(fd_tmpfs, path, O_DIRECTORY | O_PATH); | ||
| 243 | ASSERT_GE(layer_fds[i], 0); | ||
| 244 | } | ||
| 245 | |||
| 246 | ASSERT_EQ(mkdirat(fd_tmpfs, "w", 0755), 0); | ||
| 247 | fd_work = openat(fd_tmpfs, "w", O_DIRECTORY | O_PATH); | ||
| 248 | ASSERT_GE(fd_work, 0); | ||
| 249 | |||
| 250 | ASSERT_EQ(mkdirat(fd_tmpfs, "u", 0755), 0); | ||
| 251 | fd_upper = openat(fd_tmpfs, "u", O_DIRECTORY | O_PATH); | ||
| 252 | ASSERT_GE(fd_upper, 0); | ||
| 253 | |||
| 254 | ASSERT_EQ(mkdirat(fd_tmpfs, "l501", 0755), 0); | ||
| 255 | fd_lower = openat(fd_tmpfs, "l501", O_DIRECTORY | O_PATH); | ||
| 256 | ASSERT_GE(fd_lower, 0); | ||
| 257 | |||
| 258 | ASSERT_EQ(sys_move_mount(fd_tmpfs, "", -EBADF, "/tmp", MOVE_MOUNT_F_EMPTY_PATH), 0); | ||
| 259 | ASSERT_EQ(close(fd_tmpfs), 0); | ||
| 260 | |||
| 261 | fd_context = sys_fsopen("overlay", 0); | ||
| 262 | ASSERT_GE(fd_context, 0); | ||
| 263 | |||
| 264 | ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "workdir", NULL, fd_work), 0); | ||
| 265 | ASSERT_EQ(close(fd_work), 0); | ||
| 266 | |||
| 267 | ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "upperdir", NULL, fd_upper), 0); | ||
| 268 | ASSERT_EQ(close(fd_upper), 0); | ||
| 269 | |||
| 270 | for (int i = 0; i < ARRAY_SIZE(layer_fds); i++) { | ||
| 271 | ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "lowerdir+", NULL, layer_fds[i]), 0); | ||
| 272 | ASSERT_EQ(close(layer_fds[i]), 0); | ||
| 273 | } | ||
| 274 | |||
| 275 | ASSERT_NE(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "lowerdir+", NULL, fd_lower), 0); | ||
| 276 | ASSERT_EQ(close(fd_lower), 0); | ||
| 277 | |||
| 278 | ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0), 0); | ||
| 279 | |||
| 280 | fd_overlay = sys_fsmount(fd_context, 0, 0); | ||
| 281 | ASSERT_GE(fd_overlay, 0); | ||
| 282 | ASSERT_EQ(close(fd_context), 0); | ||
| 283 | ASSERT_EQ(close(fd_overlay), 0); | ||
| 284 | } | ||
| 285 | |||
| 286 | TEST_F(set_layers_via_fds, set_layers_via_detached_mount_fds) | ||
| 287 | { | ||
| 288 | int fd_context, fd_tmpfs, fd_overlay, fd_tmp; | ||
| 289 | int layer_fds[] = { [0 ... 8] = -EBADF }; | ||
| 290 | bool layers_found[] = { [0 ... 8] = false }; | ||
| 291 | size_t len = 0; | ||
| 292 | char *line = NULL; | ||
| 293 | FILE *f_mountinfo; | ||
| 294 | |||
| 295 | ASSERT_EQ(unshare(CLONE_NEWNS), 0); | ||
| 296 | ASSERT_EQ(sys_mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL), 0); | ||
| 297 | |||
| 298 | fd_context = sys_fsopen("tmpfs", 0); | ||
| 299 | ASSERT_GE(fd_context, 0); | ||
| 300 | |||
| 301 | ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0), 0); | ||
| 302 | fd_tmpfs = sys_fsmount(fd_context, 0, 0); | ||
| 303 | ASSERT_GE(fd_tmpfs, 0); | ||
| 304 | ASSERT_EQ(close(fd_context), 0); | ||
| 305 | |||
| 306 | ASSERT_EQ(mkdirat(fd_tmpfs, "u", 0755), 0); | ||
| 307 | ASSERT_EQ(mkdirat(fd_tmpfs, "u/upper", 0755), 0); | ||
| 308 | ASSERT_EQ(mkdirat(fd_tmpfs, "u/work", 0755), 0); | ||
| 309 | ASSERT_EQ(mkdirat(fd_tmpfs, "l1", 0755), 0); | ||
| 310 | ASSERT_EQ(mkdirat(fd_tmpfs, "l2", 0755), 0); | ||
| 311 | ASSERT_EQ(mkdirat(fd_tmpfs, "l3", 0755), 0); | ||
| 312 | ASSERT_EQ(mkdirat(fd_tmpfs, "l4", 0755), 0); | ||
| 313 | ASSERT_EQ(mkdirat(fd_tmpfs, "d1", 0755), 0); | ||
| 314 | ASSERT_EQ(mkdirat(fd_tmpfs, "d2", 0755), 0); | ||
| 315 | ASSERT_EQ(mkdirat(fd_tmpfs, "d3", 0755), 0); | ||
| 316 | |||
| 317 | ASSERT_EQ(sys_move_mount(fd_tmpfs, "", -EBADF, "/set_layers_via_fds_tmpfs", MOVE_MOUNT_F_EMPTY_PATH), 0); | ||
| 318 | |||
| 319 | fd_tmp = open_tree(fd_tmpfs, "u", OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); | ||
| 320 | ASSERT_GE(fd_tmp, 0); | ||
| 321 | |||
| 322 | layer_fds[0] = openat(fd_tmp, "upper", O_CLOEXEC | O_DIRECTORY | O_PATH); | ||
| 323 | ASSERT_GE(layer_fds[0], 0); | ||
| 324 | |||
| 325 | layer_fds[1] = openat(fd_tmp, "work", O_CLOEXEC | O_DIRECTORY | O_PATH); | ||
| 326 | ASSERT_GE(layer_fds[1], 0); | ||
| 327 | |||
| 328 | layer_fds[2] = open_tree(fd_tmpfs, "l1", OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); | ||
| 329 | ASSERT_GE(layer_fds[2], 0); | ||
| 330 | |||
| 331 | layer_fds[3] = open_tree(fd_tmpfs, "l2", OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); | ||
| 332 | ASSERT_GE(layer_fds[3], 0); | ||
| 333 | |||
| 334 | layer_fds[4] = open_tree(fd_tmpfs, "l3", OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); | ||
| 335 | ASSERT_GE(layer_fds[4], 0); | ||
| 336 | |||
| 337 | layer_fds[5] = open_tree(fd_tmpfs, "l4", OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); | ||
| 338 | ASSERT_GE(layer_fds[5], 0); | ||
| 339 | |||
| 340 | layer_fds[6] = open_tree(fd_tmpfs, "d1", OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); | ||
| 341 | ASSERT_GE(layer_fds[6], 0); | ||
| 342 | |||
| 343 | layer_fds[7] = open_tree(fd_tmpfs, "d2", OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); | ||
| 344 | ASSERT_GE(layer_fds[7], 0); | ||
| 345 | |||
| 346 | layer_fds[8] = open_tree(fd_tmpfs, "d3", OPEN_TREE_CLONE | OPEN_TREE_CLOEXEC); | ||
| 347 | ASSERT_GE(layer_fds[8], 0); | ||
| 348 | |||
| 349 | ASSERT_EQ(close(fd_tmpfs), 0); | ||
| 350 | |||
| 351 | fd_context = sys_fsopen("overlay", 0); | ||
| 352 | ASSERT_GE(fd_context, 0); | ||
| 353 | |||
| 354 | ASSERT_NE(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "lowerdir", NULL, layer_fds[2]), 0); | ||
| 355 | |||
| 356 | ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "upperdir", NULL, layer_fds[0]), 0); | ||
| 357 | ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "workdir", NULL, layer_fds[1]), 0); | ||
| 358 | ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "lowerdir+", NULL, layer_fds[2]), 0); | ||
| 359 | ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "lowerdir+", NULL, layer_fds[3]), 0); | ||
| 360 | ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "lowerdir+", NULL, layer_fds[4]), 0); | ||
| 361 | ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "lowerdir+", NULL, layer_fds[5]), 0); | ||
| 362 | ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "datadir+", NULL, layer_fds[6]), 0); | ||
| 363 | ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "datadir+", NULL, layer_fds[7]), 0); | ||
| 364 | ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_FD, "datadir+", NULL, layer_fds[8]), 0); | ||
| 365 | |||
| 366 | ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_SET_STRING, "metacopy", "on", 0), 0); | ||
| 367 | |||
| 368 | ASSERT_EQ(sys_fsconfig(fd_context, FSCONFIG_CMD_CREATE, NULL, NULL, 0), 0); | ||
| 369 | |||
| 370 | fd_overlay = sys_fsmount(fd_context, 0, 0); | ||
| 371 | ASSERT_GE(fd_overlay, 0); | ||
| 372 | |||
| 373 | ASSERT_EQ(sys_move_mount(fd_overlay, "", -EBADF, "/set_layers_via_fds", MOVE_MOUNT_F_EMPTY_PATH), 0); | ||
| 374 | |||
| 375 | f_mountinfo = fopen("/proc/self/mountinfo", "r"); | ||
| 376 | ASSERT_NE(f_mountinfo, NULL); | ||
| 377 | |||
| 378 | while (getline(&line, &len, f_mountinfo) != -1) { | ||
| 379 | char *haystack = line; | ||
| 380 | |||
| 381 | if (strstr(haystack, "workdir=/tmp/w")) | ||
| 382 | layers_found[0] = true; | ||
| 383 | if (strstr(haystack, "upperdir=/tmp/u")) | ||
| 384 | layers_found[1] = true; | ||
| 385 | if (strstr(haystack, "lowerdir+=/tmp/l1")) | ||
| 386 | layers_found[2] = true; | ||
| 387 | if (strstr(haystack, "lowerdir+=/tmp/l2")) | ||
| 388 | layers_found[3] = true; | ||
| 389 | if (strstr(haystack, "lowerdir+=/tmp/l3")) | ||
| 390 | layers_found[4] = true; | ||
| 391 | if (strstr(haystack, "lowerdir+=/tmp/l4")) | ||
| 392 | layers_found[5] = true; | ||
| 393 | if (strstr(haystack, "datadir+=/tmp/d1")) | ||
| 394 | layers_found[6] = true; | ||
| 395 | if (strstr(haystack, "datadir+=/tmp/d2")) | ||
| 396 | layers_found[7] = true; | ||
| 397 | if (strstr(haystack, "datadir+=/tmp/d3")) | ||
| 398 | layers_found[8] = true; | ||
| 399 | } | ||
| 400 | free(line); | ||
| 401 | |||
| 402 | for (int i = 0; i < ARRAY_SIZE(layer_fds); i++) { | ||
| 403 | ASSERT_EQ(layers_found[i], true); | ||
| 404 | ASSERT_EQ(close(layer_fds[i]), 0); | ||
| 405 | } | ||
| 406 | |||
| 407 | ASSERT_EQ(close(fd_context), 0); | ||
| 408 | ASSERT_EQ(close(fd_overlay), 0); | ||
| 409 | ASSERT_EQ(fclose(f_mountinfo), 0); | ||
| 410 | } | ||
| 411 | |||
| 217 | TEST_HARNESS_MAIN | 412 | TEST_HARNESS_MAIN |
| @@ -44,4 +44,21 @@ static inline int sys_move_mount(int from_dfd, const char *from_pathname, · 43 unmodified lines | |||
| 44 | to_pathname, flags); | 44 | to_pathname, flags); |
| 45 | } | 45 | } |
| 46 | 46 | ||
| 47 | #ifndef OPEN_TREE_CLONE | ||
| 48 | #define OPEN_TREE_CLONE 1 | ||
| 49 | #endif | ||
| 50 | |||
| 51 | #ifndef OPEN_TREE_CLOEXEC | ||
| 52 | #define OPEN_TREE_CLOEXEC O_CLOEXEC | ||
| 53 | #endif | ||
| 54 | |||
| 55 | #ifndef AT_RECURSIVE | ||
| 56 | #define AT_RECURSIVE 0x8000 | ||
| 57 | #endif | ||
| 58 | |||
| 59 | static inline int sys_open_tree(int dfd, const char *filename, unsigned int flags) | ||
| 60 | { | ||
| 61 | return syscall(__NR_open_tree, dfd, filename, flags); | ||
| 62 | } | ||
| 63 | |||
| 47 | #endif | 64 | #endif |
| @@ -25,7 +25,7 @@ static inline int statmount(uint64_t mnt_id, uint64_t mnt_ns_id, uint64_t mask, · 24 unmodified lines | |||
| 25 | return syscall(__NR_statmount, &req, buf, bufsize, flags); | 25 | return syscall(__NR_statmount, &req, buf, bufsize, flags); |
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | static ssize_t listmount(uint64_t mnt_id, uint64_t mnt_ns_id, | 28 | static inline ssize_t listmount(uint64_t mnt_id, uint64_t mnt_ns_id, |
| 29 | uint64_t last_mnt_id, uint64_t list[], size_t num, | 29 | uint64_t last_mnt_id, uint64_t list[], size_t num, |
| 30 | unsigned int flags) | 30 | unsigned int flags) |
| 31 | { | 31 | { |