xref: /openbmc/linux/fs/afs/dynroot.c (revision 92e3cc91)
1b4d0d230SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20da0b7fdSDavid Howells /* AFS dynamic root handling
366c7e1d3SDavid Howells  *
466c7e1d3SDavid Howells  * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
566c7e1d3SDavid Howells  * Written by David Howells (dhowells@redhat.com)
666c7e1d3SDavid Howells  */
766c7e1d3SDavid Howells 
866c7e1d3SDavid Howells #include <linux/fs.h>
966c7e1d3SDavid Howells #include <linux/namei.h>
1066c7e1d3SDavid Howells #include <linux/dns_resolver.h>
1166c7e1d3SDavid Howells #include "internal.h"
1266c7e1d3SDavid Howells 
13e49c7b2fSDavid Howells static atomic_t afs_autocell_ino;
14e49c7b2fSDavid Howells 
15e49c7b2fSDavid Howells /*
16e49c7b2fSDavid Howells  * iget5() comparator for inode created by autocell operations
17e49c7b2fSDavid Howells  *
18e49c7b2fSDavid Howells  * These pseudo inodes don't match anything.
19e49c7b2fSDavid Howells  */
20e49c7b2fSDavid Howells static int afs_iget5_pseudo_test(struct inode *inode, void *opaque)
21e49c7b2fSDavid Howells {
22e49c7b2fSDavid Howells 	return 0;
23e49c7b2fSDavid Howells }
24e49c7b2fSDavid Howells 
25e49c7b2fSDavid Howells /*
26e49c7b2fSDavid Howells  * iget5() inode initialiser
27e49c7b2fSDavid Howells  */
28e49c7b2fSDavid Howells static int afs_iget5_pseudo_set(struct inode *inode, void *opaque)
29e49c7b2fSDavid Howells {
30e49c7b2fSDavid Howells 	struct afs_super_info *as = AFS_FS_S(inode->i_sb);
31e49c7b2fSDavid Howells 	struct afs_vnode *vnode = AFS_FS_I(inode);
32e49c7b2fSDavid Howells 	struct afs_fid *fid = opaque;
33e49c7b2fSDavid Howells 
34e49c7b2fSDavid Howells 	vnode->volume		= as->volume;
35e49c7b2fSDavid Howells 	vnode->fid		= *fid;
36e49c7b2fSDavid Howells 	inode->i_ino		= fid->vnode;
37e49c7b2fSDavid Howells 	inode->i_generation	= fid->unique;
38e49c7b2fSDavid Howells 	return 0;
39e49c7b2fSDavid Howells }
40e49c7b2fSDavid Howells 
41e49c7b2fSDavid Howells /*
42e49c7b2fSDavid Howells  * Create an inode for a dynamic root directory or an autocell dynamic
43e49c7b2fSDavid Howells  * automount dir.
44e49c7b2fSDavid Howells  */
45e49c7b2fSDavid Howells struct inode *afs_iget_pseudo_dir(struct super_block *sb, bool root)
46e49c7b2fSDavid Howells {
47e49c7b2fSDavid Howells 	struct afs_super_info *as = AFS_FS_S(sb);
48e49c7b2fSDavid Howells 	struct afs_vnode *vnode;
49e49c7b2fSDavid Howells 	struct inode *inode;
50e49c7b2fSDavid Howells 	struct afs_fid fid = {};
51e49c7b2fSDavid Howells 
52e49c7b2fSDavid Howells 	_enter("");
53e49c7b2fSDavid Howells 
54e49c7b2fSDavid Howells 	if (as->volume)
55e49c7b2fSDavid Howells 		fid.vid = as->volume->vid;
56e49c7b2fSDavid Howells 	if (root) {
57e49c7b2fSDavid Howells 		fid.vnode = 1;
58e49c7b2fSDavid Howells 		fid.unique = 1;
59e49c7b2fSDavid Howells 	} else {
60e49c7b2fSDavid Howells 		fid.vnode = atomic_inc_return(&afs_autocell_ino);
61e49c7b2fSDavid Howells 		fid.unique = 0;
62e49c7b2fSDavid Howells 	}
63e49c7b2fSDavid Howells 
64e49c7b2fSDavid Howells 	inode = iget5_locked(sb, fid.vnode,
65e49c7b2fSDavid Howells 			     afs_iget5_pseudo_test, afs_iget5_pseudo_set, &fid);
66e49c7b2fSDavid Howells 	if (!inode) {
67e49c7b2fSDavid Howells 		_leave(" = -ENOMEM");
68e49c7b2fSDavid Howells 		return ERR_PTR(-ENOMEM);
69e49c7b2fSDavid Howells 	}
70e49c7b2fSDavid Howells 
71e49c7b2fSDavid Howells 	_debug("GOT INODE %p { ino=%lu, vl=%llx, vn=%llx, u=%x }",
72e49c7b2fSDavid Howells 	       inode, inode->i_ino, fid.vid, fid.vnode, fid.unique);
73e49c7b2fSDavid Howells 
74e49c7b2fSDavid Howells 	vnode = AFS_FS_I(inode);
75e49c7b2fSDavid Howells 
76e49c7b2fSDavid Howells 	/* there shouldn't be an existing inode */
77e49c7b2fSDavid Howells 	BUG_ON(!(inode->i_state & I_NEW));
78e49c7b2fSDavid Howells 
79e49c7b2fSDavid Howells 	inode->i_size		= 0;
80e49c7b2fSDavid Howells 	inode->i_mode		= S_IFDIR | S_IRUGO | S_IXUGO;
81e49c7b2fSDavid Howells 	if (root) {
82e49c7b2fSDavid Howells 		inode->i_op	= &afs_dynroot_inode_operations;
83e49c7b2fSDavid Howells 		inode->i_fop	= &simple_dir_operations;
84e49c7b2fSDavid Howells 	} else {
85e49c7b2fSDavid Howells 		inode->i_op	= &afs_autocell_inode_operations;
86e49c7b2fSDavid Howells 	}
87e49c7b2fSDavid Howells 	set_nlink(inode, 2);
88e49c7b2fSDavid Howells 	inode->i_uid		= GLOBAL_ROOT_UID;
89e49c7b2fSDavid Howells 	inode->i_gid		= GLOBAL_ROOT_GID;
90e49c7b2fSDavid Howells 	inode->i_ctime = inode->i_atime = inode->i_mtime = current_time(inode);
91e49c7b2fSDavid Howells 	inode->i_blocks		= 0;
92e49c7b2fSDavid Howells 	inode->i_generation	= 0;
93e49c7b2fSDavid Howells 
94e49c7b2fSDavid Howells 	set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags);
95e49c7b2fSDavid Howells 	if (!root) {
96e49c7b2fSDavid Howells 		set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
97e49c7b2fSDavid Howells 		inode->i_flags |= S_AUTOMOUNT;
98e49c7b2fSDavid Howells 	}
99e49c7b2fSDavid Howells 
100e49c7b2fSDavid Howells 	inode->i_flags |= S_NOATIME;
101e49c7b2fSDavid Howells 	unlock_new_inode(inode);
102e49c7b2fSDavid Howells 	_leave(" = %p", inode);
103e49c7b2fSDavid Howells 	return inode;
104e49c7b2fSDavid Howells }
105e49c7b2fSDavid Howells 
10666c7e1d3SDavid Howells /*
10766c7e1d3SDavid Howells  * Probe to see if a cell may exist.  This prevents positive dentries from
10866c7e1d3SDavid Howells  * being created unnecessarily.
10966c7e1d3SDavid Howells  */
11066c7e1d3SDavid Howells static int afs_probe_cell_name(struct dentry *dentry)
11166c7e1d3SDavid Howells {
11266c7e1d3SDavid Howells 	struct afs_cell *cell;
113a58946c1SDavid Howells 	struct afs_net *net = afs_d2net(dentry);
11466c7e1d3SDavid Howells 	const char *name = dentry->d_name.name;
11566c7e1d3SDavid Howells 	size_t len = dentry->d_name.len;
11666c7e1d3SDavid Howells 	int ret;
11766c7e1d3SDavid Howells 
11866c7e1d3SDavid Howells 	/* Names prefixed with a dot are R/W mounts. */
11966c7e1d3SDavid Howells 	if (name[0] == '.') {
12066c7e1d3SDavid Howells 		if (len == 1)
12166c7e1d3SDavid Howells 			return -EINVAL;
12266c7e1d3SDavid Howells 		name++;
12366c7e1d3SDavid Howells 		len--;
12466c7e1d3SDavid Howells 	}
12566c7e1d3SDavid Howells 
12692e3cc91SDavid Howells 	cell = afs_find_cell(net, name, len);
12766c7e1d3SDavid Howells 	if (!IS_ERR(cell)) {
128a58946c1SDavid Howells 		afs_put_cell(net, cell);
12966c7e1d3SDavid Howells 		return 0;
13066c7e1d3SDavid Howells 	}
13166c7e1d3SDavid Howells 
132a58946c1SDavid Howells 	ret = dns_query(net->net, "afsdb", name, len, "srv=1",
133a58946c1SDavid Howells 			NULL, NULL, false);
13466c7e1d3SDavid Howells 	if (ret == -ENODATA)
13566c7e1d3SDavid Howells 		ret = -EDESTADDRREQ;
13666c7e1d3SDavid Howells 	return ret;
13766c7e1d3SDavid Howells }
13866c7e1d3SDavid Howells 
13966c7e1d3SDavid Howells /*
14066c7e1d3SDavid Howells  * Try to auto mount the mountpoint with pseudo directory, if the autocell
14166c7e1d3SDavid Howells  * operation is setted.
14266c7e1d3SDavid Howells  */
14366c7e1d3SDavid Howells struct inode *afs_try_auto_mntpt(struct dentry *dentry, struct inode *dir)
14466c7e1d3SDavid Howells {
14566c7e1d3SDavid Howells 	struct afs_vnode *vnode = AFS_FS_I(dir);
14666c7e1d3SDavid Howells 	struct inode *inode;
14766c7e1d3SDavid Howells 	int ret = -ENOENT;
14866c7e1d3SDavid Howells 
1493b6492dfSDavid Howells 	_enter("%p{%pd}, {%llx:%llu}",
15066c7e1d3SDavid Howells 	       dentry, dentry, vnode->fid.vid, vnode->fid.vnode);
15166c7e1d3SDavid Howells 
15266c7e1d3SDavid Howells 	if (!test_bit(AFS_VNODE_AUTOCELL, &vnode->flags))
15366c7e1d3SDavid Howells 		goto out;
15466c7e1d3SDavid Howells 
15566c7e1d3SDavid Howells 	ret = afs_probe_cell_name(dentry);
15666c7e1d3SDavid Howells 	if (ret < 0)
15766c7e1d3SDavid Howells 		goto out;
15866c7e1d3SDavid Howells 
15966c7e1d3SDavid Howells 	inode = afs_iget_pseudo_dir(dir->i_sb, false);
16066c7e1d3SDavid Howells 	if (IS_ERR(inode)) {
16166c7e1d3SDavid Howells 		ret = PTR_ERR(inode);
16266c7e1d3SDavid Howells 		goto out;
16366c7e1d3SDavid Howells 	}
16466c7e1d3SDavid Howells 
16566c7e1d3SDavid Howells 	_leave("= %p", inode);
16666c7e1d3SDavid Howells 	return inode;
16766c7e1d3SDavid Howells 
16866c7e1d3SDavid Howells out:
16966c7e1d3SDavid Howells 	_leave("= %d", ret);
1701401a0fcSAl Viro 	return ret == -ENOENT ? NULL : ERR_PTR(ret);
17166c7e1d3SDavid Howells }
17266c7e1d3SDavid Howells 
17366c7e1d3SDavid Howells /*
17466c7e1d3SDavid Howells  * Look up @cell in a dynroot directory.  This is a substitution for the
17566c7e1d3SDavid Howells  * local cell name for the net namespace.
17666c7e1d3SDavid Howells  */
17766c7e1d3SDavid Howells static struct dentry *afs_lookup_atcell(struct dentry *dentry)
17866c7e1d3SDavid Howells {
17966c7e1d3SDavid Howells 	struct afs_cell *cell;
18066c7e1d3SDavid Howells 	struct afs_net *net = afs_d2net(dentry);
18166c7e1d3SDavid Howells 	struct dentry *ret;
18266c7e1d3SDavid Howells 	char *name;
18366c7e1d3SDavid Howells 	int len;
18466c7e1d3SDavid Howells 
18566c7e1d3SDavid Howells 	if (!net->ws_cell)
18666c7e1d3SDavid Howells 		return ERR_PTR(-ENOENT);
18766c7e1d3SDavid Howells 
18866c7e1d3SDavid Howells 	ret = ERR_PTR(-ENOMEM);
18966c7e1d3SDavid Howells 	name = kmalloc(AFS_MAXCELLNAME + 1, GFP_KERNEL);
19066c7e1d3SDavid Howells 	if (!name)
19166c7e1d3SDavid Howells 		goto out_p;
19266c7e1d3SDavid Howells 
19392e3cc91SDavid Howells 	down_read(&net->cells_lock);
19492e3cc91SDavid Howells 	cell = net->ws_cell;
19566c7e1d3SDavid Howells 	if (cell) {
19666c7e1d3SDavid Howells 		len = cell->name_len;
19766c7e1d3SDavid Howells 		memcpy(name, cell->name, len + 1);
19866c7e1d3SDavid Howells 	}
19992e3cc91SDavid Howells 	up_read(&net->cells_lock);
20066c7e1d3SDavid Howells 
20166c7e1d3SDavid Howells 	ret = ERR_PTR(-ENOENT);
20266c7e1d3SDavid Howells 	if (!cell)
20366c7e1d3SDavid Howells 		goto out_n;
20466c7e1d3SDavid Howells 
20566c7e1d3SDavid Howells 	ret = lookup_one_len(name, dentry->d_parent, len);
20666c7e1d3SDavid Howells 
20766c7e1d3SDavid Howells 	/* We don't want to d_add() the @cell dentry here as we don't want to
20866c7e1d3SDavid Howells 	 * the cached dentry to hide changes to the local cell name.
20966c7e1d3SDavid Howells 	 */
21066c7e1d3SDavid Howells 
21166c7e1d3SDavid Howells out_n:
21266c7e1d3SDavid Howells 	kfree(name);
21366c7e1d3SDavid Howells out_p:
21466c7e1d3SDavid Howells 	return ret;
21566c7e1d3SDavid Howells }
21666c7e1d3SDavid Howells 
21766c7e1d3SDavid Howells /*
21866c7e1d3SDavid Howells  * Look up an entry in a dynroot directory.
21966c7e1d3SDavid Howells  */
22066c7e1d3SDavid Howells static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry,
22166c7e1d3SDavid Howells 					 unsigned int flags)
22266c7e1d3SDavid Howells {
22366c7e1d3SDavid Howells 	_enter("%pd", dentry);
22466c7e1d3SDavid Howells 
22566c7e1d3SDavid Howells 	ASSERTCMP(d_inode(dentry), ==, NULL);
22666c7e1d3SDavid Howells 
2271da4bd9fSDavid Howells 	if (flags & LOOKUP_CREATE)
2281da4bd9fSDavid Howells 		return ERR_PTR(-EOPNOTSUPP);
2291da4bd9fSDavid Howells 
23066c7e1d3SDavid Howells 	if (dentry->d_name.len >= AFSNAMEMAX) {
23166c7e1d3SDavid Howells 		_leave(" = -ENAMETOOLONG");
23266c7e1d3SDavid Howells 		return ERR_PTR(-ENAMETOOLONG);
23366c7e1d3SDavid Howells 	}
23466c7e1d3SDavid Howells 
23566c7e1d3SDavid Howells 	if (dentry->d_name.len == 5 &&
23666c7e1d3SDavid Howells 	    memcmp(dentry->d_name.name, "@cell", 5) == 0)
23766c7e1d3SDavid Howells 		return afs_lookup_atcell(dentry);
23866c7e1d3SDavid Howells 
2391401a0fcSAl Viro 	return d_splice_alias(afs_try_auto_mntpt(dentry, dir), dentry);
24066c7e1d3SDavid Howells }
24166c7e1d3SDavid Howells 
24266c7e1d3SDavid Howells const struct inode_operations afs_dynroot_inode_operations = {
24366c7e1d3SDavid Howells 	.lookup		= afs_dynroot_lookup,
24466c7e1d3SDavid Howells };
24566c7e1d3SDavid Howells 
24666c7e1d3SDavid Howells /*
24766c7e1d3SDavid Howells  * Dirs in the dynamic root don't need revalidation.
24866c7e1d3SDavid Howells  */
24966c7e1d3SDavid Howells static int afs_dynroot_d_revalidate(struct dentry *dentry, unsigned int flags)
25066c7e1d3SDavid Howells {
25166c7e1d3SDavid Howells 	return 1;
25266c7e1d3SDavid Howells }
25366c7e1d3SDavid Howells 
25466c7e1d3SDavid Howells /*
25566c7e1d3SDavid Howells  * Allow the VFS to enquire as to whether a dentry should be unhashed (mustn't
25666c7e1d3SDavid Howells  * sleep)
25766c7e1d3SDavid Howells  * - called from dput() when d_count is going to 0.
25866c7e1d3SDavid Howells  * - return 1 to request dentry be unhashed, 0 otherwise
25966c7e1d3SDavid Howells  */
26066c7e1d3SDavid Howells static int afs_dynroot_d_delete(const struct dentry *dentry)
26166c7e1d3SDavid Howells {
26266c7e1d3SDavid Howells 	return d_really_is_positive(dentry);
26366c7e1d3SDavid Howells }
26466c7e1d3SDavid Howells 
26566c7e1d3SDavid Howells const struct dentry_operations afs_dynroot_dentry_operations = {
26666c7e1d3SDavid Howells 	.d_revalidate	= afs_dynroot_d_revalidate,
26766c7e1d3SDavid Howells 	.d_delete	= afs_dynroot_d_delete,
26866c7e1d3SDavid Howells 	.d_release	= afs_d_release,
26966c7e1d3SDavid Howells 	.d_automount	= afs_d_automount,
27066c7e1d3SDavid Howells };
2710da0b7fdSDavid Howells 
2720da0b7fdSDavid Howells /*
2730da0b7fdSDavid Howells  * Create a manually added cell mount directory.
2740da0b7fdSDavid Howells  * - The caller must hold net->proc_cells_lock
2750da0b7fdSDavid Howells  */
2760da0b7fdSDavid Howells int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell)
2770da0b7fdSDavid Howells {
2780da0b7fdSDavid Howells 	struct super_block *sb = net->dynroot_sb;
2790da0b7fdSDavid Howells 	struct dentry *root, *subdir;
2800da0b7fdSDavid Howells 	int ret;
2810da0b7fdSDavid Howells 
2820da0b7fdSDavid Howells 	if (!sb || atomic_read(&sb->s_active) == 0)
2830da0b7fdSDavid Howells 		return 0;
2840da0b7fdSDavid Howells 
2850da0b7fdSDavid Howells 	/* Let the ->lookup op do the creation */
2860da0b7fdSDavid Howells 	root = sb->s_root;
2870da0b7fdSDavid Howells 	inode_lock(root->d_inode);
2880da0b7fdSDavid Howells 	subdir = lookup_one_len(cell->name, root, cell->name_len);
2890da0b7fdSDavid Howells 	if (IS_ERR(subdir)) {
2900da0b7fdSDavid Howells 		ret = PTR_ERR(subdir);
2910da0b7fdSDavid Howells 		goto unlock;
2920da0b7fdSDavid Howells 	}
2930da0b7fdSDavid Howells 
2940da0b7fdSDavid Howells 	/* Note that we're retaining an extra ref on the dentry */
2950da0b7fdSDavid Howells 	subdir->d_fsdata = (void *)1UL;
2960da0b7fdSDavid Howells 	ret = 0;
2970da0b7fdSDavid Howells unlock:
2980da0b7fdSDavid Howells 	inode_unlock(root->d_inode);
2990da0b7fdSDavid Howells 	return ret;
3000da0b7fdSDavid Howells }
3010da0b7fdSDavid Howells 
3020da0b7fdSDavid Howells /*
3030da0b7fdSDavid Howells  * Remove a manually added cell mount directory.
3040da0b7fdSDavid Howells  * - The caller must hold net->proc_cells_lock
3050da0b7fdSDavid Howells  */
3060da0b7fdSDavid Howells void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
3070da0b7fdSDavid Howells {
3080da0b7fdSDavid Howells 	struct super_block *sb = net->dynroot_sb;
3090da0b7fdSDavid Howells 	struct dentry *root, *subdir;
3100da0b7fdSDavid Howells 
3110da0b7fdSDavid Howells 	if (!sb || atomic_read(&sb->s_active) == 0)
3120da0b7fdSDavid Howells 		return;
3130da0b7fdSDavid Howells 
3140da0b7fdSDavid Howells 	root = sb->s_root;
3150da0b7fdSDavid Howells 	inode_lock(root->d_inode);
3160da0b7fdSDavid Howells 
3170da0b7fdSDavid Howells 	/* Don't want to trigger a lookup call, which will re-add the cell */
3180da0b7fdSDavid Howells 	subdir = try_lookup_one_len(cell->name, root, cell->name_len);
3190da0b7fdSDavid Howells 	if (IS_ERR_OR_NULL(subdir)) {
3200da0b7fdSDavid Howells 		_debug("lookup %ld", PTR_ERR(subdir));
3210da0b7fdSDavid Howells 		goto no_dentry;
3220da0b7fdSDavid Howells 	}
3230da0b7fdSDavid Howells 
3240da0b7fdSDavid Howells 	_debug("rmdir %pd %u", subdir, d_count(subdir));
3250da0b7fdSDavid Howells 
3260da0b7fdSDavid Howells 	if (subdir->d_fsdata) {
3270da0b7fdSDavid Howells 		_debug("unpin %u", d_count(subdir));
3280da0b7fdSDavid Howells 		subdir->d_fsdata = NULL;
3290da0b7fdSDavid Howells 		dput(subdir);
3300da0b7fdSDavid Howells 	}
3310da0b7fdSDavid Howells 	dput(subdir);
3320da0b7fdSDavid Howells no_dentry:
3330da0b7fdSDavid Howells 	inode_unlock(root->d_inode);
3340da0b7fdSDavid Howells 	_leave("");
3350da0b7fdSDavid Howells }
3360da0b7fdSDavid Howells 
3370da0b7fdSDavid Howells /*
3380da0b7fdSDavid Howells  * Populate a newly created dynamic root with cell names.
3390da0b7fdSDavid Howells  */
3400da0b7fdSDavid Howells int afs_dynroot_populate(struct super_block *sb)
3410da0b7fdSDavid Howells {
3420da0b7fdSDavid Howells 	struct afs_cell *cell;
3430da0b7fdSDavid Howells 	struct afs_net *net = afs_sb2net(sb);
3440da0b7fdSDavid Howells 	int ret;
3450da0b7fdSDavid Howells 
3463b05e528SDavid Howells 	mutex_lock(&net->proc_cells_lock);
3470da0b7fdSDavid Howells 
3480da0b7fdSDavid Howells 	net->dynroot_sb = sb;
3496b3944e4SDavid Howells 	hlist_for_each_entry(cell, &net->proc_cells, proc_link) {
3500da0b7fdSDavid Howells 		ret = afs_dynroot_mkdir(net, cell);
3510da0b7fdSDavid Howells 		if (ret < 0)
3520da0b7fdSDavid Howells 			goto error;
3530da0b7fdSDavid Howells 	}
3540da0b7fdSDavid Howells 
3550da0b7fdSDavid Howells 	ret = 0;
3560da0b7fdSDavid Howells out:
3570da0b7fdSDavid Howells 	mutex_unlock(&net->proc_cells_lock);
3580da0b7fdSDavid Howells 	return ret;
3590da0b7fdSDavid Howells 
3600da0b7fdSDavid Howells error:
3610da0b7fdSDavid Howells 	net->dynroot_sb = NULL;
3620da0b7fdSDavid Howells 	goto out;
3630da0b7fdSDavid Howells }
3640da0b7fdSDavid Howells 
3650da0b7fdSDavid Howells /*
3660da0b7fdSDavid Howells  * When a dynamic root that's in the process of being destroyed, depopulate it
3670da0b7fdSDavid Howells  * of pinned directories.
3680da0b7fdSDavid Howells  */
3690da0b7fdSDavid Howells void afs_dynroot_depopulate(struct super_block *sb)
3700da0b7fdSDavid Howells {
3710da0b7fdSDavid Howells 	struct afs_net *net = afs_sb2net(sb);
3720da0b7fdSDavid Howells 	struct dentry *root = sb->s_root, *subdir, *tmp;
3730da0b7fdSDavid Howells 
3740da0b7fdSDavid Howells 	/* Prevent more subdirs from being created */
3750da0b7fdSDavid Howells 	mutex_lock(&net->proc_cells_lock);
3760da0b7fdSDavid Howells 	if (net->dynroot_sb == sb)
3770da0b7fdSDavid Howells 		net->dynroot_sb = NULL;
3780da0b7fdSDavid Howells 	mutex_unlock(&net->proc_cells_lock);
3790da0b7fdSDavid Howells 
3805e0b17b0SDavid Howells 	if (root) {
3810da0b7fdSDavid Howells 		inode_lock(root->d_inode);
3820da0b7fdSDavid Howells 
3830da0b7fdSDavid Howells 		/* Remove all the pins for dirs created for manually added cells */
3840da0b7fdSDavid Howells 		list_for_each_entry_safe(subdir, tmp, &root->d_subdirs, d_child) {
3850da0b7fdSDavid Howells 			if (subdir->d_fsdata) {
3860da0b7fdSDavid Howells 				subdir->d_fsdata = NULL;
3870da0b7fdSDavid Howells 				dput(subdir);
3880da0b7fdSDavid Howells 			}
3890da0b7fdSDavid Howells 		}
3900da0b7fdSDavid Howells 
3910da0b7fdSDavid Howells 		inode_unlock(root->d_inode);
3920da0b7fdSDavid Howells 	}
3935e0b17b0SDavid Howells }
394