Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4d673da1 authored by David Howells's avatar David Howells
Browse files

afs: Support the AFS dynamic root



Support the AFS dynamic root which is a pseudo-volume that doesn't connect
to any server resource, but rather is just a root directory that
dynamically creates mountpoint directories where the name of such a
directory is the name of the cell.

Such a mount can be created thus:

	mount -t afs none /afs -o dyn

Dynamic root superblocks aren't shared except by bind mounts and
propagation.  Cell root volumes can then be mounted by referring to them by
name, e.g.:

	ls /afs/grand.central.org/
	ls /afs/.grand.central.org/

The kernel will upcall to consult the DNS if the address wasn't supplied
directly.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
parent 16280a15
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@ Contents:
 - Overview.
 - Usage.
 - Mountpoints.
 - Dynamic root.
 - Proc filesystem.
 - The cell database.
 - Security.
@@ -127,6 +128,22 @@ mounted on /afs in one go by doing:
	umount /afs


============
DYNAMIC ROOT
============

A mount option is available to create a serverless mount that is only usable
for dynamic lookup.  Creating such a mount can be done by, for example:

	mount -t afs none /afs -o dyn

This creates a mount that just has an empty directory at the root.  Attempting
to look up a name in this directory will cause a mountpoint to be created that
looks up a cell of the same name, for example:

	ls /afs/grand.central.org/


===============
PROC FILESYSTEM
===============
+108 −14
Original line number Diff line number Diff line
@@ -17,10 +17,13 @@
#include <linux/pagemap.h>
#include <linux/ctype.h>
#include <linux/sched.h>
#include <linux/dns_resolver.h>
#include "internal.h"

static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
				 unsigned int flags);
static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry,
					 unsigned int flags);
static int afs_dir_open(struct inode *inode, struct file *file);
static int afs_readdir(struct file *file, struct dir_context *ctx);
static int afs_d_revalidate(struct dentry *dentry, unsigned int flags);
@@ -64,6 +67,17 @@ const struct inode_operations afs_dir_inode_operations = {
	.listxattr	= afs_listxattr,
};

const struct file_operations afs_dynroot_file_operations = {
	.open		= dcache_dir_open,
	.release	= dcache_dir_close,
	.iterate_shared	= dcache_readdir,
	.llseek		= dcache_dir_lseek,
};

const struct inode_operations afs_dynroot_inode_operations = {
	.lookup		= afs_dynroot_lookup,
};

