mirror of
https://github.com/tbsdtv/linux_media.git
synced 2025-07-23 04:33:26 +02:00
switch mnt_hash to hlist
fixes RCU bug - walking through hlist is safe in face of element moves, since it's self-terminating. Cyclic lists are not - if we end up jumping to another hash chain, we'll loop infinitely without ever hitting the original list head. [fix for dumb braino folded] Spotted by: Max Kellermann <mk@cm4all.com> Cc: stable@vger.kernel.org Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
26
fs/pnode.c
26
fs/pnode.c
@@ -220,14 +220,14 @@ static struct mount *get_source(struct mount *dest,
|
||||
* @tree_list : list of heads of trees to be attached.
|
||||
*/
|
||||
int propagate_mnt(struct mount *dest_mnt, struct mountpoint *dest_mp,
|
||||
struct mount *source_mnt, struct list_head *tree_list)
|
||||
struct mount *source_mnt, struct hlist_head *tree_list)
|
||||
{
|
||||
struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns;
|
||||
struct mount *m, *child;
|
||||
int ret = 0;
|
||||
struct mount *prev_dest_mnt = dest_mnt;
|
||||
struct mount *prev_src_mnt = source_mnt;
|
||||
LIST_HEAD(tmp_list);
|
||||
HLIST_HEAD(tmp_list);
|
||||
|
||||
for (m = propagation_next(dest_mnt, dest_mnt); m;
|
||||
m = propagation_next(m, dest_mnt)) {
|
||||
@@ -246,27 +246,29 @@ int propagate_mnt(struct mount *dest_mnt, struct mountpoint *dest_mp,
|
||||
child = copy_tree(source, source->mnt.mnt_root, type);
|
||||
if (IS_ERR(child)) {
|
||||
ret = PTR_ERR(child);
|
||||
list_splice(tree_list, tmp_list.prev);
|
||||
tmp_list = *tree_list;
|
||||
tmp_list.first->pprev = &tmp_list.first;
|
||||
INIT_HLIST_HEAD(tree_list);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (is_subdir(dest_mp->m_dentry, m->mnt.mnt_root)) {
|
||||
mnt_set_mountpoint(m, dest_mp, child);
|
||||
list_add_tail(&child->mnt_hash, tree_list);
|
||||
hlist_add_head(&child->mnt_hash, tree_list);
|
||||
} else {
|
||||
/*
|
||||
* This can happen if the parent mount was bind mounted
|
||||
* on some subdirectory of a shared/slave mount.
|
||||
*/
|
||||
list_add_tail(&child->mnt_hash, &tmp_list);
|
||||
hlist_add_head(&child->mnt_hash, &tmp_list);
|
||||
}
|
||||
prev_dest_mnt = m;
|
||||
prev_src_mnt = child;
|
||||
}
|
||||
out:
|
||||
lock_mount_hash();
|
||||
while (!list_empty(&tmp_list)) {
|
||||
child = list_first_entry(&tmp_list, struct mount, mnt_hash);
|
||||
while (!hlist_empty(&tmp_list)) {
|
||||
child = hlist_entry(tmp_list.first, struct mount, mnt_hash);
|
||||
umount_tree(child, 0);
|
||||
}
|
||||
unlock_mount_hash();
|
||||
@@ -338,8 +340,10 @@ static void __propagate_umount(struct mount *mnt)
|
||||
* umount the child only if the child has no
|
||||
* other children
|
||||
*/
|
||||
if (child && list_empty(&child->mnt_mounts))
|
||||
list_move_tail(&child->mnt_hash, &mnt->mnt_hash);
|
||||
if (child && list_empty(&child->mnt_mounts)) {
|
||||
hlist_del_init_rcu(&child->mnt_hash);
|
||||
hlist_add_before_rcu(&child->mnt_hash, &mnt->mnt_hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,11 +354,11 @@ static void __propagate_umount(struct mount *mnt)
|
||||
*
|
||||
* vfsmount lock must be held for write
|
||||
*/
|
||||
int propagate_umount(struct list_head *list)
|
||||
int propagate_umount(struct hlist_head *list)
|
||||
{
|
||||
struct mount *mnt;
|
||||
|
||||
list_for_each_entry(mnt, list, mnt_hash)
|
||||
hlist_for_each_entry(mnt, list, mnt_hash)
|
||||
__propagate_umount(mnt);
|
||||
return 0;
|
||||
}
|
||||
|
Reference in New Issue
Block a user