xref: /linux/fs/afs/dynroot.c (revision da549bdd15c295c24b2ee7ffe7ad0f3877fa8a87)
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 
79e81fb419SLinus Torvalds 	netfs_inode_init(&vnode->netfs, NULL);
80e49c7b2fSDavid Howells 	inode->i_size		= 0;
81e49c7b2fSDavid Howells 	inode->i_mode		= S_IFDIR | S_IRUGO | S_IXUGO;
82e49c7b2fSDavid Howells 	if (root) {
83e49c7b2fSDavid Howells 		inode->i_op	= &afs_dynroot_inode_operations;
84e49c7b2fSDavid Howells 		inode->i_fop	= &simple_dir_operations;
85e49c7b2fSDavid Howells 	} else {
86e49c7b2fSDavid Howells 		inode->i_op	= &afs_autocell_inode_operations;
87e49c7b2fSDavid Howells 	}
88e49c7b2fSDavid Howells 	set_nlink(inode, 2);
89e49c7b2fSDavid Howells 	inode->i_uid		= GLOBAL_ROOT_UID;
90e49c7b2fSDavid Howells 	inode->i_gid		= GLOBAL_ROOT_GID;
91562ce1f7SJeff Layton 	simple_inode_init_ts(inode);
92e49c7b2fSDavid Howells 	inode->i_blocks		= 0;
93e49c7b2fSDavid Howells 	inode->i_generation	= 0;
94e49c7b2fSDavid Howells 
95e49c7b2fSDavid Howells 	set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags);
96e49c7b2fSDavid Howells 	if (!root) {
97e49c7b2fSDavid Howells 		set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
98e49c7b2fSDavid Howells 		inode->i_flags |= S_AUTOMOUNT;
99e49c7b2fSDavid Howells 	}
100e49c7b2fSDavid Howells 
101e49c7b2fSDavid Howells 	inode->i_flags |= S_NOATIME;
102e49c7b2fSDavid Howells 	unlock_new_inode(inode);
103e49c7b2fSDavid Howells 	_leave(" = %p", inode);
104e49c7b2fSDavid Howells 	return inode;
105e49c7b2fSDavid Howells }
106e49c7b2fSDavid Howells 
10766c7e1d3SDavid Howells /*
10866c7e1d3SDavid Howells  * Probe to see if a cell may exist.  This prevents positive dentries from
10966c7e1d3SDavid Howells  * being created unnecessarily.
11066c7e1d3SDavid Howells  */
11166c7e1d3SDavid Howells static int afs_probe_cell_name(struct dentry *dentry)
11266c7e1d3SDavid Howells {
11366c7e1d3SDavid Howells 	struct afs_cell *cell;
114a58946c1SDavid Howells 	struct afs_net *net = afs_d2net(dentry);
11566c7e1d3SDavid Howells 	const char *name = dentry->d_name.name;
11666c7e1d3SDavid Howells 	size_t len = dentry->d_name.len;
11766c7e1d3SDavid Howells 	int ret;
11866c7e1d3SDavid Howells 
11966c7e1d3SDavid Howells 	/* Names prefixed with a dot are R/W mounts. */
12066c7e1d3SDavid Howells 	if (name[0] == '.') {
12166c7e1d3SDavid Howells 		if (len == 1)
12266c7e1d3SDavid Howells 			return -EINVAL;
12366c7e1d3SDavid Howells 		name++;
12466c7e1d3SDavid Howells 		len--;
12566c7e1d3SDavid Howells 	}
12666c7e1d3SDavid Howells 
127dca54a7bSDavid Howells 	cell = afs_find_cell(net, name, len, afs_cell_trace_use_probe);
12866c7e1d3SDavid Howells 	if (!IS_ERR(cell)) {
129dca54a7bSDavid Howells 		afs_unuse_cell(net, cell, afs_cell_trace_unuse_probe);
13066c7e1d3SDavid Howells 		return 0;
13166c7e1d3SDavid Howells 	}
13266c7e1d3SDavid Howells 
133a58946c1SDavid Howells 	ret = dns_query(net->net, "afsdb", name, len, "srv=1",
134a58946c1SDavid Howells 			NULL, NULL, false);
13566c7e1d3SDavid Howells 	if (ret == -ENODATA)
13666c7e1d3SDavid Howells 		ret = -EDESTADDRREQ;
13766c7e1d3SDavid Howells 	return ret;
13866c7e1d3SDavid Howells }
13966c7e1d3SDavid Howells 
14066c7e1d3SDavid Howells /*
14166c7e1d3SDavid Howells  * Try to auto mount the mountpoint with pseudo directory, if the autocell
14266c7e1d3SDavid Howells  * operation is setted.
14366c7e1d3SDavid Howells  */
14466c7e1d3SDavid Howells struct inode *afs_try_auto_mntpt(struct dentry *dentry, struct inode *dir)
14566c7e1d3SDavid Howells {
14666c7e1d3SDavid Howells 	struct afs_vnode *vnode = AFS_FS_I(dir);
14766c7e1d3SDavid Howells 	struct inode *inode;
14866c7e1d3SDavid Howells 	int ret = -ENOENT;
14966c7e1d3SDavid Howells 
1503b6492dfSDavid Howells 	_enter("%p{%pd}, {%llx:%llu}",
15166c7e1d3SDavid Howells 	       dentry, dentry, vnode->fid.vid, vnode->fid.vnode);
15266c7e1d3SDavid Howells 
15366c7e1d3SDavid Howells 	if (!test_bit(AFS_VNODE_AUTOCELL, &vnode->flags))
15466c7e1d3SDavid Howells 		goto out;
15566c7e1d3SDavid Howells 
15666c7e1d3SDavid Howells 	ret = afs_probe_cell_name(dentry);
15766c7e1d3SDavid Howells 	if (ret < 0)
15866c7e1d3SDavid Howells 		goto out;
15966c7e1d3SDavid Howells 
16066c7e1d3SDavid Howells 	inode = afs_iget_pseudo_dir(dir->i_sb, false);
16166c7e1d3SDavid Howells 	if (IS_ERR(inode)) {
16266c7e1d3SDavid Howells 		ret = PTR_ERR(inode);
16366c7e1d3SDavid Howells 		goto out;
16466c7e1d3SDavid Howells 	}
16566c7e1d3SDavid Howells 
16666c7e1d3SDavid Howells 	_leave("= %p", inode);
16766c7e1d3SDavid Howells 	return inode;
16866c7e1d3SDavid Howells 
16966c7e1d3SDavid Howells out:
17066c7e1d3SDavid Howells 	_leave("= %d", ret);
1711401a0fcSAl Viro 	return ret == -ENOENT ? NULL : ERR_PTR(ret);
17266c7e1d3SDavid Howells }
17366c7e1d3SDavid Howells 
17466c7e1d3SDavid Howells /*
17566c7e1d3SDavid Howells  * Look up @cell in a dynroot directory.  This is a substitution for the
17666c7e1d3SDavid Howells  * local cell name for the net namespace.
17766c7e1d3SDavid Howells  */
17866c7e1d3SDavid Howells static struct dentry *afs_lookup_atcell(struct dentry *dentry)
17966c7e1d3SDavid Howells {
18066c7e1d3SDavid Howells 	struct afs_cell *cell;
18166c7e1d3SDavid Howells 	struct afs_net *net = afs_d2net(dentry);
18266c7e1d3SDavid Howells 	struct dentry *ret;
18366c7e1d3SDavid Howells 	char *name;
18466c7e1d3SDavid Howells 	int len;
18566c7e1d3SDavid Howells 
18666c7e1d3SDavid Howells 	if (!net->ws_cell)
18766c7e1d3SDavid Howells 		return ERR_PTR(-ENOENT);
18866c7e1d3SDavid Howells 
18966c7e1d3SDavid Howells 	ret = ERR_PTR(-ENOMEM);
19066c7e1d3SDavid Howells 	name = kmalloc(AFS_MAXCELLNAME + 1, GFP_KERNEL);
19166c7e1d3SDavid Howells 	if (!name)
19266c7e1d3SDavid Howells 		goto out_p;
19366c7e1d3SDavid Howells 
19492e3cc91SDavid Howells 	down_read(&net->cells_lock);
19592e3cc91SDavid Howells 	cell = net->ws_cell;
19666c7e1d3SDavid Howells 	if (cell) {
19766c7e1d3SDavid Howells 		len = cell->name_len;
19866c7e1d3SDavid Howells 		memcpy(name, cell->name, len + 1);
19966c7e1d3SDavid Howells 	}
20092e3cc91SDavid Howells 	up_read(&net->cells_lock);
20166c7e1d3SDavid Howells 
20266c7e1d3SDavid Howells 	ret = ERR_PTR(-ENOENT);
20366c7e1d3SDavid Howells 	if (!cell)
20466c7e1d3SDavid Howells 		goto out_n;
20566c7e1d3SDavid Howells 
20666c7e1d3SDavid Howells 	ret = lookup_one_len(name, dentry->d_parent, len);
20766c7e1d3SDavid Howells 
20866c7e1d3SDavid Howells 	/* We don't want to d_add() the @cell dentry here as we don't want to
20966c7e1d3SDavid Howells 	 * the cached dentry to hide changes to the local cell name.
21066c7e1d3SDavid Howells 	 */
21166c7e1d3SDavid Howells 
21266c7e1d3SDavid Howells out_n:
21366c7e1d3SDavid Howells 	kfree(name);
21466c7e1d3SDavid Howells out_p:
21566c7e1d3SDavid Howells 	return ret;
21666c7e1d3SDavid Howells }
21766c7e1d3SDavid Howells 
21866c7e1d3SDavid Howells /*
21966c7e1d3SDavid Howells  * Look up an entry in a dynroot directory.
22066c7e1d3SDavid Howells  */
22166c7e1d3SDavid Howells static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry,
22266c7e1d3SDavid Howells 					 unsigned int flags)
22366c7e1d3SDavid Howells {
22466c7e1d3SDavid Howells 	_enter("%pd", dentry);
22566c7e1d3SDavid Howells 
22666c7e1d3SDavid Howells 	ASSERTCMP(d_inode(dentry), ==, NULL);
22766c7e1d3SDavid Howells 
2281da4bd9fSDavid Howells 	if (flags & LOOKUP_CREATE)
2291da4bd9fSDavid Howells 		return ERR_PTR(-EOPNOTSUPP);
2301da4bd9fSDavid Howells 
23166c7e1d3SDavid Howells 	if (dentry->d_name.len >= AFSNAMEMAX) {
23266c7e1d3SDavid Howells 		_leave(" = -ENAMETOOLONG");
23366c7e1d3SDavid Howells 		return ERR_PTR(-ENAMETOOLONG);
23466c7e1d3SDavid Howells 	}
23566c7e1d3SDavid Howells 
23666c7e1d3SDavid Howells 	if (dentry->d_name.len == 5 &&
23766c7e1d3SDavid Howells 	    memcmp(dentry->d_name.name, "@cell", 5) == 0)
23866c7e1d3SDavid Howells 		return afs_lookup_atcell(dentry);
23966c7e1d3SDavid Howells 
2401401a0fcSAl Viro 	return d_splice_alias(afs_try_auto_mntpt(dentry, dir), dentry);
24166c7e1d3SDavid Howells }
24266c7e1d3SDavid Howells 
24366c7e1d3SDavid Howells const struct inode_operations afs_dynroot_inode_operations = {
24466c7e1d3SDavid Howells 	.lookup		= afs_dynroot_lookup,
24566c7e1d3SDavid Howells };
24666c7e1d3SDavid Howells 
24766c7e1d3SDavid Howells /*
24866c7e1d3SDavid Howells  * Dirs in the dynamic root don't need revalidation.
24966c7e1d3SDavid Howells  */
25066c7e1d3SDavid Howells static int afs_dynroot_d_revalidate(struct dentry *dentry, unsigned int flags)
25166c7e1d3SDavid Howells {
25266c7e1d3SDavid Howells 	return 1;
25366c7e1d3SDavid Howells }
25466c7e1d3SDavid Howells 
25566c7e1d3SDavid Howells /*
25666c7e1d3SDavid Howells  * Allow the VFS to enquire as to whether a dentry should be unhashed (mustn't
25766c7e1d3SDavid Howells  * sleep)
25866c7e1d3SDavid Howells  * - called from dput() when d_count is going to 0.
25966c7e1d3SDavid Howells  * - return 1 to request dentry be unhashed, 0 otherwise
26066c7e1d3SDavid Howells  */
26166c7e1d3SDavid Howells static int afs_dynroot_d_delete(const struct dentry *dentry)
26266c7e1d3SDavid Howells {
26366c7e1d3SDavid Howells 	return d_really_is_positive(dentry);
26466c7e1d3SDavid Howells }
26566c7e1d3SDavid Howells 
26666c7e1d3SDavid Howells const struct dentry_operations afs_dynroot_dentry_operations = {
26766c7e1d3SDavid Howells 	.d_revalidate	= afs_dynroot_d_revalidate,
26866c7e1d3SDavid Howells 	.d_delete	= afs_dynroot_d_delete,
26966c7e1d3SDavid Howells 	.d_release	= afs_d_release,
27066c7e1d3SDavid Howells 	.d_automount	= afs_d_automount,
27166c7e1d3SDavid Howells };
2720da0b7fdSDavid Howells 
2730da0b7fdSDavid Howells /*
2740da0b7fdSDavid Howells  * Create a manually added cell mount directory.
2750da0b7fdSDavid Howells  * - The caller must hold net->proc_cells_lock
2760da0b7fdSDavid Howells  */
2770da0b7fdSDavid Howells int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell)
2780da0b7fdSDavid Howells {
2790da0b7fdSDavid Howells 	struct super_block *sb = net->dynroot_sb;
2800da0b7fdSDavid Howells 	struct dentry *root, *subdir;
2810da0b7fdSDavid Howells 	int ret;
2820da0b7fdSDavid Howells 
2830da0b7fdSDavid Howells 	if (!sb || atomic_read(&sb->s_active) == 0)
2840da0b7fdSDavid Howells 		return 0;
2850da0b7fdSDavid Howells 
2860da0b7fdSDavid Howells 	/* Let the ->lookup op do the creation */
2870da0b7fdSDavid Howells 	root = sb->s_root;
2880da0b7fdSDavid Howells 	inode_lock(root->d_inode);
2890da0b7fdSDavid Howells 	subdir = lookup_one_len(cell->name, root, cell->name_len);
2900da0b7fdSDavid Howells 	if (IS_ERR(subdir)) {
2910da0b7fdSDavid Howells 		ret = PTR_ERR(subdir);
2920da0b7fdSDavid Howells 		goto unlock;
2930da0b7fdSDavid Howells 	}
2940da0b7fdSDavid Howells 
2950da0b7fdSDavid Howells 	/* Note that we're retaining an extra ref on the dentry */
2960da0b7fdSDavid Howells 	subdir->d_fsdata = (void *)1UL;
2970da0b7fdSDavid Howells 	ret = 0;
2980da0b7fdSDavid Howells unlock:
2990da0b7fdSDavid Howells 	inode_unlock(root->d_inode);
3000da0b7fdSDavid Howells 	return ret;
3010da0b7fdSDavid Howells }
3020da0b7fdSDavid Howells 
3030da0b7fdSDavid Howells /*
3040da0b7fdSDavid Howells  * Remove a manually added cell mount directory.
3050da0b7fdSDavid Howells  * - The caller must hold net->proc_cells_lock
3060da0b7fdSDavid Howells  */
3070da0b7fdSDavid Howells void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
3080da0b7fdSDavid Howells {
3090da0b7fdSDavid Howells 	struct super_block *sb = net->dynroot_sb;
3100da0b7fdSDavid Howells 	struct dentry *root, *subdir;
3110da0b7fdSDavid Howells 
3120da0b7fdSDavid Howells 	if (!sb || atomic_read(&sb->s_active) == 0)
3130da0b7fdSDavid Howells 		return;
3140da0b7fdSDavid Howells 
3150da0b7fdSDavid Howells 	root = sb->s_root;
3160da0b7fdSDavid Howells 	inode_lock(root->d_inode);
3170da0b7fdSDavid Howells 
3180da0b7fdSDavid Howells 	/* Don't want to trigger a lookup call, which will re-add the cell */
3190da0b7fdSDavid Howells 	subdir = try_lookup_one_len(cell->name, root, cell->name_len);
3200da0b7fdSDavid Howells 	if (IS_ERR_OR_NULL(subdir)) {
3210da0b7fdSDavid Howells 		_debug("lookup %ld", PTR_ERR(subdir));
3220da0b7fdSDavid Howells 		goto no_dentry;
3230da0b7fdSDavid Howells 	}
3240da0b7fdSDavid Howells 
3250da0b7fdSDavid Howells 	_debug("rmdir %pd %u", subdir, d_count(subdir));
3260da0b7fdSDavid Howells 
3270da0b7fdSDavid Howells 	if (subdir->d_fsdata) {
3280da0b7fdSDavid Howells 		_debug("unpin %u", d_count(subdir));
3290da0b7fdSDavid Howells 		subdir->d_fsdata = NULL;
3300da0b7fdSDavid Howells 		dput(subdir);
3310da0b7fdSDavid Howells 	}
3320da0b7fdSDavid Howells 	dput(subdir);
3330da0b7fdSDavid Howells no_dentry:
3340da0b7fdSDavid Howells 	inode_unlock(root->d_inode);
3350da0b7fdSDavid Howells 	_leave("");
3360da0b7fdSDavid Howells }
3370da0b7fdSDavid Howells 
3380da0b7fdSDavid Howells /*
3390da0b7fdSDavid Howells  * Populate a newly created dynamic root with cell names.
3400da0b7fdSDavid Howells  */
3410da0b7fdSDavid Howells int afs_dynroot_populate(struct super_block *sb)
3420da0b7fdSDavid Howells {
3430da0b7fdSDavid Howells 	struct afs_cell *cell;
3440da0b7fdSDavid Howells 	struct afs_net *net = afs_sb2net(sb);
3450da0b7fdSDavid Howells 	int ret;
3460da0b7fdSDavid Howells 
3473b05e528SDavid Howells 	mutex_lock(&net->proc_cells_lock);
3480da0b7fdSDavid Howells 
3490da0b7fdSDavid Howells 	net->dynroot_sb = sb;
3506b3944e4SDavid Howells 	hlist_for_each_entry(cell, &net->proc_cells, proc_link) {
3510da0b7fdSDavid Howells 		ret = afs_dynroot_mkdir(net, cell);
3520da0b7fdSDavid Howells 		if (ret < 0)
3530da0b7fdSDavid Howells 			goto error;
3540da0b7fdSDavid Howells 	}
3550da0b7fdSDavid Howells 
3560da0b7fdSDavid Howells 	ret = 0;
3570da0b7fdSDavid Howells out:
3580da0b7fdSDavid Howells 	mutex_unlock(&net->proc_cells_lock);
3590da0b7fdSDavid Howells 	return ret;
3600da0b7fdSDavid Howells 
3610da0b7fdSDavid Howells error:
3620da0b7fdSDavid Howells 	net->dynroot_sb = NULL;
3630da0b7fdSDavid Howells 	goto out;
3640da0b7fdSDavid Howells }
3650da0b7fdSDavid Howells 
3660da0b7fdSDavid Howells /*
3670da0b7fdSDavid Howells  * When a dynamic root that's in the process of being destroyed, depopulate it
3680da0b7fdSDavid Howells  * of pinned directories.
3690da0b7fdSDavid Howells  */
3700da0b7fdSDavid Howells void afs_dynroot_depopulate(struct super_block *sb)
3710da0b7fdSDavid Howells {
3720da0b7fdSDavid Howells 	struct afs_net *net = afs_sb2net(sb);
373*da549bddSAl Viro 	struct dentry *root = sb->s_root, *subdir;
3740da0b7fdSDavid Howells 
3750da0b7fdSDavid Howells 	/* Prevent more subdirs from being created */
3760da0b7fdSDavid Howells 	mutex_lock(&net->proc_cells_lock);
3770da0b7fdSDavid Howells 	if (net->dynroot_sb == sb)
3780da0b7fdSDavid Howells 		net->dynroot_sb = NULL;
3790da0b7fdSDavid Howells 	mutex_unlock(&net->proc_cells_lock);
3800da0b7fdSDavid Howells 
3815e0b17b0SDavid Howells 	if (root) {
382*da549bddSAl Viro 		struct hlist_node *n;
3830da0b7fdSDavid Howells 		inode_lock(root->d_inode);
3840da0b7fdSDavid Howells 
3850da0b7fdSDavid Howells 		/* Remove all the pins for dirs created for manually added cells */
386*da549bddSAl Viro 		hlist_for_each_entry_safe(subdir, n, &root->d_children, d_sib) {
3870da0b7fdSDavid Howells 			if (subdir->d_fsdata) {
3880da0b7fdSDavid Howells 				subdir->d_fsdata = NULL;
3890da0b7fdSDavid Howells 				dput(subdir);
3900da0b7fdSDavid Howells 			}
3910da0b7fdSDavid Howells 		}
3920da0b7fdSDavid Howells 
3930da0b7fdSDavid Howells 		inode_unlock(root->d_inode);
3940da0b7fdSDavid Howells 	}
3955e0b17b0SDavid Howells }
396