|
@@ -742,26 +742,50 @@ static struct mountpoint *lookup_mountpoint(struct dentry *dentry)
|
|
return NULL;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
-static struct mountpoint *new_mountpoint(struct dentry *dentry)
|
|
|
|
|
|
+static struct mountpoint *get_mountpoint(struct dentry *dentry)
|
|
{
|
|
{
|
|
- struct hlist_head *chain = mp_hash(dentry);
|
|
|
|
- struct mountpoint *mp;
|
|
|
|
|
|
+ struct mountpoint *mp, *new = NULL;
|
|
int ret;
|
|
int ret;
|
|
|
|
|
|
- mp = kmalloc(sizeof(struct mountpoint), GFP_KERNEL);
|
|
|
|
- if (!mp)
|
|
|
|
|
|
+ if (d_mountpoint(dentry)) {
|
|
|
|
+mountpoint:
|
|
|
|
+ read_seqlock_excl(&mount_lock);
|
|
|
|
+ mp = lookup_mountpoint(dentry);
|
|
|
|
+ read_sequnlock_excl(&mount_lock);
|
|
|
|
+ if (mp)
|
|
|
|
+ goto done;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!new)
|
|
|
|
+ new = kmalloc(sizeof(struct mountpoint), GFP_KERNEL);
|
|
|
|
+ if (!new)
|
|
return ERR_PTR(-ENOMEM);
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
+
|
|
|
|
+ /* Exactly one processes may set d_mounted */
|
|
ret = d_set_mounted(dentry);
|
|
ret = d_set_mounted(dentry);
|
|
- if (ret) {
|
|
|
|
- kfree(mp);
|
|
|
|
- return ERR_PTR(ret);
|
|
|
|
- }
|
|
|
|
|
|
|
|
- mp->m_dentry = dentry;
|
|
|
|
- mp->m_count = 1;
|
|
|
|
- hlist_add_head(&mp->m_hash, chain);
|
|
|
|
- INIT_HLIST_HEAD(&mp->m_list);
|
|
|
|
|
|
+ /* Someone else set d_mounted? */
|
|
|
|
+ if (ret == -EBUSY)
|
|
|
|
+ goto mountpoint;
|
|
|
|
+
|
|
|
|
+ /* The dentry is not available as a mountpoint? */
|
|
|
|
+ mp = ERR_PTR(ret);
|
|
|
|
+ if (ret)
|
|
|
|
+ goto done;
|
|
|
|
+
|
|
|
|
+ /* Add the new mountpoint to the hash table */
|
|
|
|
+ read_seqlock_excl(&mount_lock);
|
|
|
|
+ new->m_dentry = dentry;
|
|
|
|
+ new->m_count = 1;
|
|
|
|
+ hlist_add_head(&new->m_hash, mp_hash(dentry));
|
|
|
|
+ INIT_HLIST_HEAD(&new->m_list);
|
|
|
|
+ read_sequnlock_excl(&mount_lock);
|
|
|
|
+
|
|
|
|
+ mp = new;
|
|
|
|
+ new = NULL;
|
|
|
|
+done:
|
|
|
|
+ kfree(new);
|
|
return mp;
|
|
return mp;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1595,11 +1619,11 @@ void __detach_mounts(struct dentry *dentry)
|
|
struct mount *mnt;
|
|
struct mount *mnt;
|
|
|
|
|
|
namespace_lock();
|
|
namespace_lock();
|
|
|
|
+ lock_mount_hash();
|
|
mp = lookup_mountpoint(dentry);
|
|
mp = lookup_mountpoint(dentry);
|
|
if (IS_ERR_OR_NULL(mp))
|
|
if (IS_ERR_OR_NULL(mp))
|
|
goto out_unlock;
|
|
goto out_unlock;
|
|
|
|
|
|
- lock_mount_hash();
|
|
|
|
event++;
|
|
event++;
|
|
while (!hlist_empty(&mp->m_list)) {
|
|
while (!hlist_empty(&mp->m_list)) {
|
|
mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list);
|
|
mnt = hlist_entry(mp->m_list.first, struct mount, mnt_mp_list);
|
|
@@ -1609,9 +1633,9 @@ void __detach_mounts(struct dentry *dentry)
|
|
}
|
|
}
|
|
else umount_tree(mnt, UMOUNT_CONNECTED);
|
|
else umount_tree(mnt, UMOUNT_CONNECTED);
|
|
}
|
|
}
|
|
- unlock_mount_hash();
|
|
|
|
put_mountpoint(mp);
|
|
put_mountpoint(mp);
|
|
out_unlock:
|
|
out_unlock:
|
|
|
|
+ unlock_mount_hash();
|
|
namespace_unlock();
|
|
namespace_unlock();
|
|
}
|
|
}
|
|
|
|
|
|
@@ -2038,9 +2062,7 @@ retry:
|
|
namespace_lock();
|
|
namespace_lock();
|
|
mnt = lookup_mnt(path);
|
|
mnt = lookup_mnt(path);
|
|
if (likely(!mnt)) {
|
|
if (likely(!mnt)) {
|
|
- struct mountpoint *mp = lookup_mountpoint(dentry);
|
|
|
|
- if (!mp)
|
|
|
|
- mp = new_mountpoint(dentry);
|
|
|
|
|
|
+ struct mountpoint *mp = get_mountpoint(dentry);
|
|
if (IS_ERR(mp)) {
|
|
if (IS_ERR(mp)) {
|
|
namespace_unlock();
|
|
namespace_unlock();
|
|
inode_unlock(dentry->d_inode);
|
|
inode_unlock(dentry->d_inode);
|
|
@@ -2059,7 +2081,11 @@ retry:
|
|
static void unlock_mount(struct mountpoint *where)
|
|
static void unlock_mount(struct mountpoint *where)
|
|
{
|
|
{
|
|
struct dentry *dentry = where->m_dentry;
|
|
struct dentry *dentry = where->m_dentry;
|
|
|
|
+
|
|
|
|
+ read_seqlock_excl(&mount_lock);
|
|
put_mountpoint(where);
|
|
put_mountpoint(where);
|
|
|
|
+ read_sequnlock_excl(&mount_lock);
|
|
|
|
+
|
|
namespace_unlock();
|
|
namespace_unlock();
|
|
inode_unlock(dentry->d_inode);
|
|
inode_unlock(dentry->d_inode);
|
|
}
|
|
}
|
|
@@ -3135,9 +3161,9 @@ SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
|
|
touch_mnt_namespace(current->nsproxy->mnt_ns);
|
|
touch_mnt_namespace(current->nsproxy->mnt_ns);
|
|
/* A moved mount should not expire automatically */
|
|
/* A moved mount should not expire automatically */
|
|
list_del_init(&new_mnt->mnt_expire);
|
|
list_del_init(&new_mnt->mnt_expire);
|
|
|
|
+ put_mountpoint(root_mp);
|
|
unlock_mount_hash();
|
|
unlock_mount_hash();
|
|
chroot_fs_refs(&root, &new);
|
|
chroot_fs_refs(&root, &new);
|
|
- put_mountpoint(root_mp);
|
|
|
|
error = 0;
|
|
error = 0;
|
|
out4:
|
|
out4:
|
|
unlock_mount(old_mp);
|
|
unlock_mount(old_mp);
|