mirror of
https://github.com/torvalds/linux.git
synced 2026-11-09 13:44:03 +01:00
vfs: add notifications for mount attach and detach
Add notifications for attaching and detaching mounts to fs/namespace.c Signed-off-by: Miklos Szeredi <mszeredi@redhat.com> Link: https://lore.kernel.org/r/20250129165803.72138-4-mszeredi@redhat.com Signed-off-by: Christian Brauner <brauner@kernel.org>
This commit is contained in:
parent
0f46d81f2b
commit
bf630c4016
20
fs/mount.h
20
fs/mount.h
|
|
@ -5,6 +5,8 @@
|
||||||
#include <linux/ns_common.h>
|
#include <linux/ns_common.h>
|
||||||
#include <linux/fs_pin.h>
|
#include <linux/fs_pin.h>
|
||||||
|
|
||||||
|
extern struct list_head notify_list;
|
||||||
|
|
||||||
struct mnt_namespace {
|
struct mnt_namespace {
|
||||||
struct ns_common ns;
|
struct ns_common ns;
|
||||||
struct mount * root;
|
struct mount * root;
|
||||||
|
|
@ -80,6 +82,8 @@ struct mount {
|
||||||
#ifdef CONFIG_FSNOTIFY
|
#ifdef CONFIG_FSNOTIFY
|
||||||
struct fsnotify_mark_connector __rcu *mnt_fsnotify_marks;
|
struct fsnotify_mark_connector __rcu *mnt_fsnotify_marks;
|
||||||
__u32 mnt_fsnotify_mask;
|
__u32 mnt_fsnotify_mask;
|
||||||
|
struct list_head to_notify; /* need to queue notification */
|
||||||
|
struct mnt_namespace *prev_ns; /* previous namespace (NULL if none) */
|
||||||
#endif
|
#endif
|
||||||
int mnt_id; /* mount identifier, reused */
|
int mnt_id; /* mount identifier, reused */
|
||||||
u64 mnt_id_unique; /* mount ID unique until reboot */
|
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);
|
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);
|
struct mnt_namespace *mnt_ns_from_dentry(struct dentry *dentry);
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,9 @@ static HLIST_HEAD(unmounted); /* protected by namespace_sem */
|
||||||
static LIST_HEAD(ex_mountpoints); /* protected by namespace_sem */
|
static LIST_HEAD(ex_mountpoints); /* protected by namespace_sem */
|
||||||
static DEFINE_SEQLOCK(mnt_ns_tree_lock);
|
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 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 */
|
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() */
|
/* keep alive for {list,stat}mount() */
|
||||||
if (refcount_dec_and_test(&ns->passive)) {
|
if (refcount_dec_and_test(&ns->passive)) {
|
||||||
|
fsnotify_mntns_delete(ns);
|
||||||
put_user_ns(ns->user_ns);
|
put_user_ns(ns->user_ns);
|
||||||
kfree(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;
|
ns->mnt_first_node = &mnt->mnt_node;
|
||||||
rb_link_node(&mnt->mnt_node, parent, link);
|
rb_link_node(&mnt->mnt_node, parent, link);
|
||||||
rb_insert_color(&mnt->mnt_node, &ns->mounts);
|
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);
|
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)
|
static void namespace_unlock(void)
|
||||||
{
|
{
|
||||||
struct hlist_head head;
|
struct hlist_head head;
|
||||||
|
|
@ -1733,7 +1783,18 @@ static void namespace_unlock(void)
|
||||||
hlist_move_list(&unmounted, &head);
|
hlist_move_list(&unmounted, &head);
|
||||||
list_splice_init(&ex_mountpoints, &list);
|
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);
|
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);
|
change_mnt_propagation(p, MS_PRIVATE);
|
||||||
if (disconnect)
|
if (disconnect)
|
||||||
hlist_add_head(&p->mnt_umount, &unmounted);
|
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;
|
dest_mp = smp;
|
||||||
unhash_mnt(source_mnt);
|
unhash_mnt(source_mnt);
|
||||||
attach_mnt(source_mnt, top_mnt, dest_mp, beneath);
|
attach_mnt(source_mnt, top_mnt, dest_mp, beneath);
|
||||||
|
mnt_notify_add(source_mnt);
|
||||||
touch_mnt_namespace(source_mnt->mnt_ns);
|
touch_mnt_namespace(source_mnt->mnt_ns);
|
||||||
} else {
|
} else {
|
||||||
if (source_mnt->mnt_ns) {
|
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);
|
list_del_init(&new_mnt->mnt_expire);
|
||||||
put_mountpoint(root_mp);
|
put_mountpoint(root_mp);
|
||||||
unlock_mount_hash();
|
unlock_mount_hash();
|
||||||
|
mnt_notify_add(root_mnt);
|
||||||
|
mnt_notify_add(new_mnt);
|
||||||
chroot_fs_refs(&root, &new);
|
chroot_fs_refs(&root, &new);
|
||||||
error = 0;
|
error = 0;
|
||||||
out4:
|
out4:
|
||||||
|
|
|
||||||
|
|
@ -549,8 +549,10 @@ static void restore_mounts(struct list_head *to_restore)
|
||||||
mp = parent->mnt_mp;
|
mp = parent->mnt_mp;
|
||||||
parent = parent->mnt_parent;
|
parent = parent->mnt_parent;
|
||||||
}
|
}
|
||||||
if (parent != mnt->mnt_parent)
|
if (parent != mnt->mnt_parent) {
|
||||||
mnt_change_mountpoint(parent, mp, mnt);
|
mnt_change_mountpoint(parent, mp, mnt);
|
||||||
|
mnt_notify_add(mnt);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user