const struct dentry_operations afs_fs_dentry_operations = {
	.d_revalidate	= afs_d_revalidate,
	.d_delete	= afs_d_delete,
@@ -467,26 +481,59 @@ static int afs_do_lookup(struct inode *dir, struct dentry *dentry,
	return 0;
}

/*
 * Probe to see if a cell may exist.  This prevents positive dentries from
 * being created unnecessarily.
 */
static int afs_probe_cell_name(struct dentry *dentry)
{
	struct afs_cell *cell;
	const char *name = dentry->d_name.name;
	size_t len = dentry->d_name.len;
	int ret;

	/* Names prefixed with a dot are R/W mounts. */
	if (name[0] == '.') {
		if (len == 1)
			return -EINVAL;
		name++;
		len--;
	}

	cell = afs_lookup_cell_rcu(afs_d2net(dentry), name, len);
	if (!IS_ERR(cell)) {
		afs_put_cell(afs_d2net(dentry), cell);
		return 0;
	}

	ret = dns_query("afsdb", name, len, "ipv4", NULL, NULL);
	if (ret == -ENODATA)
		ret = -EDESTADDRREQ;
	return ret;
}

/*
 * Try to auto mount the mountpoint with pseudo directory, if the autocell
 * operation is setted.
 */
static struct inode *afs_try_auto_mntpt(
	int ret, struct dentry *dentry, struct inode *dir, struct key *key,
	struct afs_fid *fid)
static struct inode *afs_try_auto_mntpt(struct dentry *dentry,
					struct inode *dir, struct afs_fid *fid)
{
	const char *devname = dentry->d_name.name;
	struct afs_vnode *vnode = AFS_FS_I(dir);
	struct inode *inode;
	int ret = -ENOENT;

	_enter("%p{%pd}, {%x:%u}",
	       dentry, dentry, vnode->fid.vid, vnode->fid.vnode);

	_enter("%d, %p{%pd}, {%x:%u}, %p",
	       ret, dentry, dentry, vnode->fid.vid, vnode->fid.vnode, key);
	if (!test_bit(AFS_VNODE_AUTOCELL, &vnode->flags))
		goto out;

	if (ret != -ENOENT ||
	    !test_bit(AFS_VNODE_AUTOCELL, &vnode->flags))
	ret = afs_probe_cell_name(dentry);
	if (ret < 0)
		goto out;

	inode = afs_iget_autocell(dir, devname, strlen(devname), key);
	inode = afs_iget_pseudo_dir(dir->i_sb, false);
	if (IS_ERR(inode)) {
		ret = PTR_ERR(inode);
		goto out;
@@ -545,13 +592,16 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,

	ret = afs_do_lookup(dir, dentry, &fid, key);
	if (ret < 0) {
		inode = afs_try_auto_mntpt(ret, dentry, dir, key, &fid);
		if (ret == -ENOENT) {
			inode = afs_try_auto_mntpt(dentry, dir, &fid);
			if (!IS_ERR(inode)) {
				key_put(key);
				goto success;
			}

			ret = PTR_ERR(inode);
		}

		key_put(key);
		if (ret == -ENOENT) {
			d_add(dentry, NULL);
@@ -582,6 +632,46 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
	return NULL;
}

/*
 * Look up an entry in a dynroot directory.
 */
static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry,
					 unsigned int flags)
{
	struct afs_vnode *vnode;
	struct afs_fid fid;
	struct inode *inode;
	int ret;

	vnode = AFS_FS_I(dir);

	_enter("%pd", dentry);

	ASSERTCMP(d_inode(dentry), ==, NULL);

	if (dentry->d_name.len >= AFSNAMEMAX) {
		_leave(" = -ENAMETOOLONG");
		return ERR_PTR(-ENAMETOOLONG);
	}

	inode = afs_try_auto_mntpt(dentry, dir, &fid);
	if (IS_ERR(inode)) {
		ret = PTR_ERR(inode);
		if (ret == -ENOENT) {
			d_add(dentry, NULL);
			_leave(" = NULL [negative]");
			return NULL;
		}
		_leave(" = %d [do]", ret);
		return ERR_PTR(ret);
	}

	d_add(dentry, inode);
	_leave(" = 0 { ino=%lu v=%u }",
	       d_inode(dentry)->i_ino, d_inode(dentry)->i_generation);
	return NULL;
}

/*
 * check that a dentry lookup hit has found a valid entry
 * - NOTE! the hit can be a negative hit too, so we can't assume we have an
@@ -589,6 +679,7 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
 */
static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
{
	struct afs_super_info *as = dentry->d_sb->s_fs_info;
	struct afs_vnode *vnode, *dir;
	struct afs_fid uninitialized_var(fid);
	struct dentry *parent;
@@ -600,6 +691,9 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
	if (flags & LOOKUP_RCU)
		return -ECHILD;

	if (as->dyn_root)
		return 1;

	if (d_really_is_positive(dentry)) {
		vnode = AFS_FS_I(d_inode(dentry));
		_enter("{v={%x:%u} n=%pd fl=%lx},",
+30 −18
Original line number Diff line number Diff line
@@ -147,7 +147,7 @@ int afs_iget5_test(struct inode *inode, void *opaque)
 *
 * These pseudo inodes don't match anything.
 */
static int afs_iget5_autocell_test(struct inode *inode, void *opaque)
static int afs_iget5_pseudo_dir_test(struct inode *inode, void *opaque)
{
	return 0;
}
@@ -169,31 +169,34 @@ static int afs_iget5_set(struct inode *inode, void *opaque)
}

/*
 * inode retrieval for autocell
 * Create an inode for a dynamic root directory or an autocell dynamic
 * automount dir.
 */
struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name,
				int namesz, struct key *key)
struct inode *afs_iget_pseudo_dir(struct super_block *sb, bool root)
{
	struct afs_iget_data data;
	struct afs_super_info *as;
	struct afs_vnode *vnode;
	struct super_block *sb;
	struct inode *inode;
	static atomic_t afs_autocell_ino;

	_enter("{%x:%u},%*.*s,",
	       AFS_FS_I(dir)->fid.vid, AFS_FS_I(dir)->fid.vnode,
	       namesz, namesz, dev_name ?: "");
	_enter("");

	sb = dir->i_sb;
	as = sb->s_fs_info;
	if (as->volume) {
		data.volume = as->volume;
		data.fid.vid = as->volume->vid;
	}
	if (root) {
		data.fid.vnode = 1;
		data.fid.unique = 1;
	} else {
		data.fid.vnode = atomic_inc_return(&afs_autocell_ino);
		data.fid.unique = 0;
	data.fid.vnode = 0;
	}

	inode = iget5_locked(sb, atomic_inc_return(&afs_autocell_ino),
			     afs_iget5_autocell_test, afs_iget5_set,
	inode = iget5_locked(sb, data.fid.vnode,
			     afs_iget5_pseudo_dir_test, afs_iget5_set,
			     &data);
	if (!inode) {
		_leave(" = -ENOMEM");
@@ -211,7 +214,12 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name,

	inode->i_size		= 0;
	inode->i_mode		= S_IFDIR | S_IRUGO | S_IXUGO;
	if (root) {
		inode->i_op	= &afs_dynroot_inode_operations;
		inode->i_fop	= &afs_dynroot_file_operations;
	} else {
		inode->i_op	= &afs_autocell_inode_operations;
	}
	set_nlink(inode, 2);
	inode->i_uid		= GLOBAL_ROOT_UID;
	inode->i_gid		= GLOBAL_ROOT_GID;
@@ -223,8 +231,12 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name,
	inode->i_generation	= 0;

	set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags);
	if (!root) {
		set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
	inode->i_flags |= S_AUTOMOUNT | S_NOATIME;
		inode->i_flags |= S_AUTOMOUNT;
	}

	inode->i_flags |= S_NOATIME;
	unlock_new_inode(inode);
	_leave(" = %p", inode);
	return inode;
+8 −4
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ struct afs_mount_params {
	bool			rwpath;		/* T if the parent should be considered R/W */
	bool			force;		/* T to force cell type */
	bool			autocell;	/* T if set auto mount operation */
	bool			dyn_root;	/* T if dynamic root */
	afs_voltype_t		type;		/* type of volume requested */
	int			volnamesz;	/* size of volume name */
	const char		*volname;	/* name of volume to mount */
@@ -186,6 +187,7 @@ struct afs_super_info {
	struct afs_net		*net;		/* Network namespace */
	struct afs_cell		*cell;		/* The cell in which the volume resides */
	struct afs_volume	*volume;	/* volume record */
	bool			dyn_root;	/* True if dynamic root */
};

static inline struct afs_super_info *AFS_FS_S(struct super_block *sb)
@@ -634,10 +636,13 @@ extern bool afs_cm_incoming_call(struct afs_call *);
/*
 * dir.c
 */
extern bool afs_dir_check_page(struct inode *, struct page *);
extern const struct file_operations afs_dir_file_operations;
extern const struct inode_operations afs_dir_inode_operations;
extern const struct file_operations afs_dynroot_file_operations;
extern const struct inode_operations afs_dynroot_inode_operations;
extern const struct dentry_operations afs_fs_dentry_operations;
extern const struct file_operations afs_dir_file_operations;

extern bool afs_dir_check_page(struct inode *, struct page *);

/*
 * file.c
@@ -695,8 +700,7 @@ extern int afs_fs_get_capabilities(struct afs_net *, struct afs_server *,
 */
extern int afs_fetch_status(struct afs_vnode *, struct key *);
extern int afs_iget5_test(struct inode *, void *);
extern struct inode *afs_iget_autocell(struct inode *, const char *, int,
				       struct key *);
extern struct inode *afs_iget_pseudo_dir(struct super_block *, bool);
extern struct inode *afs_iget(struct super_block *, struct key *,
			      struct afs_fid *, struct afs_file_status *,
			      struct afs_callback *,
+11 −9
Original line number Diff line number Diff line
@@ -72,7 +72,7 @@ static int afs_mntpt_open(struct inode *inode, struct file *file)
 */
static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
{
	struct afs_super_info *super;
	struct afs_super_info *as;
	struct vfsmount *mnt;
	struct afs_vnode *vnode;
	struct page *page;
@@ -104,13 +104,13 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
			goto error_no_page;

		if (mntpt->d_name.name[0] == '.') {
			devname[0] = '#';
			memcpy(devname + 1, mntpt->d_name.name, size - 1);
			devname[0] = '%';
			memcpy(devname + 1, mntpt->d_name.name + 1, size - 1);
			memcpy(devname + size, afs_root_cell,
			       sizeof(afs_root_cell));
			rwpath = true;
		} else {
			devname[0] = '%';
			devname[0] = '#';
			memcpy(devname + 1, mntpt->d_name.name, size);
			memcpy(devname + size + 1, afs_root_cell,
			       sizeof(afs_root_cell));
@@ -142,11 +142,13 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
	}

	/* work out what options we want */
	super = AFS_FS_S(mntpt->d_sb);
	as = AFS_FS_S(mntpt->d_sb);
	if (as->cell) {
		memcpy(options, "cell=", 5);
	strcpy(options + 5, super->volume->cell->name);
	if (super->volume->type == AFSVL_RWVOL || rwpath)
		strcpy(options + 5, as->cell->name);
		if ((as->volume && as->volume->type == AFSVL_RWVOL) || rwpath)
			strcat(options, ",rwpath");
	}

	/* try and do the mount */
	_debug("--- attempting mount %s -o %s ---", devname, options);
Loading