162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/* AFS dynamic root handling
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
562306a36Sopenharmony_ci * Written by David Howells (dhowells@redhat.com)
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/fs.h>
962306a36Sopenharmony_ci#include <linux/namei.h>
1062306a36Sopenharmony_ci#include <linux/dns_resolver.h>
1162306a36Sopenharmony_ci#include "internal.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic atomic_t afs_autocell_ino;
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci/*
1662306a36Sopenharmony_ci * iget5() comparator for inode created by autocell operations
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * These pseudo inodes don't match anything.
1962306a36Sopenharmony_ci */
2062306a36Sopenharmony_cistatic int afs_iget5_pseudo_test(struct inode *inode, void *opaque)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	return 0;
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/*
2662306a36Sopenharmony_ci * iget5() inode initialiser
2762306a36Sopenharmony_ci */
2862306a36Sopenharmony_cistatic int afs_iget5_pseudo_set(struct inode *inode, void *opaque)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	struct afs_super_info *as = AFS_FS_S(inode->i_sb);
3162306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(inode);
3262306a36Sopenharmony_ci	struct afs_fid *fid = opaque;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	vnode->volume		= as->volume;
3562306a36Sopenharmony_ci	vnode->fid		= *fid;
3662306a36Sopenharmony_ci	inode->i_ino		= fid->vnode;
3762306a36Sopenharmony_ci	inode->i_generation	= fid->unique;
3862306a36Sopenharmony_ci	return 0;
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/*
4262306a36Sopenharmony_ci * Create an inode for a dynamic root directory or an autocell dynamic
4362306a36Sopenharmony_ci * automount dir.
4462306a36Sopenharmony_ci */
4562306a36Sopenharmony_cistruct inode *afs_iget_pseudo_dir(struct super_block *sb, bool root)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	struct afs_super_info *as = AFS_FS_S(sb);
4862306a36Sopenharmony_ci	struct afs_vnode *vnode;
4962306a36Sopenharmony_ci	struct inode *inode;
5062306a36Sopenharmony_ci	struct afs_fid fid = {};
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	_enter("");
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	if (as->volume)
5562306a36Sopenharmony_ci		fid.vid = as->volume->vid;
5662306a36Sopenharmony_ci	if (root) {
5762306a36Sopenharmony_ci		fid.vnode = 1;
5862306a36Sopenharmony_ci		fid.unique = 1;
5962306a36Sopenharmony_ci	} else {
6062306a36Sopenharmony_ci		fid.vnode = atomic_inc_return(&afs_autocell_ino);
6162306a36Sopenharmony_ci		fid.unique = 0;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	inode = iget5_locked(sb, fid.vnode,
6562306a36Sopenharmony_ci			     afs_iget5_pseudo_test, afs_iget5_pseudo_set, &fid);
6662306a36Sopenharmony_ci	if (!inode) {
6762306a36Sopenharmony_ci		_leave(" = -ENOMEM");
6862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
6962306a36Sopenharmony_ci	}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	_debug("GOT INODE %p { ino=%lu, vl=%llx, vn=%llx, u=%x }",
7262306a36Sopenharmony_ci	       inode, inode->i_ino, fid.vid, fid.vnode, fid.unique);
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	vnode = AFS_FS_I(inode);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* there shouldn't be an existing inode */
7762306a36Sopenharmony_ci	BUG_ON(!(inode->i_state & I_NEW));
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	netfs_inode_init(&vnode->netfs, NULL);
8062306a36Sopenharmony_ci	inode->i_size		= 0;
8162306a36Sopenharmony_ci	inode->i_mode		= S_IFDIR | S_IRUGO | S_IXUGO;
8262306a36Sopenharmony_ci	if (root) {
8362306a36Sopenharmony_ci		inode->i_op	= &afs_dynroot_inode_operations;
8462306a36Sopenharmony_ci		inode->i_fop	= &simple_dir_operations;
8562306a36Sopenharmony_ci	} else {
8662306a36Sopenharmony_ci		inode->i_op	= &afs_autocell_inode_operations;
8762306a36Sopenharmony_ci	}
8862306a36Sopenharmony_ci	set_nlink(inode, 2);
8962306a36Sopenharmony_ci	inode->i_uid		= GLOBAL_ROOT_UID;
9062306a36Sopenharmony_ci	inode->i_gid		= GLOBAL_ROOT_GID;
9162306a36Sopenharmony_ci	inode->i_atime = inode->i_mtime = inode_set_ctime_current(inode);
9262306a36Sopenharmony_ci	inode->i_blocks		= 0;
9362306a36Sopenharmony_ci	inode->i_generation	= 0;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags);
9662306a36Sopenharmony_ci	if (!root) {
9762306a36Sopenharmony_ci		set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
9862306a36Sopenharmony_ci		inode->i_flags |= S_AUTOMOUNT;
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	inode->i_flags |= S_NOATIME;
10262306a36Sopenharmony_ci	unlock_new_inode(inode);
10362306a36Sopenharmony_ci	_leave(" = %p", inode);
10462306a36Sopenharmony_ci	return inode;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci/*
10862306a36Sopenharmony_ci * Probe to see if a cell may exist.  This prevents positive dentries from
10962306a36Sopenharmony_ci * being created unnecessarily.
11062306a36Sopenharmony_ci */
11162306a36Sopenharmony_cistatic int afs_probe_cell_name(struct dentry *dentry)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct afs_cell *cell;
11462306a36Sopenharmony_ci	struct afs_net *net = afs_d2net(dentry);
11562306a36Sopenharmony_ci	const char *name = dentry->d_name.name;
11662306a36Sopenharmony_ci	size_t len = dentry->d_name.len;
11762306a36Sopenharmony_ci	char *result = NULL;
11862306a36Sopenharmony_ci	int ret;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* Names prefixed with a dot are R/W mounts. */
12162306a36Sopenharmony_ci	if (name[0] == '.') {
12262306a36Sopenharmony_ci		if (len == 1)
12362306a36Sopenharmony_ci			return -EINVAL;
12462306a36Sopenharmony_ci		name++;
12562306a36Sopenharmony_ci		len--;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	cell = afs_find_cell(net, name, len, afs_cell_trace_use_probe);
12962306a36Sopenharmony_ci	if (!IS_ERR(cell)) {
13062306a36Sopenharmony_ci		afs_unuse_cell(net, cell, afs_cell_trace_unuse_probe);
13162306a36Sopenharmony_ci		return 0;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	ret = dns_query(net->net, "afsdb", name, len, "srv=1",
13562306a36Sopenharmony_ci			&result, NULL, false);
13662306a36Sopenharmony_ci	if (ret == -ENODATA || ret == -ENOKEY || ret == 0)
13762306a36Sopenharmony_ci		ret = -ENOENT;
13862306a36Sopenharmony_ci	if (ret > 0 && ret >= sizeof(struct dns_server_list_v1_header)) {
13962306a36Sopenharmony_ci		struct dns_server_list_v1_header *v1 = (void *)result;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci		if (v1->hdr.zero == 0 &&
14262306a36Sopenharmony_ci		    v1->hdr.content == DNS_PAYLOAD_IS_SERVER_LIST &&
14362306a36Sopenharmony_ci		    v1->hdr.version == 1 &&
14462306a36Sopenharmony_ci		    (v1->status != DNS_LOOKUP_GOOD &&
14562306a36Sopenharmony_ci		     v1->status != DNS_LOOKUP_GOOD_WITH_BAD))
14662306a36Sopenharmony_ci			return -ENOENT;
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	kfree(result);
15162306a36Sopenharmony_ci	return ret;
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci/*
15562306a36Sopenharmony_ci * Try to auto mount the mountpoint with pseudo directory, if the autocell
15662306a36Sopenharmony_ci * operation is setted.
15762306a36Sopenharmony_ci */
15862306a36Sopenharmony_cistruct inode *afs_try_auto_mntpt(struct dentry *dentry, struct inode *dir)
15962306a36Sopenharmony_ci{
16062306a36Sopenharmony_ci	struct afs_vnode *vnode = AFS_FS_I(dir);
16162306a36Sopenharmony_ci	struct inode *inode;
16262306a36Sopenharmony_ci	int ret = -ENOENT;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	_enter("%p{%pd}, {%llx:%llu}",
16562306a36Sopenharmony_ci	       dentry, dentry, vnode->fid.vid, vnode->fid.vnode);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	if (!test_bit(AFS_VNODE_AUTOCELL, &vnode->flags))
16862306a36Sopenharmony_ci		goto out;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	ret = afs_probe_cell_name(dentry);
17162306a36Sopenharmony_ci	if (ret < 0)
17262306a36Sopenharmony_ci		goto out;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	inode = afs_iget_pseudo_dir(dir->i_sb, false);
17562306a36Sopenharmony_ci	if (IS_ERR(inode)) {
17662306a36Sopenharmony_ci		ret = PTR_ERR(inode);
17762306a36Sopenharmony_ci		goto out;
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	_leave("= %p", inode);
18162306a36Sopenharmony_ci	return inode;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ciout:
18462306a36Sopenharmony_ci	_leave("= %d", ret);
18562306a36Sopenharmony_ci	return ret == -ENOENT ? NULL : ERR_PTR(ret);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/*
18962306a36Sopenharmony_ci * Look up @cell in a dynroot directory.  This is a substitution for the
19062306a36Sopenharmony_ci * local cell name for the net namespace.
19162306a36Sopenharmony_ci */
19262306a36Sopenharmony_cistatic struct dentry *afs_lookup_atcell(struct dentry *dentry)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	struct afs_cell *cell;
19562306a36Sopenharmony_ci	struct afs_net *net = afs_d2net(dentry);
19662306a36Sopenharmony_ci	struct dentry *ret;
19762306a36Sopenharmony_ci	char *name;
19862306a36Sopenharmony_ci	int len;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	if (!net->ws_cell)
20162306a36Sopenharmony_ci		return ERR_PTR(-ENOENT);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	ret = ERR_PTR(-ENOMEM);
20462306a36Sopenharmony_ci	name = kmalloc(AFS_MAXCELLNAME + 1, GFP_KERNEL);
20562306a36Sopenharmony_ci	if (!name)
20662306a36Sopenharmony_ci		goto out_p;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	down_read(&net->cells_lock);
20962306a36Sopenharmony_ci	cell = net->ws_cell;
21062306a36Sopenharmony_ci	if (cell) {
21162306a36Sopenharmony_ci		len = cell->name_len;
21262306a36Sopenharmony_ci		memcpy(name, cell->name, len + 1);
21362306a36Sopenharmony_ci	}
21462306a36Sopenharmony_ci	up_read(&net->cells_lock);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	ret = ERR_PTR(-ENOENT);
21762306a36Sopenharmony_ci	if (!cell)
21862306a36Sopenharmony_ci		goto out_n;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	ret = lookup_one_len(name, dentry->d_parent, len);
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	/* We don't want to d_add() the @cell dentry here as we don't want to
22362306a36Sopenharmony_ci	 * the cached dentry to hide changes to the local cell name.
22462306a36Sopenharmony_ci	 */
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ciout_n:
22762306a36Sopenharmony_ci	kfree(name);
22862306a36Sopenharmony_ciout_p:
22962306a36Sopenharmony_ci	return ret;
23062306a36Sopenharmony_ci}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci/*
23362306a36Sopenharmony_ci * Look up an entry in a dynroot directory.
23462306a36Sopenharmony_ci */
23562306a36Sopenharmony_cistatic struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry,
23662306a36Sopenharmony_ci					 unsigned int flags)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	_enter("%pd", dentry);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	ASSERTCMP(d_inode(dentry), ==, NULL);
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	if (flags & LOOKUP_CREATE)
24362306a36Sopenharmony_ci		return ERR_PTR(-EOPNOTSUPP);
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if (dentry->d_name.len >= AFSNAMEMAX) {
24662306a36Sopenharmony_ci		_leave(" = -ENAMETOOLONG");
24762306a36Sopenharmony_ci		return ERR_PTR(-ENAMETOOLONG);
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	if (dentry->d_name.len == 5 &&
25162306a36Sopenharmony_ci	    memcmp(dentry->d_name.name, "@cell", 5) == 0)
25262306a36Sopenharmony_ci		return afs_lookup_atcell(dentry);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	return d_splice_alias(afs_try_auto_mntpt(dentry, dir), dentry);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ciconst struct inode_operations afs_dynroot_inode_operations = {
25862306a36Sopenharmony_ci	.lookup		= afs_dynroot_lookup,
25962306a36Sopenharmony_ci};
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci/*
26262306a36Sopenharmony_ci * Dirs in the dynamic root don't need revalidation.
26362306a36Sopenharmony_ci */
26462306a36Sopenharmony_cistatic int afs_dynroot_d_revalidate(struct dentry *dentry, unsigned int flags)
26562306a36Sopenharmony_ci{
26662306a36Sopenharmony_ci	return 1;
26762306a36Sopenharmony_ci}
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ciconst struct dentry_operations afs_dynroot_dentry_operations = {
27062306a36Sopenharmony_ci	.d_revalidate	= afs_dynroot_d_revalidate,
27162306a36Sopenharmony_ci	.d_delete	= always_delete_dentry,
27262306a36Sopenharmony_ci	.d_release	= afs_d_release,
27362306a36Sopenharmony_ci	.d_automount	= afs_d_automount,
27462306a36Sopenharmony_ci};
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/*
27762306a36Sopenharmony_ci * Create a manually added cell mount directory.
27862306a36Sopenharmony_ci * - The caller must hold net->proc_cells_lock
27962306a36Sopenharmony_ci */
28062306a36Sopenharmony_ciint afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	struct super_block *sb = net->dynroot_sb;
28362306a36Sopenharmony_ci	struct dentry *root, *subdir;
28462306a36Sopenharmony_ci	int ret;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	if (!sb || atomic_read(&sb->s_active) == 0)
28762306a36Sopenharmony_ci		return 0;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	/* Let the ->lookup op do the creation */
29062306a36Sopenharmony_ci	root = sb->s_root;
29162306a36Sopenharmony_ci	inode_lock(root->d_inode);
29262306a36Sopenharmony_ci	subdir = lookup_one_len(cell->name, root, cell->name_len);
29362306a36Sopenharmony_ci	if (IS_ERR(subdir)) {
29462306a36Sopenharmony_ci		ret = PTR_ERR(subdir);
29562306a36Sopenharmony_ci		goto unlock;
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	/* Note that we're retaining an extra ref on the dentry */
29962306a36Sopenharmony_ci	subdir->d_fsdata = (void *)1UL;
30062306a36Sopenharmony_ci	ret = 0;
30162306a36Sopenharmony_ciunlock:
30262306a36Sopenharmony_ci	inode_unlock(root->d_inode);
30362306a36Sopenharmony_ci	return ret;
30462306a36Sopenharmony_ci}
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci/*
30762306a36Sopenharmony_ci * Remove a manually added cell mount directory.
30862306a36Sopenharmony_ci * - The caller must hold net->proc_cells_lock
30962306a36Sopenharmony_ci */
31062306a36Sopenharmony_civoid afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	struct super_block *sb = net->dynroot_sb;
31362306a36Sopenharmony_ci	struct dentry *root, *subdir;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	if (!sb || atomic_read(&sb->s_active) == 0)
31662306a36Sopenharmony_ci		return;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	root = sb->s_root;
31962306a36Sopenharmony_ci	inode_lock(root->d_inode);
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	/* Don't want to trigger a lookup call, which will re-add the cell */
32262306a36Sopenharmony_ci	subdir = try_lookup_one_len(cell->name, root, cell->name_len);
32362306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(subdir)) {
32462306a36Sopenharmony_ci		_debug("lookup %ld", PTR_ERR(subdir));
32562306a36Sopenharmony_ci		goto no_dentry;
32662306a36Sopenharmony_ci	}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	_debug("rmdir %pd %u", subdir, d_count(subdir));
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	if (subdir->d_fsdata) {
33162306a36Sopenharmony_ci		_debug("unpin %u", d_count(subdir));
33262306a36Sopenharmony_ci		subdir->d_fsdata = NULL;
33362306a36Sopenharmony_ci		dput(subdir);
33462306a36Sopenharmony_ci	}
33562306a36Sopenharmony_ci	dput(subdir);
33662306a36Sopenharmony_cino_dentry:
33762306a36Sopenharmony_ci	inode_unlock(root->d_inode);
33862306a36Sopenharmony_ci	_leave("");
33962306a36Sopenharmony_ci}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci/*
34262306a36Sopenharmony_ci * Populate a newly created dynamic root with cell names.
34362306a36Sopenharmony_ci */
34462306a36Sopenharmony_ciint afs_dynroot_populate(struct super_block *sb)
34562306a36Sopenharmony_ci{
34662306a36Sopenharmony_ci	struct afs_cell *cell;
34762306a36Sopenharmony_ci	struct afs_net *net = afs_sb2net(sb);
34862306a36Sopenharmony_ci	int ret;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	mutex_lock(&net->proc_cells_lock);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	net->dynroot_sb = sb;
35362306a36Sopenharmony_ci	hlist_for_each_entry(cell, &net->proc_cells, proc_link) {
35462306a36Sopenharmony_ci		ret = afs_dynroot_mkdir(net, cell);
35562306a36Sopenharmony_ci		if (ret < 0)
35662306a36Sopenharmony_ci			goto error;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	ret = 0;
36062306a36Sopenharmony_ciout:
36162306a36Sopenharmony_ci	mutex_unlock(&net->proc_cells_lock);
36262306a36Sopenharmony_ci	return ret;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_cierror:
36562306a36Sopenharmony_ci	net->dynroot_sb = NULL;
36662306a36Sopenharmony_ci	goto out;
36762306a36Sopenharmony_ci}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci/*
37062306a36Sopenharmony_ci * When a dynamic root that's in the process of being destroyed, depopulate it
37162306a36Sopenharmony_ci * of pinned directories.
37262306a36Sopenharmony_ci */
37362306a36Sopenharmony_civoid afs_dynroot_depopulate(struct super_block *sb)
37462306a36Sopenharmony_ci{
37562306a36Sopenharmony_ci	struct afs_net *net = afs_sb2net(sb);
37662306a36Sopenharmony_ci	struct dentry *root = sb->s_root, *subdir, *tmp;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_ci	/* Prevent more subdirs from being created */
37962306a36Sopenharmony_ci	mutex_lock(&net->proc_cells_lock);
38062306a36Sopenharmony_ci	if (net->dynroot_sb == sb)
38162306a36Sopenharmony_ci		net->dynroot_sb = NULL;
38262306a36Sopenharmony_ci	mutex_unlock(&net->proc_cells_lock);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (root) {
38562306a36Sopenharmony_ci		inode_lock(root->d_inode);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci		/* Remove all the pins for dirs created for manually added cells */
38862306a36Sopenharmony_ci		list_for_each_entry_safe(subdir, tmp, &root->d_subdirs, d_child) {
38962306a36Sopenharmony_ci			if (subdir->d_fsdata) {
39062306a36Sopenharmony_ci				subdir->d_fsdata = NULL;
39162306a36Sopenharmony_ci				dput(subdir);
39262306a36Sopenharmony_ci			}
39362306a36Sopenharmony_ci		}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci		inode_unlock(root->d_inode);
39662306a36Sopenharmony_ci	}
39762306a36Sopenharmony_ci}
398