Skip to content

Commit

Permalink
afs: Support the AFS dynamic root
Browse files Browse the repository at this point in the history
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: David Howells <[email protected]>
  • Loading branch information
dhowells committed Feb 6, 2018
1 parent 16280a1 commit 4d673da
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 97 deletions.
17 changes: 17 additions & 0 deletions Documentation/filesystems/afs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Contents:
- Overview.
- Usage.
- Mountpoints.
- Dynamic root.
- Proc filesystem.
- The cell database.
- Security.
Expand Down Expand Up @@ -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
===============
Expand Down
122 changes: 108 additions & 14 deletions fs/afs/dir.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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("%d, %p{%pd}, {%x:%u}, %p",
ret, dentry, dentry, vnode->fid.vid, vnode->fid.vnode, key);
_enter("%p{%pd}, {%x:%u}",
dentry, dentry, vnode->fid.vid, vnode->fid.vnode);

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;
Expand Down Expand Up @@ -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 (!IS_ERR(inode)) {
key_put(key);
goto success;
if (ret == -ENOENT) {
inode = afs_try_auto_mntpt(dentry, dir, &fid);
if (!IS_ERR(inode)) {
key_put(key);
goto success;
}

ret = PTR_ERR(inode);
}

ret = PTR_ERR(inode);
key_put(key);
if (ret == -ENOENT) {
d_add(dentry, NULL);
Expand Down Expand Up @@ -582,13 +632,54 @@ 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
* inode
*/
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;
Expand All @@ -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},",
Expand Down
48 changes: 30 additions & 18 deletions fs/afs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
data.volume = as->volume;
data.fid.vid = as->volume->vid;
data.fid.unique = 0;
data.fid.vnode = 0;
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;
}

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");
Expand All @@ -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;
inode->i_op = &afs_autocell_inode_operations;
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;
Expand All @@ -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);
set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
inode->i_flags |= S_AUTOMOUNT | S_NOATIME;
if (!root) {
set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
inode->i_flags |= S_AUTOMOUNT;
}

inode->i_flags |= S_NOATIME;
unlock_new_inode(inode);
_leave(" = %p", inode);
return inode;
Expand Down
12 changes: 8 additions & 4 deletions fs/afs/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 *,
Expand Down
20 changes: 11 additions & 9 deletions fs/afs/mntpt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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);
memcpy(options, "cell=", 5);
strcpy(options + 5, super->volume->cell->name);
if (super->volume->type == AFSVL_RWVOL || rwpath)
strcat(options, ",rwpath");
as = AFS_FS_S(mntpt->d_sb);
if (as->cell) {
memcpy(options, "cell=", 5);
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);
Expand Down
Loading

0 comments on commit 4d673da

Please sign in to comment.