xref: /openbmc/linux/fs/afs/dynroot.c (revision 0da0b7fd)
10da0b7fdSDavid Howells /* AFS dynamic root handling
266c7e1d3SDavid Howells  *
366c7e1d3SDavid Howells  * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
466c7e1d3SDavid Howells  * Written by David Howells (dhowells@redhat.com)
566c7e1d3SDavid Howells  *
666c7e1d3SDavid Howells  * This program is free software; you can redistribute it and/or
766c7e1d3SDavid Howells  * modify it under the terms of the GNU General Public Licence
866c7e1d3SDavid Howells  * as published by the Free Software Foundation; either version
966c7e1d3SDavid Howells  * 2 of the Licence, or (at your option) any later version.
1066c7e1d3SDavid Howells  */
1166c7e1d3SDavid Howells 
1266c7e1d3SDavid Howells #include <linux/fs.h>
1366c7e1d3SDavid Howells #include <linux/namei.h>
1466c7e1d3SDavid Howells #include <linux/dns_resolver.h>
1566c7e1d3SDavid Howells #include "internal.h"
1666c7e1d3SDavid Howells 
1766c7e1d3SDavid Howells const struct file_operations afs_dynroot_file_operations = {
1866c7e1d3SDavid Howells 	.open		= dcache_dir_open,
1966c7e1d3SDavid Howells 	.release	= dcache_dir_close,
2066c7e1d3SDavid Howells 	.iterate_shared	= dcache_readdir,
2166c7e1d3SDavid Howells 	.llseek		= dcache_dir_lseek,
2266c7e1d3SDavid Howells };
2366c7e1d3SDavid Howells 
2466c7e1d3SDavid Howells /*
2566c7e1d3SDavid Howells  * Probe to see if a cell may exist.  This prevents positive dentries from
2666c7e1d3SDavid Howells  * being created unnecessarily.
2766c7e1d3SDavid Howells  */
2866c7e1d3SDavid Howells static int afs_probe_cell_name(struct dentry *dentry)
2966c7e1d3SDavid Howells {
3066c7e1d3SDavid Howells 	struct afs_cell *cell;
3166c7e1d3SDavid Howells 	const char *name = dentry->d_name.name;
3266c7e1d3SDavid Howells 	size_t len = dentry->d_name.len;
3366c7e1d3SDavid Howells 	int ret;
3466c7e1d3SDavid Howells 
3566c7e1d3SDavid Howells 	/* Names prefixed with a dot are R/W mounts. */
3666c7e1d3SDavid Howells 	if (name[0] == '.') {
3766c7e1d3SDavid Howells 		if (len == 1)
3866c7e1d3SDavid Howells 			return -EINVAL;
3966c7e1d3SDavid Howells 		name++;
4066c7e1d3SDavid Howells 		len--;
4166c7e1d3SDavid Howells 	}
4266c7e1d3SDavid Howells 
4366c7e1d3SDavid Howells 	cell = afs_lookup_cell_rcu(afs_d2net(dentry), name, len);
4466c7e1d3SDavid Howells 	if (!IS_ERR(cell)) {
4566c7e1d3SDavid Howells 		afs_put_cell(afs_d2net(dentry), cell);
4666c7e1d3SDavid Howells 		return 0;
4766c7e1d3SDavid Howells 	}
4866c7e1d3SDavid Howells 
49c88d5a7fSDavid Howells 	ret = dns_query("afsdb", name, len, "", NULL, NULL);
5066c7e1d3SDavid Howells 	if (ret == -ENODATA)
5166c7e1d3SDavid Howells 		ret = -EDESTADDRREQ;
5266c7e1d3SDavid Howells 	return ret;
5366c7e1d3SDavid Howells }
5466c7e1d3SDavid Howells 
5566c7e1d3SDavid Howells /*
5666c7e1d3SDavid Howells  * Try to auto mount the mountpoint with pseudo directory, if the autocell
5766c7e1d3SDavid Howells  * operation is setted.
5866c7e1d3SDavid Howells  */
5966c7e1d3SDavid Howells struct inode *afs_try_auto_mntpt(struct dentry *dentry, struct inode *dir)
6066c7e1d3SDavid Howells {
6166c7e1d3SDavid Howells 	struct afs_vnode *vnode = AFS_FS_I(dir);
6266c7e1d3SDavid Howells 	struct inode *inode;
6366c7e1d3SDavid Howells 	int ret = -ENOENT;
6466c7e1d3SDavid Howells 
6566c7e1d3SDavid Howells 	_enter("%p{%pd}, {%x:%u}",
6666c7e1d3SDavid Howells 	       dentry, dentry, vnode->fid.vid, vnode->fid.vnode);
6766c7e1d3SDavid Howells 
6866c7e1d3SDavid Howells 	if (!test_bit(AFS_VNODE_AUTOCELL, &vnode->flags))
6966c7e1d3SDavid Howells 		goto out;
7066c7e1d3SDavid Howells 
7166c7e1d3SDavid Howells 	ret = afs_probe_cell_name(dentry);
7266c7e1d3SDavid Howells 	if (ret < 0)
7366c7e1d3SDavid Howells 		goto out;
7466c7e1d3SDavid Howells 
7566c7e1d3SDavid Howells 	inode = afs_iget_pseudo_dir(dir->i_sb, false);
7666c7e1d3SDavid Howells 	if (IS_ERR(inode)) {
7766c7e1d3SDavid Howells 		ret = PTR_ERR(inode);
7866c7e1d3SDavid Howells 		goto out;
7966c7e1d3SDavid Howells 	}
8066c7e1d3SDavid Howells 
8166c7e1d3SDavid Howells 	_leave("= %p", inode);
8266c7e1d3SDavid Howells 	return inode;
8366c7e1d3SDavid Howells 
8466c7e1d3SDavid Howells out:
8566c7e1d3SDavid Howells 	_leave("= %d", ret);
8666c7e1d3SDavid Howells 	return ERR_PTR(ret);
8766c7e1d3SDavid Howells }
8866c7e1d3SDavid Howells 
8966c7e1d3SDavid Howells /*
9066c7e1d3SDavid Howells  * Look up @cell in a dynroot directory.  This is a substitution for the
9166c7e1d3SDavid Howells  * local cell name for the net namespace.
9266c7e1d3SDavid Howells  */
9366c7e1d3SDavid Howells static struct dentry *afs_lookup_atcell(struct dentry *dentry)
9466c7e1d3SDavid Howells {
9566c7e1d3SDavid Howells 	struct afs_cell *cell;
9666c7e1d3SDavid Howells 	struct afs_net *net = afs_d2net(dentry);
9766c7e1d3SDavid Howells 	struct dentry *ret;
9866c7e1d3SDavid Howells 	unsigned int seq = 0;
9966c7e1d3SDavid Howells 	char *name;
10066c7e1d3SDavid Howells 	int len;
10166c7e1d3SDavid Howells 
10266c7e1d3SDavid Howells 	if (!net->ws_cell)
10366c7e1d3SDavid Howells 		return ERR_PTR(-ENOENT);
10466c7e1d3SDavid Howells 
10566c7e1d3SDavid Howells 	ret = ERR_PTR(-ENOMEM);
10666c7e1d3SDavid Howells 	name = kmalloc(AFS_MAXCELLNAME + 1, GFP_KERNEL);
10766c7e1d3SDavid Howells 	if (!name)
10866c7e1d3SDavid Howells 		goto out_p;
10966c7e1d3SDavid Howells 
11066c7e1d3SDavid Howells 	rcu_read_lock();
11166c7e1d3SDavid Howells 	do {
11266c7e1d3SDavid Howells 		read_seqbegin_or_lock(&net->cells_lock, &seq);
11366c7e1d3SDavid Howells 		cell = rcu_dereference_raw(net->ws_cell);
11466c7e1d3SDavid Howells 		if (cell) {
11566c7e1d3SDavid Howells 			len = cell->name_len;
11666c7e1d3SDavid Howells 			memcpy(name, cell->name, len + 1);
11766c7e1d3SDavid Howells 		}
11866c7e1d3SDavid Howells 	} while (need_seqretry(&net->cells_lock, seq));
11966c7e1d3SDavid Howells 	done_seqretry(&net->cells_lock, seq);
12066c7e1d3SDavid Howells 	rcu_read_unlock();
12166c7e1d3SDavid Howells 
12266c7e1d3SDavid Howells 	ret = ERR_PTR(-ENOENT);
12366c7e1d3SDavid Howells 	if (!cell)
12466c7e1d3SDavid Howells 		goto out_n;
12566c7e1d3SDavid Howells 
12666c7e1d3SDavid Howells 	ret = lookup_one_len(name, dentry->d_parent, len);
12766c7e1d3SDavid Howells 
12866c7e1d3SDavid Howells 	/* We don't want to d_add() the @cell dentry here as we don't want to
12966c7e1d3SDavid Howells 	 * the cached dentry to hide changes to the local cell name.
13066c7e1d3SDavid Howells 	 */
13166c7e1d3SDavid Howells 
13266c7e1d3SDavid Howells out_n:
13366c7e1d3SDavid Howells 	kfree(name);
13466c7e1d3SDavid Howells out_p:
13566c7e1d3SDavid Howells 	return ret;
13666c7e1d3SDavid Howells }
13766c7e1d3SDavid Howells 
13866c7e1d3SDavid Howells /*
13966c7e1d3SDavid Howells  * Look up an entry in a dynroot directory.
14066c7e1d3SDavid Howells  */
14166c7e1d3SDavid Howells static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry,
14266c7e1d3SDavid Howells 					 unsigned int flags)
14366c7e1d3SDavid Howells {
14466c7e1d3SDavid Howells 	struct afs_vnode *vnode;
14566c7e1d3SDavid Howells 	struct inode *inode;
14666c7e1d3SDavid Howells 	int ret;
14766c7e1d3SDavid Howells 
14866c7e1d3SDavid Howells 	vnode = AFS_FS_I(dir);
14966c7e1d3SDavid Howells 
15066c7e1d3SDavid Howells 	_enter("%pd", dentry);
15166c7e1d3SDavid Howells 
15266c7e1d3SDavid Howells 	ASSERTCMP(d_inode(dentry), ==, NULL);
15366c7e1d3SDavid Howells 
15466c7e1d3SDavid Howells 	if (dentry->d_name.len >= AFSNAMEMAX) {
15566c7e1d3SDavid Howells 		_leave(" = -ENAMETOOLONG");
15666c7e1d3SDavid Howells 		return ERR_PTR(-ENAMETOOLONG);
15766c7e1d3SDavid Howells 	}
15866c7e1d3SDavid Howells 
15966c7e1d3SDavid Howells 	if (dentry->d_name.len == 5 &&
16066c7e1d3SDavid Howells 	    memcmp(dentry->d_name.name, "@cell", 5) == 0)
16166c7e1d3SDavid Howells 		return afs_lookup_atcell(dentry);
16266c7e1d3SDavid Howells 
16366c7e1d3SDavid Howells 	inode = afs_try_auto_mntpt(dentry, dir);
16466c7e1d3SDavid Howells 	if (IS_ERR(inode)) {
16566c7e1d3SDavid Howells 		ret = PTR_ERR(inode);
16666c7e1d3SDavid Howells 		if (ret == -ENOENT) {
16766c7e1d3SDavid Howells 			d_add(dentry, NULL);
16866c7e1d3SDavid Howells 			_leave(" = NULL [negative]");
16966c7e1d3SDavid Howells 			return NULL;
17066c7e1d3SDavid Howells 		}
17166c7e1d3SDavid Howells 		_leave(" = %d [do]", ret);
17266c7e1d3SDavid Howells 		return ERR_PTR(ret);
17366c7e1d3SDavid Howells 	}
17466c7e1d3SDavid Howells 
17566c7e1d3SDavid Howells 	d_add(dentry, inode);
17666c7e1d3SDavid Howells 	_leave(" = 0 { ino=%lu v=%u }",
17766c7e1d3SDavid Howells 	       d_inode(dentry)->i_ino, d_inode(dentry)->i_generation);
17866c7e1d3SDavid Howells 	return NULL;
17966c7e1d3SDavid Howells }
18066c7e1d3SDavid Howells 
18166c7e1d3SDavid Howells const struct inode_operations afs_dynroot_inode_operations = {
18266c7e1d3SDavid Howells 	.lookup		= afs_dynroot_lookup,
18366c7e1d3SDavid Howells };
18466c7e1d3SDavid Howells 
18566c7e1d3SDavid Howells /*
18666c7e1d3SDavid Howells  * Dirs in the dynamic root don't need revalidation.
18766c7e1d3SDavid Howells  */
18866c7e1d3SDavid Howells static int afs_dynroot_d_revalidate(struct dentry *dentry, unsigned int flags)
18966c7e1d3SDavid Howells {
19066c7e1d3SDavid Howells 	return 1;
19166c7e1d3SDavid Howells }
19266c7e1d3SDavid Howells 
19366c7e1d3SDavid Howells /*
19466c7e1d3SDavid Howells  * Allow the VFS to enquire as to whether a dentry should be unhashed (mustn't
19566c7e1d3SDavid Howells  * sleep)
19666c7e1d3SDavid Howells  * - called from dput() when d_count is going to 0.
19766c7e1d3SDavid Howells  * - return 1 to request dentry be unhashed, 0 otherwise
19866c7e1d3SDavid Howells  */
19966c7e1d3SDavid Howells static int afs_dynroot_d_delete(const struct dentry *dentry)
20066c7e1d3SDavid Howells {
20166c7e1d3SDavid Howells 	return d_really_is_positive(dentry);
20266c7e1d3SDavid Howells }
20366c7e1d3SDavid Howells 
20466c7e1d3SDavid Howells const struct dentry_operations afs_dynroot_dentry_operations = {
20566c7e1d3SDavid Howells 	.d_revalidate	= afs_dynroot_d_revalidate,
20666c7e1d3SDavid Howells 	.d_delete	= afs_dynroot_d_delete,
20766c7e1d3SDavid Howells 	.d_release	= afs_d_release,
20866c7e1d3SDavid Howells 	.d_automount	= afs_d_automount,
20966c7e1d3SDavid Howells };
2100da0b7fdSDavid Howells 
2110da0b7fdSDavid Howells /*
2120da0b7fdSDavid Howells  * Create a manually added cell mount directory.
2130da0b7fdSDavid Howells  * - The caller must hold net->proc_cells_lock
2140da0b7fdSDavid Howells  */
2150da0b7fdSDavid Howells int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell)
2160da0b7fdSDavid Howells {
2170da0b7fdSDavid Howells 	struct super_block *sb = net->dynroot_sb;
2180da0b7fdSDavid Howells 	struct dentry *root, *subdir;
2190da0b7fdSDavid Howells 	int ret;
2200da0b7fdSDavid Howells 
2210da0b7fdSDavid Howells 	if (!sb || atomic_read(&sb->s_active) == 0)
2220da0b7fdSDavid Howells 		return 0;
2230da0b7fdSDavid Howells 
2240da0b7fdSDavid Howells 	/* Let the ->lookup op do the creation */
2250da0b7fdSDavid Howells 	root = sb->s_root;
2260da0b7fdSDavid Howells 	inode_lock(root->d_inode);
2270da0b7fdSDavid Howells 	subdir = lookup_one_len(cell->name, root, cell->name_len);
2280da0b7fdSDavid Howells 	if (IS_ERR(subdir)) {
2290da0b7fdSDavid Howells 		ret = PTR_ERR(subdir);
2300da0b7fdSDavid Howells 		goto unlock;
2310da0b7fdSDavid Howells 	}
2320da0b7fdSDavid Howells 
2330da0b7fdSDavid Howells 	/* Note that we're retaining an extra ref on the dentry */
2340da0b7fdSDavid Howells 	subdir->d_fsdata = (void *)1UL;
2350da0b7fdSDavid Howells 	ret = 0;
2360da0b7fdSDavid Howells unlock:
2370da0b7fdSDavid Howells 	inode_unlock(root->d_inode);
2380da0b7fdSDavid Howells 	return ret;
2390da0b7fdSDavid Howells }
2400da0b7fdSDavid Howells 
2410da0b7fdSDavid Howells /*
2420da0b7fdSDavid Howells  * Remove a manually added cell mount directory.
2430da0b7fdSDavid Howells  * - The caller must hold net->proc_cells_lock
2440da0b7fdSDavid Howells  */
2450da0b7fdSDavid Howells void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
2460da0b7fdSDavid Howells {
2470da0b7fdSDavid Howells 	struct super_block *sb = net->dynroot_sb;
2480da0b7fdSDavid Howells 	struct dentry *root, *subdir;
2490da0b7fdSDavid Howells 
2500da0b7fdSDavid Howells 	if (!sb || atomic_read(&sb->s_active) == 0)
2510da0b7fdSDavid Howells 		return;
2520da0b7fdSDavid Howells 
2530da0b7fdSDavid Howells 	root = sb->s_root;
2540da0b7fdSDavid Howells 	inode_lock(root->d_inode);
2550da0b7fdSDavid Howells 
2560da0b7fdSDavid Howells 	/* Don't want to trigger a lookup call, which will re-add the cell */
2570da0b7fdSDavid Howells 	subdir = try_lookup_one_len(cell->name, root, cell->name_len);
2580da0b7fdSDavid Howells 	if (IS_ERR_OR_NULL(subdir)) {
2590da0b7fdSDavid Howells 		_debug("lookup %ld", PTR_ERR(subdir));
2600da0b7fdSDavid Howells 		goto no_dentry;
2610da0b7fdSDavid Howells 	}
2620da0b7fdSDavid Howells 
2630da0b7fdSDavid Howells 	_debug("rmdir %pd %u", subdir, d_count(subdir));
2640da0b7fdSDavid Howells 
2650da0b7fdSDavid Howells 	if (subdir->d_fsdata) {
2660da0b7fdSDavid Howells 		_debug("unpin %u", d_count(subdir));
2670da0b7fdSDavid Howells 		subdir->d_fsdata = NULL;
2680da0b7fdSDavid Howells 		dput(subdir);
2690da0b7fdSDavid Howells 	}
2700da0b7fdSDavid Howells 	dput(subdir);
2710da0b7fdSDavid Howells no_dentry:
2720da0b7fdSDavid Howells 	inode_unlock(root->d_inode);
2730da0b7fdSDavid Howells 	_leave("");
2740da0b7fdSDavid Howells }
2750da0b7fdSDavid Howells 
2760da0b7fdSDavid Howells /*
2770da0b7fdSDavid Howells  * Populate a newly created dynamic root with cell names.
2780da0b7fdSDavid Howells  */
2790da0b7fdSDavid Howells int afs_dynroot_populate(struct super_block *sb)
2800da0b7fdSDavid Howells {
2810da0b7fdSDavid Howells 	struct afs_cell *cell;
2820da0b7fdSDavid Howells 	struct afs_net *net = afs_sb2net(sb);
2830da0b7fdSDavid Howells 	int ret;
2840da0b7fdSDavid Howells 
2850da0b7fdSDavid Howells 	if (mutex_lock_interruptible(&net->proc_cells_lock) < 0)
2860da0b7fdSDavid Howells 		return -ERESTARTSYS;
2870da0b7fdSDavid Howells 
2880da0b7fdSDavid Howells 	net->dynroot_sb = sb;
2890da0b7fdSDavid Howells 	list_for_each_entry(cell, &net->proc_cells, proc_link) {
2900da0b7fdSDavid Howells 		ret = afs_dynroot_mkdir(net, cell);
2910da0b7fdSDavid Howells 		if (ret < 0)
2920da0b7fdSDavid Howells 			goto error;
2930da0b7fdSDavid Howells 	}
2940da0b7fdSDavid Howells 
2950da0b7fdSDavid Howells 	ret = 0;
2960da0b7fdSDavid Howells out:
2970da0b7fdSDavid Howells 	mutex_unlock(&net->proc_cells_lock);
2980da0b7fdSDavid Howells 	return ret;
2990da0b7fdSDavid Howells 
3000da0b7fdSDavid Howells error:
3010da0b7fdSDavid Howells 	net->dynroot_sb = NULL;
3020da0b7fdSDavid Howells 	goto out;
3030da0b7fdSDavid Howells }
3040da0b7fdSDavid Howells 
3050da0b7fdSDavid Howells /*
3060da0b7fdSDavid Howells  * When a dynamic root that's in the process of being destroyed, depopulate it
3070da0b7fdSDavid Howells  * of pinned directories.
3080da0b7fdSDavid Howells  */
3090da0b7fdSDavid Howells void afs_dynroot_depopulate(struct super_block *sb)
3100da0b7fdSDavid Howells {
3110da0b7fdSDavid Howells 	struct afs_net *net = afs_sb2net(sb);
3120da0b7fdSDavid Howells 	struct dentry *root = sb->s_root, *subdir, *tmp;
3130da0b7fdSDavid Howells 
3140da0b7fdSDavid Howells 	/* Prevent more subdirs from being created */
3150da0b7fdSDavid Howells 	mutex_lock(&net->proc_cells_lock);
3160da0b7fdSDavid Howells 	if (net->dynroot_sb == sb)
3170da0b7fdSDavid Howells 		net->dynroot_sb = NULL;
3180da0b7fdSDavid Howells 	mutex_unlock(&net->proc_cells_lock);
3190da0b7fdSDavid Howells 
3200da0b7fdSDavid Howells 	inode_lock(root->d_inode);
3210da0b7fdSDavid Howells 
3220da0b7fdSDavid Howells 	/* Remove all the pins for dirs created for manually added cells */
3230da0b7fdSDavid Howells 	list_for_each_entry_safe(subdir, tmp, &root->d_subdirs, d_child) {
3240da0b7fdSDavid Howells 		if (subdir->d_fsdata) {
3250da0b7fdSDavid Howells 			subdir->d_fsdata = NULL;
3260da0b7fdSDavid Howells 			dput(subdir);
3270da0b7fdSDavid Howells 		}
3280da0b7fdSDavid Howells 	}
3290da0b7fdSDavid Howells 
3300da0b7fdSDavid Howells 	inode_unlock(root->d_inode);
3310da0b7fdSDavid Howells }
332