out_bad:
/* don't unhash if we have submounts */
- if (check_submounts_and_drop(dentry) != 0)
- goto out_skip;
+ shrink_submounts_and_drop(dentry);
_debug("dropping dentry %s/%s",
parent->d_name.name, dentry->d_name.name);
int ret = -ENOENT;
write_seqlock(&rename_lock);
for (p = dentry->d_parent; !IS_ROOT(p); p = p->d_parent) {
- /* Need exclusion wrt. check_submounts_and_drop() */
+ /* Need exclusion wrt. shrink_submounts_and_drop() */
spin_lock(&p->d_lock);
if (unlikely(d_unhashed(p))) {
spin_unlock(&p->d_lock);
}
EXPORT_SYMBOL(shrink_dcache_parent);
-static enum d_walk_ret check_and_collect(void *_data, struct dentry *dentry)
+struct detach_data {
+ struct dentry *found;
+};
+static enum d_walk_ret do_detach_submounts(void *ptr, struct dentry *dentry)
{
- struct select_data *data = _data;
-
- if (d_mountpoint(dentry)) {
- data->found = -EBUSY;
- return D_WALK_QUIT;
- }
-
- return select_collect(_data, dentry);
-}
+ struct detach_data *data = ptr;
-static void check_and_drop(void *_data)
-{
- struct select_data *data = _data;
+ if (d_mountpoint(dentry))
+ data->found = dentry;
- if (d_mountpoint(data->start))
- data->found = -EBUSY;
- if (!data->found)
- __d_drop(data->start);
+ return data->found ? D_WALK_QUIT : D_WALK_CONTINUE;
}
/**
- * check_submounts_and_drop - prune dcache, check for submounts and drop
+ * detach_submounts - check for submounts and detach them.
*
- * All done as a single atomic operation relative to has_unlinked_ancestor().
- * Returns 0 if successfully unhashed @parent. If there were submounts then
- * return -EBUSY.
+ * @dentry: dentry to find mount points under.
*
- * @dentry: dentry to prune and drop
+ * If dentry or any of it's children is a mount point detach those mounts.
*/
-int check_submounts_and_drop(struct dentry *dentry)
+void detach_submounts(struct dentry *dentry)
{
- int ret = 0;
-
- /* Negative dentries can be dropped without further checks */
- if (!dentry->d_inode) {
- d_drop(dentry);
- goto out;
- }
-
+ struct detach_data data;
for (;;) {
- struct select_data data;
-
- INIT_LIST_HEAD(&data.dispose);
- data.start = dentry;
- data.found = 0;
+ data.found = NULL;
+ d_walk(dentry, &data, do_detach_submounts, NULL);
- d_walk(dentry, &data, check_and_collect, check_and_drop);
- ret = data.found;
-
- if (!list_empty(&data.dispose))
- shrink_dentry_list(&data.dispose);
-
- if (ret <= 0)
+ if (!data.found)
break;
+ detach_mounts(data.found);
cond_resched();
}
+ detach_mounts(dentry);
+}
-out:
- return ret;
+/**
+ * shrink_submounts_and_drop - detach submounts, prune dcache, and drop
+ *
+ * All done as a single atomic operation reletaive to d_set_mounted().
+ *
+ * @dentry: dentry to detach, prune and drop
+ */
+void shrink_submounts_and_drop(struct dentry *dentry)
+{
+ d_drop(dentry);
+ detach_submounts(dentry);
+ shrink_dcache_parent(dentry);
}
-EXPORT_SYMBOL(check_submounts_and_drop);
+EXPORT_SYMBOL(shrink_submounts_and_drop);
/**
* __d_alloc - allocate a dcache entry
invalid:
ret = 0;
- if (check_submounts_and_drop(entry) != 0)
- ret = 1;
+ shrink_submounts_and_drop(entry);
goto out;
}
if (!had_lock)
gfs2_glock_dq_uninit(&d_gh);
invalid:
- if (check_submounts_and_drop(dentry) != 0)
- goto valid;
-
+ shrink_submounts_and_drop(dentry);
dput(parent);
return 0;
dget(dentry);
mutex_lock(&dentry->d_inode->i_mutex);
- error = -EBUSY;
- if (d_mountpoint(dentry))
- goto out;
-
error = security_inode_rmdir(dir, dentry);
if (error)
goto out;
dentry->d_inode->i_flags |= S_DEAD;
dont_mount(dentry);
+ detach_mounts(dentry);
out:
mutex_unlock(&dentry->d_inode->i_mutex);
return -EPERM;
mutex_lock(&dentry->d_inode->i_mutex);
- if (d_mountpoint(dentry))
- error = -EBUSY;
- else {
- error = security_inode_unlink(dir, dentry);
+ error = security_inode_unlink(dir, dentry);
+ if (!error) {
+ error = dir->i_op->unlink(dir, dentry);
if (!error) {
- error = dir->i_op->unlink(dir, dentry);
- if (!error)
- dont_mount(dentry);
+ dont_mount(dentry);
+ detach_mounts(dentry);
}
}
mutex_unlock(&dentry->d_inode->i_mutex);
if (target)
mutex_lock(&target->i_mutex);
- error = -EBUSY;
- if (d_mountpoint(old_dentry) || d_mountpoint(new_dentry))
- goto out;
-
error = -EMLINK;
if (max_links && !target && new_dir != old_dir &&
new_dir->i_nlink >= max_links)
if (target) {
target->i_flags |= S_DEAD;
dont_mount(new_dentry);
+ detach_mounts(new_dentry);
}
out:
if (target)
if (target)
mutex_lock(&target->i_mutex);
- error = -EBUSY;
- if (d_mountpoint(old_dentry)||d_mountpoint(new_dentry))
- goto out;
-
error = old_dir->i_op->rename(old_dir, old_dentry, new_dir, new_dentry);
if (error)
goto out;
- if (target)
+ if (target) {
dont_mount(new_dentry);
+ detach_mounts(new_dentry);
+ }
if (!(old_dir->i_sb->s_type->fs_flags & FS_RENAME_DOES_D_MOVE))
d_move(old_dentry, new_dentry);
out:
if (dentry->d_flags & DCACHE_DISCONNECTED)
goto out_valid;
}
- /* If we have submounts, don't unhash ! */
- if (check_submounts_and_drop(dentry) != 0)
- goto out_valid;
-
+ shrink_submounts_and_drop(dentry);
dput(parent);
dfprintk(LOOKUPCACHE, "NFS: %s(%s/%s) is invalid\n",
__func__, dentry->d_parent->d_name.name,
}
mutex_unlock(&sysfs_mutex);
-out_valid:
return 1;
out_bad:
/* Remove the dentry from the dcache hashes.
* to the dcache hashes.
*/
mutex_unlock(&sysfs_mutex);
-
- /* If we have submounts we must allow the vfs caches
- * to lie about the state of the filesystem to prevent
- * leaks and other nasty things.
- */
- if (check_submounts_and_drop(dentry) != 0)
- goto out_valid;
+ shrink_submounts_and_drop(dentry);
return 0;
}
/* test whether we have any submounts in a subdir tree */
extern int have_submounts(struct dentry *);
-extern int check_submounts_and_drop(struct dentry *);
+extern void detach_submounts(struct dentry *dentry);
+extern void shrink_submounts_and_drop(struct dentry *);
/*
* This adds the entry to the hash queues.