aboutsummaryrefslogtreecommitdiffstats
path: root/fs/dcache.c
diff options
context:
space:
mode:
authorMiklos Szeredi <miklos@szeredi.hu>2013-09-05 08:39:11 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2013-09-05 16:23:50 -0400
commiteed810076685c77dc9a8c5c3593e641c93caed1c (patch)
tree5aba507073b0a7e29dfa0739272dfeda16eb5e29 /fs/dcache.c
parent848ac114e847af3f1f9141c90a39ebe79bdb13b3 (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/dcache.c')
-rw-r--r--fs/dcache.c33
1 files changed, 33 insertions, 0 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index ce5a7e6d84cd..761e31bacbc2 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -1184,6 +1184,39 @@ int have_submounts(struct dentry *parent)
1184EXPORT_SYMBOL(have_submounts); 1184EXPORT_SYMBOL(have_submounts);
1185 1185
1186/* 1186/*
1187 * Called by mount code to set a mountpoint and check if the mountpoint is
1188 * reachable (e.g. NFS can unhash a directory dentry and then the complete
1189 * subtree can become unreachable).
1190 *
1191 * Only one of check_submounts_and_drop() and d_set_mounted() must succeed. For
1192 * this reason take rename_lock and d_lock on dentry and ancestors.
1193 */
1194int d_set_mounted(struct dentry *dentry)
1195{
1196 struct dentry *p;
1197 int ret = -ENOENT;
1198 write_seqlock(&rename_lock);
1199 for (p = dentry->d_parent; !IS_ROOT(p); p = p->d_parent) {
1200 /* Need exclusion wrt. check_submounts_and_drop() */
1201 spin_lock(&p->d_lock);
1202 if (unlikely(d_unhashed(p))) {
1203 spin_unlock(&p->d_lock);
1204 goto out;
1205 }
1206 spin_unlock(&p->d_lock);
1207 }
1208 spin_lock(&dentry->d_lock);
1209 if (!d_unlinked(dentry)) {
1210 dentry->d_flags |= DCACHE_MOUNTED;
1211 ret = 0;
1212 }
1213 spin_unlock(&dentry->d_lock);
1214out:
1215 write_sequnlock(&rename_lock);
1216 return ret;
1217}
1218
1219/*
1187 * Search the dentry child list of the specified parent, 1220 * Search the dentry child list of the specified parent,
1188 * and move any unused dentries to the end of the unused 1221 * and move any unused dentries to the end of the unused
1189 * list for prune_dcache(). We descend to the next level 1222 * list for prune_dcache(). We descend to the next level