diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2013-09-05 08:39:11 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-09-05 16:23:50 -0400 |
commit | eed810076685c77dc9a8c5c3593e641c93caed1c (patch) | |
tree | 5aba507073b0a7e29dfa0739272dfeda16eb5e29 /fs/namespace.c | |
parent | 848ac114e847af3f1f9141c90a39ebe79bdb13b3 (diff) |
vfs: check unlinked ancestors before mount
We check submounts before doing d_drop() on a non-empty directory dentry in
NFS (have_submounts()), but we do not exclude a racing mount. Nor do we
prevent mounts to be added to the disconnected subtree using relative paths
after the d_drop().
This patch fixes these issues by checking for unlinked (unhashed, non-root)
ancestors before proceeding with the mount. This is done with rename
seqlock taken for write and with ->d_lock grabbed on each ancestor in turn,
including our dentry itself. This ensures that the only one of
check_submounts_and_drop() or has_unlinked_ancestor() can succeed.
Signed-off-by: Miklos Szeredi <miklos@szeredi.hu>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/namespace.c')
-rw-r--r-- | fs/namespace.c | 11 |
1 files changed, 5 insertions, 6 deletions
diff --git a/fs/namespace.c b/fs/namespace.c index ad8ea9bc2518..5997887cc64a 100644 --- a/fs/namespace.c +++ b/fs/namespace.c | |||
@@ -611,6 +611,7 @@ static struct mountpoint *new_mountpoint(struct dentry *dentry) | |||
611 | { | 611 | { |
612 | struct list_head *chain = mountpoint_hashtable + hash(NULL, dentry); | 612 | struct list_head *chain = mountpoint_hashtable + hash(NULL, dentry); |
613 | struct mountpoint *mp; | 613 | struct mountpoint *mp; |
614 | int ret; | ||
614 | 615 | ||
615 | list_for_each_entry(mp, chain, m_hash) { | 616 | list_for_each_entry(mp, chain, m_hash) { |
616 | if (mp->m_dentry == dentry) { | 617 | if (mp->m_dentry == dentry) { |
@@ -626,14 +627,12 @@ static struct mountpoint *new_mountpoint(struct dentry *dentry) | |||
626 | if (!mp) | 627 | if (!mp) |
627 | return ERR_PTR(-ENOMEM); | 628 | return ERR_PTR(-ENOMEM); |
628 | 629 | ||
629 | spin_lock(&dentry->d_lock); | 630 | ret = d_set_mounted(dentry); |
630 | if (d_unlinked(dentry)) { | 631 | if (ret) { |
631 | spin_unlock(&dentry->d_lock); | ||
632 | kfree(mp); | 632 | kfree(mp); |
633 | return ERR_PTR(-ENOENT); | 633 | return ERR_PTR(ret); |
634 | } | 634 | } |
635 | dentry->d_flags |= DCACHE_MOUNTED; | 635 | |
636 | spin_unlock(&dentry->d_lock); | ||
637 | mp->m_dentry = dentry; | 636 | mp->m_dentry = dentry; |
638 | mp->m_count = 1; | 637 | mp->m_count = 1; |
639 | list_add(&mp->m_hash, chain); | 638 | list_add(&mp->m_hash, chain); |