diff options
-rw-r--r-- | fs/dcache.c | 33 | ||||
-rw-r--r-- | fs/internal.h | 1 | ||||
-rw-r--r-- | fs/namespace.c | 11 |
3 files changed, 39 insertions, 6 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) | |||
1184 | EXPORT_SYMBOL(have_submounts); | 1184 | EXPORT_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 | */ | ||
1194 | int 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); | ||
1214 | out: | ||
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 |
diff --git a/fs/internal.h b/fs/internal.h index 7c5f01cf619d..d20893795526 100644 --- a/fs/internal.h +++ b/fs/internal.h | |||
@@ -126,6 +126,7 @@ extern int invalidate_inodes(struct super_block *, bool); | |||
126 | * dcache.c | 126 | * dcache.c |
127 | */ | 127 | */ |
128 | extern struct dentry *__d_alloc(struct super_block *, const struct qstr *); | 128 | extern struct dentry *__d_alloc(struct super_block *, const struct qstr *); |
129 | extern int d_set_mounted(struct dentry *dentry); | ||
129 | 130 | ||
130 | /* | 131 | /* |
131 | * read_write.c | 132 | * read_write.c |
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); |