xref: /openbmc/linux/fs/afs/dynroot.c (revision 3c305aa9)
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  */
afs_iget5_pseudo_test(struct inode * inode,void * opaque)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  */
afs_iget5_pseudo_set(struct inode * inode,void * opaque)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  */
afs_iget_pseudo_dir(struct super_block * sb,bool root)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;
91b9170a28SJeff Layton 	inode->i_atime = inode->i_mtime = inode_set_ctime_current(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  */
afs_probe_cell_name(struct dentry * dentry)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;
117*3c305aa9SDavid Howells 	char *result = NULL;
11866c7e1d3SDavid Howells 	int ret;
11966c7e1d3SDavid Howells 
12066c7e1d3SDavid Howells 	/* Names prefixed with a dot are R/W mounts. */
12166c7e1d3SDavid Howells 	if (name[0] == '.') {
12266c7e1d3SDavid Howells 		if (len == 1)
12366c7e1d3SDavid Howells 			return -EINVAL;
12466c7e1d3SDavid Howells 		name++;
12566c7e1d3SDavid Howells 		len--;
12666c7e1d3SDavid Howells 	}
12766c7e1d3SDavid Howells 
128dca54a7bSDavid Howells 	cell = afs_find_cell(net, name, len, afs_cell_trace_use_probe);
12966c7e1d3SDavid Howells 	if (!IS_ERR(cell)) {
130dca54a7bSDavid Howells 		afs_unuse_cell(net, cell, afs_cell_trace_unuse_probe);
13166c7e1d3SDavid Howells 		return 0;
13266c7e1d3SDavid Howells 	}
13366c7e1d3SDavid Howells 
134a58946c1SDavid Howells 	ret = dns_query(net->net, "afsdb", name, len, "srv=1",
135*3c305aa9SDavid Howells 			&result, NULL, false);
136*3c305aa9SDavid Howells 	if (ret == -ENODATA || ret == -ENOKEY || ret == 0)
13718350d28SDavid Howells 		ret = -ENOENT;
138*3c305aa9SDavid Howells 	if (ret > 0 && ret >= sizeof(struct dns_server_list_v1_header)) {
139*3c305aa9SDavid Howells 		struct dns_server_list_v1_header *v1 = (void *)result;
140*3c305aa9SDavid Howells 
141*3c305aa9SDavid Howells 		if (v1->hdr.zero == 0 &&
142*3c305aa9SDavid Howells 		    v1->hdr.content == DNS_PAYLOAD_IS_SERVER_LIST &&
143*3c305aa9SDavid Howells 		    v1->hdr.version == 1 &&
144*3c305aa9SDavid Howells 		    (v1->status != DNS_LOOKUP_GOOD &&
145*3c305aa9SDavid Howells 		     v1->status != DNS_LOOKUP_GOOD_WITH_BAD))
146*3c305aa9SDavid Howells 			return -ENOENT;
147*3c305aa9SDavid Howells 
148*3c305aa9SDavid Howells 	}
149*3c305aa9SDavid Howells 
150*3c305aa9SDavid Howells 	kfree(result);
15166c7e1d3SDavid Howells 	return ret;
15266c7e1d3SDavid Howells }
15366c7e1d3SDavid Howells 
15466c7e1d3SDavid Howells /*
15566c7e1d3SDavid Howells  * Try to auto mount the mountpoint with pseudo directory, if the autocell
15666c7e1d3SDavid Howells  * operation is setted.
15766c7e1d3SDavid Howells  */
afs_try_auto_mntpt(struct dentry * dentry,struct inode * dir)15866c7e1d3SDavid Howells struct inode *afs_try_auto_mntpt(struct dentry *dentry, struct inode *dir)
15966c7e1d3SDavid Howells {
16066c7e1d3SDavid Howells 	struct afs_vnode *vnode = AFS_FS_I(dir);
16166c7e1d3SDavid Howells 	struct inode *inode;
16266c7e1d3SDavid Howells 	int ret = -ENOENT;
16366c7e1d3SDavid Howells 
1643b6492dfSDavid Howells 	_enter("%p{%pd}, {%llx:%llu}",
16566c7e1d3SDavid Howells 	       dentry, dentry, vnode->fid.vid, vnode->fid.vnode);
16666c7e1d3SDavid Howells 
16766c7e1d3SDavid Howells 	if (!test_bit(AFS_VNODE_AUTOCELL, &vnode->flags))
16866c7e1d3SDavid Howells 		goto out;
16966c7e1d3SDavid Howells 
17066c7e1d3SDavid Howells 	ret = afs_probe_cell_name(dentry);
17166c7e1d3SDavid Howells 	if (ret < 0)
17266c7e1d3SDavid Howells 		goto out;
17366c7e1d3SDavid Howells 
17466c7e1d3SDavid Howells 	inode = afs_iget_pseudo_dir(dir->i_sb, false);
17566c7e1d3SDavid Howells 	if (IS_ERR(inode)) {
17666c7e1d3SDavid Howells 		ret = PTR_ERR(inode);
17766c7e1d3SDavid Howells 		goto out;
17866c7e1d3SDavid Howells 	}
17966c7e1d3SDavid Howells 
18066c7e1d3SDavid Howells 	_leave("= %p", inode);
18166c7e1d3SDavid Howells 	return inode;
18266c7e1d3SDavid Howells 
18366c7e1d3SDavid Howells out:
18466c7e1d3SDavid Howells 	_leave("= %d", ret);
1851401a0fcSAl Viro 	return ret == -ENOENT ? NULL : ERR_PTR(ret);
18666c7e1d3SDavid Howells }
18766c7e1d3SDavid Howells 
18866c7e1d3SDavid Howells /*
18966c7e1d3SDavid Howells  * Look up @cell in a dynroot directory.  This is a substitution for the
19066c7e1d3SDavid Howells  * local cell name for the net namespace.
19166c7e1d3SDavid Howells  */
afs_lookup_atcell(struct dentry * dentry)19266c7e1d3SDavid Howells static struct dentry *afs_lookup_atcell(struct dentry *dentry)
19366c7e1d3SDavid Howells {
19466c7e1d3SDavid Howells 	struct afs_cell *cell;
19566c7e1d3SDavid Howells 	struct afs_net *net = afs_d2net(dentry);
19666c7e1d3SDavid Howells 	struct dentry *ret;
19766c7e1d3SDavid Howells 	char *name;
19866c7e1d3SDavid Howells 	int len;
19966c7e1d3SDavid Howells 
20066c7e1d3SDavid Howells 	if (!net->ws_cell)
20166c7e1d3SDavid Howells 		return ERR_PTR(-ENOENT);
20266c7e1d3SDavid Howells 
20366c7e1d3SDavid Howells 	ret = ERR_PTR(-ENOMEM);
20466c7e1d3SDavid Howells 	name = kmalloc(AFS_MAXCELLNAME + 1, GFP_KERNEL);
20566c7e1d3SDavid Howells 	if (!name)
20666c7e1d3SDavid Howells 		goto out_p;
20766c7e1d3SDavid Howells 
20892e3cc91SDavid Howells 	down_read(&net->cells_lock);
20992e3cc91SDavid Howells 	cell = net->ws_cell;
21066c7e1d3SDavid Howells 	if (cell) {
21166c7e1d3SDavid Howells 		len = cell->name_len;
21266c7e1d3SDavid Howells 		memcpy(name, cell->name, len + 1);
21366c7e1d3SDavid Howells 	}
21492e3cc91SDavid Howells 	up_read(&net->cells_lock);
21566c7e1d3SDavid Howells 
21666c7e1d3SDavid Howells 	ret = ERR_PTR(-ENOENT);
21766c7e1d3SDavid Howells 	if (!cell)
21866c7e1d3SDavid Howells 		goto out_n;
21966c7e1d3SDavid Howells 
22066c7e1d3SDavid Howells 	ret = lookup_one_len(name, dentry->d_parent, len);
22166c7e1d3SDavid Howells 
22266c7e1d3SDavid Howells 	/* We don't want to d_add() the @cell dentry here as we don't want to
22366c7e1d3SDavid Howells 	 * the cached dentry to hide changes to the local cell name.
22466c7e1d3SDavid Howells 	 */
22566c7e1d3SDavid Howells 
22666c7e1d3SDavid Howells out_n:
22766c7e1d3SDavid Howells 	kfree(name);
22866c7e1d3SDavid Howells out_p:
22966c7e1d3SDavid Howells 	return ret;
23066c7e1d3SDavid Howells }
23166c7e1d3SDavid Howells 
23266c7e1d3SDavid Howells /*
23366c7e1d3SDavid Howells  * Look up an entry in a dynroot directory.
23466c7e1d3SDavid Howells  */
afs_dynroot_lookup(struct inode * dir,struct dentry * dentry,unsigned int flags)23566c7e1d3SDavid Howells static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry,
23666c7e1d3SDavid Howells 					 unsigned int flags)
23766c7e1d3SDavid Howells {
23866c7e1d3SDavid Howells 	_enter("%pd", dentry);
23966c7e1d3SDavid Howells 
24066c7e1d3SDavid Howells 	ASSERTCMP(d_inode(dentry), ==, NULL);
24166c7e1d3SDavid Howells 
2421da4bd9fSDavid Howells 	if (flags & LOOKUP_CREATE)
2431da4bd9fSDavid Howells 		return ERR_PTR(-EOPNOTSUPP);
2441da4bd9fSDavid Howells 
24566c7e1d3SDavid Howells 	if (dentry->d_name.len >= AFSNAMEMAX) {
24666c7e1d3SDavid Howells 		_leave(" = -ENAMETOOLONG");
24766c7e1d3SDavid Howells 		return ERR_PTR(-ENAMETOOLONG);
24866c7e1d3SDavid Howells 	}
24966c7e1d3SDavid Howells 
25066c7e1d3SDavid Howells 	if (dentry->d_name.len == 5 &&
25166c7e1d3SDavid Howells 	    memcmp(dentry->d_name.name, "@cell", 5) == 0)
25266c7e1d3SDavid Howells 		return afs_lookup_atcell(dentry);
25366c7e1d3SDavid Howells 
2541401a0fcSAl Viro 	return d_splice_alias(afs_try_auto_mntpt(dentry, dir), dentry);
25566c7e1d3SDavid Howells }
25666c7e1d3SDavid Howells 
25766c7e1d3SDavid Howells const struct inode_operations afs_dynroot_inode_operations = {
25866c7e1d3SDavid Howells 	.lookup		= afs_dynroot_lookup,
25966c7e1d3SDavid Howells };
26066c7e1d3SDavid Howells 
26166c7e1d3SDavid Howells /*
26266c7e1d3SDavid Howells  * Dirs in the dynamic root don't need revalidation.
26366c7e1d3SDavid Howells  */
afs_dynroot_d_revalidate(struct dentry * dentry,unsigned int flags)26466c7e1d3SDavid Howells static int afs_dynroot_d_revalidate(struct dentry *dentry, unsigned int flags)
26566c7e1d3SDavid Howells {
26666c7e1d3SDavid Howells 	return 1;
26766c7e1d3SDavid Howells }
26866c7e1d3SDavid Howells 
26966c7e1d3SDavid Howells const struct dentry_operations afs_dynroot_dentry_operations = {
27066c7e1d3SDavid Howells 	.d_revalidate	= afs_dynroot_d_revalidate,
2719ff7ae01SDavid Howells 	.d_delete	= always_delete_dentry,
27266c7e1d3SDavid Howells 	.d_release	= afs_d_release,
27366c7e1d3SDavid Howells 	.d_automount	= afs_d_automount,
27466c7e1d3SDavid Howells };
2750da0b7fdSDavid Howells 
2760da0b7fdSDavid Howells /*
2770da0b7fdSDavid Howells  * Create a manually added cell mount directory.
2780da0b7fdSDavid Howells  * - The caller must hold net->proc_cells_lock
2790da0b7fdSDavid Howells  */
afs_dynroot_mkdir(struct afs_net * net,struct afs_cell * cell)2800da0b7fdSDavid Howells int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell)
2810da0b7fdSDavid Howells {
2820da0b7fdSDavid Howells 	struct super_block *sb = net->dynroot_sb;
2830da0b7fdSDavid Howells 	struct dentry *root, *subdir;
2840da0b7fdSDavid Howells 	int ret;
2850da0b7fdSDavid Howells 
2860da0b7fdSDavid Howells 	if (!sb || atomic_read(&sb->s_active) == 0)
2870da0b7fdSDavid Howells 		return 0;
2880da0b7fdSDavid Howells 
2890da0b7fdSDavid Howells 	/* Let the ->lookup op do the creation */
2900da0b7fdSDavid Howells 	root = sb->s_root;
2910da0b7fdSDavid Howells 	inode_lock(root->d_inode);
2920da0b7fdSDavid Howells 	subdir = lookup_one_len(cell->name, root, cell->name_len);
2930da0b7fdSDavid Howells 	if (IS_ERR(subdir)) {
2940da0b7fdSDavid Howells 		ret = PTR_ERR(subdir);
2950da0b7fdSDavid Howells 		goto unlock;
2960da0b7fdSDavid Howells 	}
2970da0b7fdSDavid Howells 
2980da0b7fdSDavid Howells 	/* Note that we're retaining an extra ref on the dentry */
2990da0b7fdSDavid Howells 	subdir->d_fsdata = (void *)1UL;
3000da0b7fdSDavid Howells 	ret = 0;
3010da0b7fdSDavid Howells unlock:
3020da0b7fdSDavid Howells 	inode_unlock(root->d_inode);
3030da0b7fdSDavid Howells 	return ret;
3040da0b7fdSDavid Howells }
3050da0b7fdSDavid Howells 
3060da0b7fdSDavid Howells /*
3070da0b7fdSDavid Howells  * Remove a manually added cell mount directory.
3080da0b7fdSDavid Howells  * - The caller must hold net->proc_cells_lock
3090da0b7fdSDavid Howells  */
afs_dynroot_rmdir(struct afs_net * net,struct afs_cell * cell)3100da0b7fdSDavid Howells void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
3110da0b7fdSDavid Howells {
3120da0b7fdSDavid Howells 	struct super_block *sb = net->dynroot_sb;
3130da0b7fdSDavid Howells 	struct dentry *root, *subdir;
3140da0b7fdSDavid Howells 
3150da0b7fdSDavid Howells 	if (!sb || atomic_read(&sb->s_active) == 0)
3160da0b7fdSDavid Howells 		return;
3170da0b7fdSDavid Howells 
3180da0b7fdSDavid Howells 	root = sb->s_root;
3190da0b7fdSDavid Howells 	inode_lock(root->d_inode);
3200da0b7fdSDavid Howells 
3210da0b7fdSDavid Howells 	/* Don't want to trigger a lookup call, which will re-add the cell */
3220da0b7fdSDavid Howells 	subdir = try_lookup_one_len(cell->name, root, cell->name_len);
3230da0b7fdSDavid Howells 	if (IS_ERR_OR_NULL(subdir)) {
3240da0b7fdSDavid Howells 		_debug("lookup %ld", PTR_ERR(subdir));
3250da0b7fdSDavid Howells 		goto no_dentry;
3260da0b7fdSDavid Howells 	}
3270da0b7fdSDavid Howells 
3280da0b7fdSDavid Howells 	_debug("rmdir %pd %u", subdir, d_count(subdir));
3290da0b7fdSDavid Howells 
3300da0b7fdSDavid Howells 	if (subdir->d_fsdata) {
3310da0b7fdSDavid Howells 		_debug("unpin %u", d_count(subdir));
3320da0b7fdSDavid Howells 		subdir->d_fsdata = NULL;
3330da0b7fdSDavid Howells 		dput(subdir);
3340da0b7fdSDavid Howells 	}
3350da0b7fdSDavid Howells 	dput(subdir);
3360da0b7fdSDavid Howells no_dentry:
3370da0b7fdSDavid Howells 	inode_unlock(root->d_inode);
3380da0b7fdSDavid Howells 	_leave("");
3390da0b7fdSDavid Howells }
3400da0b7fdSDavid Howells 
3410da0b7fdSDavid Howells /*
3420da0b7fdSDavid Howells  * Populate a newly created dynamic root with cell names.
3430da0b7fdSDavid Howells  */
afs_dynroot_populate(struct super_block * sb)3440da0b7fdSDavid Howells int afs_dynroot_populate(struct super_block *sb)
3450da0b7fdSDavid Howells {
3460da0b7fdSDavid Howells 	struct afs_cell *cell;
3470da0b7fdSDavid Howells 	struct afs_net *net = afs_sb2net(sb);
3480da0b7fdSDavid Howells 	int ret;
3490da0b7fdSDavid Howells 
3503b05e528SDavid Howells 	mutex_lock(&net->proc_cells_lock);
3510da0b7fdSDavid Howells 
3520da0b7fdSDavid Howells 	net->dynroot_sb = sb;
3536b3944e4SDavid Howells 	hlist_for_each_entry(cell, &net->proc_cells, proc_link) {
3540da0b7fdSDavid Howells 		ret = afs_dynroot_mkdir(net, cell);
3550da0b7fdSDavid Howells 		if (ret < 0)
3560da0b7fdSDavid Howells 			goto error;
3570da0b7fdSDavid Howells 	}
3580da0b7fdSDavid Howells 
3590da0b7fdSDavid Howells 	ret = 0;
3600da0b7fdSDavid Howells out:
3610da0b7fdSDavid Howells 	mutex_unlock(&net->proc_cells_lock);
3620da0b7fdSDavid Howells 	return ret;
3630da0b7fdSDavid Howells 
3640da0b7fdSDavid Howells error:
3650da0b7fdSDavid Howells 	net->dynroot_sb = NULL;
3660da0b7fdSDavid Howells 	goto out;
3670da0b7fdSDavid Howells }
3680da0b7fdSDavid Howells 
3690da0b7fdSDavid Howells /*
3700da0b7fdSDavid Howells  * When a dynamic root that's in the process of being destroyed, depopulate it
3710da0b7fdSDavid Howells  * of pinned directories.
3720da0b7fdSDavid Howells  */
afs_dynroot_depopulate(struct super_block * sb)3730da0b7fdSDavid Howells void afs_dynroot_depopulate(struct super_block *sb)
3740da0b7fdSDavid Howells {
3750da0b7fdSDavid Howells 	struct afs_net *net = afs_sb2net(sb);
3760da0b7fdSDavid Howells 	struct dentry *root = sb->s_root, *subdir, *tmp;
3770da0b7fdSDavid Howells 
3780da0b7fdSDavid Howells 	/* Prevent more subdirs from being created */
3790da0b7fdSDavid Howells 	mutex_lock(&net->proc_cells_lock);
3800da0b7fdSDavid Howells 	if (net->dynroot_sb == sb)
3810da0b7fdSDavid Howells 		net->dynroot_sb = NULL;
3820da0b7fdSDavid Howells 	mutex_unlock(&net->proc_cells_lock);
3830da0b7fdSDavid Howells 
3845e0b17b0SDavid Howells 	if (root) {
3850da0b7fdSDavid Howells 		inode_lock(root->d_inode);
3860da0b7fdSDavid Howells 
3870da0b7fdSDavid Howells 		/* Remove all the pins for dirs created for manually added cells */
3880da0b7fdSDavid Howells 		list_for_each_entry_safe(subdir, tmp, &root->d_subdirs, d_child) {
3890da0b7fdSDavid Howells 			if (subdir->d_fsdata) {
3900da0b7fdSDavid Howells 				subdir->d_fsdata = NULL;
3910da0b7fdSDavid Howells 				dput(subdir);
3920da0b7fdSDavid Howells 			}
3930da0b7fdSDavid Howells 		}
3940da0b7fdSDavid Howells 
3950da0b7fdSDavid Howells 		inode_unlock(root->d_inode);
3960da0b7fdSDavid Howells 	}
3975e0b17b0SDavid Howells }
398