[PATCH v5 3/3] vfs: add notifications for mount attach and detach
Jan Kara
jack at suse.cz
Tue Feb 11 13:04:16 UTC 2025
On Wed 29-01-25 17:58:01, Miklos Szeredi wrote:
> Add notifications for attaching and detaching mounts to fs/namespace.c
>
> Signed-off-by: Miklos Szeredi <mszeredi at redhat.com>
Looks good to me. Feel free to add:
Reviewed-by: Jan Kara <jack at suse.cz>
Honza
> ---
> fs/mount.h | 20 +++++++++++++
> fs/namespace.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++-
> fs/pnode.c | 4 ++-
> 3 files changed, 101 insertions(+), 2 deletions(-)
>
> diff --git a/fs/mount.h b/fs/mount.h
> index 5324a931b403..946dc8b792d7 100644
> --- a/fs/mount.h
> +++ b/fs/mount.h
> @@ -5,6 +5,8 @@
> #include <linux/ns_common.h>
> #include <linux/fs_pin.h>
>
> +extern struct list_head notify_list;
> +
> struct mnt_namespace {
> struct ns_common ns;
> struct mount * root;
> @@ -80,6 +82,8 @@ struct mount {
> #ifdef CONFIG_FSNOTIFY
> struct fsnotify_mark_connector __rcu *mnt_fsnotify_marks;
> __u32 mnt_fsnotify_mask;
> + struct list_head to_notify; /* need to queue notification */
> + struct mnt_namespace *prev_ns; /* previous namespace (NULL if none) */
> #endif
> int mnt_id; /* mount identifier, reused */
> u64 mnt_id_unique; /* mount ID unique until reboot */
> @@ -182,4 +186,20 @@ static inline struct mnt_namespace *to_mnt_ns(struct ns_common *ns)
> return container_of(ns, struct mnt_namespace, ns);
> }
>
> +#ifdef CONFIG_FSNOTIFY
> +static inline void mnt_notify_add(struct mount *m)
> +{
> + /* Optimize the case where there are no watches */
> + if ((m->mnt_ns && m->mnt_ns->n_fsnotify_marks) ||
> + (m->prev_ns && m->prev_ns->n_fsnotify_marks))
> + list_add_tail(&m->to_notify, ¬ify_list);
> + else
> + m->prev_ns = m->mnt_ns;
> +}
> +#else
> +static inline void mnt_notify_add(struct mount *m)
> +{
> +}
> +#endif
> +
> struct mnt_namespace *mnt_ns_from_dentry(struct dentry *dentry);
> diff --git a/fs/namespace.c b/fs/namespace.c
> index d8d70da56e7b..1e964b646509 100644
> --- a/fs/namespace.c
> +++ b/fs/namespace.c
> @@ -81,6 +81,9 @@ static HLIST_HEAD(unmounted); /* protected by namespace_sem */
> static LIST_HEAD(ex_mountpoints); /* protected by namespace_sem */
> static DEFINE_SEQLOCK(mnt_ns_tree_lock);
>
> +#ifdef CONFIG_FSNOTIFY
> +LIST_HEAD(notify_list); /* protected by namespace_sem */
> +#endif
> static struct rb_root mnt_ns_tree = RB_ROOT; /* protected by mnt_ns_tree_lock */
> static LIST_HEAD(mnt_ns_list); /* protected by mnt_ns_tree_lock */
>
> @@ -163,6 +166,7 @@ static void mnt_ns_release(struct mnt_namespace *ns)
> {
> /* keep alive for {list,stat}mount() */
> if (refcount_dec_and_test(&ns->passive)) {
> + fsnotify_mntns_delete(ns);
> put_user_ns(ns->user_ns);
> kfree(ns);
> }
> @@ -1176,6 +1180,8 @@ static void mnt_add_to_ns(struct mnt_namespace *ns, struct mount *mnt)
> ns->mnt_first_node = &mnt->mnt_node;
> rb_link_node(&mnt->mnt_node, parent, link);
> rb_insert_color(&mnt->mnt_node, &ns->mounts);
> +
> + mnt_notify_add(mnt);
> }
>
> /*
> @@ -1723,6 +1729,50 @@ int may_umount(struct vfsmount *mnt)
>
> EXPORT_SYMBOL(may_umount);
>
> +#ifdef CONFIG_FSNOTIFY
> +static void mnt_notify(struct mount *p)
> +{
> + if (!p->prev_ns && p->mnt_ns) {
> + fsnotify_mnt_attach(p->mnt_ns, &p->mnt);
> + } else if (p->prev_ns && !p->mnt_ns) {
> + fsnotify_mnt_detach(p->prev_ns, &p->mnt);
> + } else if (p->prev_ns == p->mnt_ns) {
> + fsnotify_mnt_move(p->mnt_ns, &p->mnt);
> + } else {
> + fsnotify_mnt_detach(p->prev_ns, &p->mnt);
> + fsnotify_mnt_attach(p->mnt_ns, &p->mnt);
> + }
> + p->prev_ns = p->mnt_ns;
> +}
> +
> +static void notify_mnt_list(void)
> +{
> + struct mount *m, *tmp;
> + /*
> + * Notify about mounts that were added/reparented/detached/remain
> + * connected after unmount.
> + */
> + list_for_each_entry_safe(m, tmp, ¬ify_list, to_notify) {
> + mnt_notify(m);
> + list_del_init(&m->to_notify);
> + }
> +}
> +
> +static bool need_notify_mnt_list(void)
> +{
> + return !list_empty(¬ify_list);
> +}
> +#else
> +static void notify_mnt_list(void)
> +{
> +}
> +
> +static bool need_notify_mnt_list(void)
> +{
> + return false;
> +}
> +#endif
> +
> static void namespace_unlock(void)
> {
> struct hlist_head head;
> @@ -1733,7 +1783,18 @@ static void namespace_unlock(void)
> hlist_move_list(&unmounted, &head);
> list_splice_init(&ex_mountpoints, &list);
>
> - up_write(&namespace_sem);
> + if (need_notify_mnt_list()) {
> + /*
> + * No point blocking out concurrent readers while notifications
> + * are sent. This will also allow statmount()/listmount() to run
> + * concurrently.
> + */
> + downgrade_write(&namespace_sem);
> + notify_mnt_list();
> + up_read(&namespace_sem);
> + } else {
> + up_write(&namespace_sem);
> + }
>
> shrink_dentry_list(&list);
>
> @@ -1846,6 +1907,19 @@ static void umount_tree(struct mount *mnt, enum umount_tree_flags how)
> change_mnt_propagation(p, MS_PRIVATE);
> if (disconnect)
> hlist_add_head(&p->mnt_umount, &unmounted);
> +
> + /*
> + * At this point p->mnt_ns is NULL, notification will be queued
> + * only if
> + *
> + * - p->prev_ns is non-NULL *and*
> + * - p->prev_ns->n_fsnotify_marks is non-NULL
> + *
> + * This will preclude queuing the mount if this is a cleanup
> + * after a failed copy_tree() or destruction of an anonymous
> + * namespace, etc.
> + */
> + mnt_notify_add(p);
> }
> }
>
> @@ -2555,6 +2629,7 @@ static int attach_recursive_mnt(struct mount *source_mnt,
> dest_mp = smp;
> unhash_mnt(source_mnt);
> attach_mnt(source_mnt, top_mnt, dest_mp, beneath);
> + mnt_notify_add(source_mnt);
> touch_mnt_namespace(source_mnt->mnt_ns);
> } else {
> if (source_mnt->mnt_ns) {
> @@ -4476,6 +4551,8 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
> list_del_init(&new_mnt->mnt_expire);
> put_mountpoint(root_mp);
> unlock_mount_hash();
> + mnt_notify_add(root_mnt);
> + mnt_notify_add(new_mnt);
> chroot_fs_refs(&root, &new);
> error = 0;
> out4:
> diff --git a/fs/pnode.c b/fs/pnode.c
> index ef048f008bdd..82d809c785ec 100644
> --- a/fs/pnode.c
> +++ b/fs/pnode.c
> @@ -549,8 +549,10 @@ static void restore_mounts(struct list_head *to_restore)
> mp = parent->mnt_mp;
> parent = parent->mnt_parent;
> }
> - if (parent != mnt->mnt_parent)
> + if (parent != mnt->mnt_parent) {
> mnt_change_mountpoint(parent, mp, mnt);
> + mnt_notify_add(mnt);
> + }
> }
> }
>
> --
> 2.48.1
>
--
Jan Kara <jack at suse.com>
SUSE Labs, CR
More information about the Linux-security-module-archive
mailing list