xref: /openbmc/linux/fs/afs/dynroot.c (revision 66c7e1d3)
166c7e1d3SDavid Howells /* dir.c: 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 
4966c7e1d3SDavid Howells 	ret = dns_query("afsdb", name, len, "ipv4", 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 };
210