Commit 81153336 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'afs-next-20180208' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs

Pull afs updates from David Howells:
 "Four fixes:

   - add a missing put

   - two fixes to reset the address iteration cursor correctly

   - fix setting up the fileserver iteration cursor.

  Two cleanups:

   - remove some dead code

   - rearrange a function to be more logically laid out

  And one new feature:

   - Support AFS dynamic root.

     With this one should be able to do, say:

        mkdir /afs
        mount -t afs none /afs -o dyn

     to create a dynamic root and then, provided you have keyutils
     installed, do:

        ls /afs/grand.central.org

     and:

        ls /afs/umich.edu

     to list the root volumes of both those organisations' AFS cells
     without requiring any other setup (the kernel upcall to a program
     in the keyutils package to do DNS access as does NFS)"

* tag 'afs-next-20180208' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs:
  afs: Support the AFS dynamic root
  afs: Rearrange afs_select_fileserver() a little
  afs: Remove unused code
  afs: Fix server list handling
  afs: Need to clear responded flag in addr cursor
  afs: Fix missing cursor clearance
  afs: Add missing afs_put_cell()
parents ef9417e8 4d673da1
...@@ -7,6 +7,7 @@ Contents: ...@@ -7,6 +7,7 @@ Contents:
- Overview. - Overview.
- Usage. - Usage.
- Mountpoints. - Mountpoints.
- Dynamic root.
- Proc filesystem. - Proc filesystem.
- The cell database. - The cell database.
- Security. - Security.
...@@ -127,6 +128,22 @@ mounted on /afs in one go by doing: ...@@ -127,6 +128,22 @@ mounted on /afs in one go by doing:
umount /afs 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 PROC FILESYSTEM
=============== ===============
......
...@@ -332,11 +332,18 @@ bool afs_iterate_addresses(struct afs_addr_cursor *ac) ...@@ -332,11 +332,18 @@ bool afs_iterate_addresses(struct afs_addr_cursor *ac)
*/ */
int afs_end_cursor(struct afs_addr_cursor *ac) int afs_end_cursor(struct afs_addr_cursor *ac)
{ {
if (ac->responded && ac->index != ac->start) struct afs_addr_list *alist;
WRITE_ONCE(ac->alist->index, ac->index);
alist = ac->alist;
if (alist) {
if (ac->responded && ac->index != ac->start)
WRITE_ONCE(alist->index, ac->index);
afs_put_addrlist(alist);
}
afs_put_addrlist(ac->alist); ac->addr = NULL;
ac->alist = NULL; ac->alist = NULL;
ac->begun = false;
return ac->error; return ac->error;
} }
......
...@@ -17,10 +17,13 @@ ...@@ -17,10 +17,13 @@
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/dns_resolver.h>
#include "internal.h" #include "internal.h"
static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags); 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_dir_open(struct inode *inode, struct file *file);
static int afs_readdir(struct file *file, struct dir_context *ctx); static int afs_readdir(struct file *file, struct dir_context *ctx);
static int afs_d_revalidate(struct dentry *dentry, unsigned int flags); static int afs_d_revalidate(struct dentry *dentry, unsigned int flags);
...@@ -64,6 +67,17 @@ const struct inode_operations afs_dir_inode_operations = { ...@@ -64,6 +67,17 @@ const struct inode_operations afs_dir_inode_operations = {
.listxattr = afs_listxattr, .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 = { const struct dentry_operations afs_fs_dentry_operations = {
.d_revalidate = afs_d_revalidate, .d_revalidate = afs_d_revalidate,
.d_delete = afs_d_delete, .d_delete = afs_d_delete,
...@@ -467,26 +481,59 @@ static int afs_do_lookup(struct inode *dir, struct dentry *dentry, ...@@ -467,26 +481,59 @@ static int afs_do_lookup(struct inode *dir, struct dentry *dentry,
return 0; 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 * Try to auto mount the mountpoint with pseudo directory, if the autocell
* operation is setted. * operation is setted.
*/ */
static struct inode *afs_try_auto_mntpt( static struct inode *afs_try_auto_mntpt(struct dentry *dentry,
int ret, struct dentry *dentry, struct inode *dir, struct key *key, struct inode *dir, struct afs_fid *fid)
struct afs_fid *fid)
{ {
const char *devname = dentry->d_name.name;
struct afs_vnode *vnode = AFS_FS_I(dir); struct afs_vnode *vnode = AFS_FS_I(dir);
struct inode *inode; struct inode *inode;
int ret = -ENOENT;
_enter("%d, %p{%pd}, {%x:%u}, %p", _enter("%p{%pd}, {%x:%u}",
ret, dentry, dentry, vnode->fid.vid, vnode->fid.vnode, key); dentry, dentry, vnode->fid.vid, vnode->fid.vnode);
if (!test_bit(AFS_VNODE_AUTOCELL, &vnode->flags))
goto out;
if (ret != -ENOENT || ret = afs_probe_cell_name(dentry);
!test_bit(AFS_VNODE_AUTOCELL, &vnode->flags)) if (ret < 0)
goto out; goto out;
inode = afs_iget_autocell(dir, devname, strlen(devname), key); inode = afs_iget_pseudo_dir(dir->i_sb, false);
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
ret = PTR_ERR(inode); ret = PTR_ERR(inode);
goto out; goto out;
...@@ -545,13 +592,16 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, ...@@ -545,13 +592,16 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
ret = afs_do_lookup(dir, dentry, &fid, key); ret = afs_do_lookup(dir, dentry, &fid, key);
if (ret < 0) { if (ret < 0) {
inode = afs_try_auto_mntpt(ret, dentry, dir, key, &fid); if (ret == -ENOENT) {
if (!IS_ERR(inode)) { inode = afs_try_auto_mntpt(dentry, dir, &fid);
key_put(key); if (!IS_ERR(inode)) {
goto success; key_put(key);
goto success;
}
ret = PTR_ERR(inode);
} }
ret = PTR_ERR(inode);
key_put(key); key_put(key);
if (ret == -ENOENT) { if (ret == -ENOENT) {
d_add(dentry, NULL); d_add(dentry, NULL);
...@@ -582,6 +632,46 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, ...@@ -582,6 +632,46 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
return NULL; 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 * 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 * - 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, ...@@ -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) 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_vnode *vnode, *dir;
struct afs_fid uninitialized_var(fid); struct afs_fid uninitialized_var(fid);
struct dentry *parent; struct dentry *parent;
...@@ -600,6 +691,9 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags) ...@@ -600,6 +691,9 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
if (flags & LOOKUP_RCU) if (flags & LOOKUP_RCU)
return -ECHILD; return -ECHILD;
if (as->dyn_root)
return 1;
if (d_really_is_positive(dentry)) { if (d_really_is_positive(dentry)) {
vnode = AFS_FS_I(d_inode(dentry)); vnode = AFS_FS_I(d_inode(dentry));
_enter("{v={%x:%u} n=%pd fl=%lx},", _enter("{v={%x:%u} n=%pd fl=%lx},",
......
...@@ -147,7 +147,7 @@ int afs_iget5_test(struct inode *inode, void *opaque) ...@@ -147,7 +147,7 @@ int afs_iget5_test(struct inode *inode, void *opaque)
* *
* These pseudo inodes don't match anything. * 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; return 0;
} }
...@@ -169,31 +169,34 @@ static int afs_iget5_set(struct inode *inode, void *opaque) ...@@ -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, struct inode *afs_iget_pseudo_dir(struct super_block *sb, bool root)
int namesz, struct key *key)
{ {
struct afs_iget_data data; struct afs_iget_data data;
struct afs_super_info *as; struct afs_super_info *as;
struct afs_vnode *vnode; struct afs_vnode *vnode;
struct super_block *sb;
struct inode *inode; struct inode *inode;
static atomic_t afs_autocell_ino; static atomic_t afs_autocell_ino;
_enter("{%x:%u},%*.*s,", _enter("");
AFS_FS_I(dir)->fid.vid, AFS_FS_I(dir)->fid.vnode,
namesz, namesz, dev_name ?: "");
sb = dir->i_sb;
as = sb->s_fs_info; as = sb->s_fs_info;
data.volume = as->volume; if (as->volume) {
data.fid.vid = as->volume->vid; data.volume = as->volume;
data.fid.unique = 0; data.fid.vid = as->volume->vid;
data.fid.vnode = 0; }
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), inode = iget5_locked(sb, data.fid.vnode,
afs_iget5_autocell_test, afs_iget5_set, afs_iget5_pseudo_dir_test, afs_iget5_set,
&data); &data);
if (!inode) { if (!inode) {
_leave(" = -ENOMEM"); _leave(" = -ENOMEM");
...@@ -211,7 +214,12 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name, ...@@ -211,7 +214,12 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name,
inode->i_size = 0; inode->i_size = 0;
inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; 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); set_nlink(inode, 2);
inode->i_uid = GLOBAL_ROOT_UID; inode->i_uid = GLOBAL_ROOT_UID;
inode->i_gid = GLOBAL_ROOT_GID; inode->i_gid = GLOBAL_ROOT_GID;
...@@ -223,8 +231,12 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name, ...@@ -223,8 +231,12 @@ struct inode *afs_iget_autocell(struct inode *dir, const char *dev_name,
inode->i_generation = 0; inode->i_generation = 0;
set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags); set_bit(AFS_VNODE_PSEUDODIR, &vnode->flags);
set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags); if (!root) {
inode->i_flags |= S_AUTOMOUNT | S_NOATIME; set_bit(AFS_VNODE_MOUNTPOINT, &vnode->flags);
inode->i_flags |= S_AUTOMOUNT;
}
inode->i_flags |= S_NOATIME;
unlock_new_inode(inode); unlock_new_inode(inode);
_leave(" = %p", inode); _leave(" = %p", inode);
return inode; return inode;
......
...@@ -36,6 +36,7 @@ struct afs_mount_params { ...@@ -36,6 +36,7 @@ struct afs_mount_params {
bool rwpath; /* T if the parent should be considered R/W */ bool rwpath; /* T if the parent should be considered R/W */
bool force; /* T to force cell type */ bool force; /* T to force cell type */
bool autocell; /* T if set auto mount operation */ bool autocell; /* T if set auto mount operation */
bool dyn_root; /* T if dynamic root */
afs_voltype_t type; /* type of volume requested */ afs_voltype_t type; /* type of volume requested */
int volnamesz; /* size of volume name */ int volnamesz; /* size of volume name */
const char *volname; /* name of volume to mount */ const char *volname; /* name of volume to mount */
...@@ -186,6 +187,7 @@ struct afs_super_info { ...@@ -186,6 +187,7 @@ struct afs_super_info {
struct afs_net *net; /* Network namespace */ struct afs_net *net; /* Network namespace */
struct afs_cell *cell; /* The cell in which the volume resides */ struct afs_cell *cell; /* The cell in which the volume resides */
struct afs_volume *volume; /* volume record */ 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) 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 *); ...@@ -634,10 +636,13 @@ extern bool afs_cm_incoming_call(struct afs_call *);
/* /*
* dir.c * 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 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 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 * file.c
...@@ -695,8 +700,7 @@ extern int afs_fs_get_capabilities(struct afs_net *, struct afs_server *, ...@@ -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_fetch_status(struct afs_vnode *, struct key *);
extern int afs_iget5_test(struct inode *, void *); extern int afs_iget5_test(struct inode *, void *);
extern struct inode *afs_iget_autocell(struct inode *, const char *, int, extern struct inode *afs_iget_pseudo_dir(struct super_block *, bool);
struct key *);
extern struct inode *afs_iget(struct super_block *, struct key *, extern struct inode *afs_iget(struct super_block *, struct key *,
struct afs_fid *, struct afs_file_status *, struct afs_fid *, struct afs_file_status *,
struct afs_callback *, struct afs_callback *,
......
...@@ -72,7 +72,7 @@ static int afs_mntpt_open(struct inode *inode, struct file *file) ...@@ -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) static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
{ {
struct afs_super_info *super; struct afs_super_info *as;
struct vfsmount *mnt; struct vfsmount *mnt;
struct afs_vnode *vnode; struct afs_vnode *vnode;
struct page *page; struct page *page;
...@@ -104,13 +104,13 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) ...@@ -104,13 +104,13 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
goto error_no_page; goto error_no_page;
if (mntpt->d_name.name[0] == '.') { if (mntpt->d_name.name[0] == '.') {
devname[0] = '#'; devname[0] = '%';
memcpy(devname + 1, mntpt->d_name.name, size - 1); memcpy(devname + 1, mntpt->d_name.name + 1, size - 1);
memcpy(devname + size, afs_root_cell, memcpy(devname + size, afs_root_cell,
sizeof(afs_root_cell)); sizeof(afs_root_cell));
rwpath = true; rwpath = true;
} else { } else {
devname[0] = '%'; devname[0] = '#';
memcpy(devname + 1, mntpt->d_name.name, size); memcpy(devname + 1, mntpt->d_name.name, size);
memcpy(devname + size + 1, afs_root_cell, memcpy(devname + size + 1, afs_root_cell,
sizeof(afs_root_cell)); sizeof(afs_root_cell));
...@@ -142,11 +142,13 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt) ...@@ -142,11 +142,13 @@ static struct vfsmount *afs_mntpt_do_automount(struct dentry *mntpt)
} }
/* work out what options we want */ /* work out what options we want */
super = AFS_FS_S(mntpt->d_sb); as = AFS_FS_S(mntpt->d_sb);
memcpy(options, "cell=", 5); if (as->cell) {
strcpy(options + 5, super->volume->cell->name); memcpy(options, "cell=", 5);
if (super->volume->type == AFSVL_RWVOL || rwpath) strcpy(options + 5, as->cell->name);
strcat(options, ",rwpath"); if ((as->volume && as->volume->type == AFSVL_RWVOL) || rwpath)
strcat(options, ",rwpath");
}
/* try and do the mount */ /* try and do the mount */
_debug("--- attempting mount %s -o %s ---", devname, options); _debug("--- attempting mount %s -o %s ---", devname, options);
......
...@@ -330,26 +330,6 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc) ...@@ -330,26 +330,6 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
if (!afs_start_fs_iteration(fc, vnode)) if (!afs_start_fs_iteration(fc, vnode))
goto failed; goto failed;
goto use_server;
next_server:
_debug("next");
afs_put_cb_interest(afs_v2net(vnode), fc->cbi);
fc->cbi = NULL;
fc->index++;
if (fc->index >= fc->server_list->nr_servers)
fc->index = 0;
if (fc->index != fc->start)
goto use_server;
/* That's all the servers poked to no good effect. Try again if some
* of them were busy.
*/
if (fc->flags & AFS_FS_CURSOR_VBUSY)
goto restart_from_beginning;
fc->ac.error = -EDESTADDRREQ;
goto failed;
use_server: use_server:
_debug("use"); _debug("use");
...@@ -383,6 +363,7 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc) ...@@ -383,6 +363,7 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
afs_get_addrlist(alist); afs_get_addrlist(alist);
read_unlock(&server->fs_lock); read_unlock(&server->fs_lock);
memset(&fc->ac, 0, sizeof(fc->ac));
/* Probe the current fileserver if we haven't done so yet. */ /* Probe the current fileserver if we haven't done so yet. */
if (!test_bit(AFS_SERVER_FL_PROBED, &server->flags)) { if (!test_bit(AFS_SERVER_FL_PROBED, &server->flags)) {
...@@ -397,12 +378,8 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc) ...@@ -397,12 +378,8 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
else else
afs_put_addrlist(alist); afs_put_addrlist(alist);
fc->ac.addr = NULL;
fc->ac.start = READ_ONCE(alist->index); fc->ac.start = READ_ONCE(alist->index);
fc->ac.index = fc->ac.start; fc->ac.index = fc->ac.start;
fc->ac.error = 0;
fc->ac.begun = false;
goto iterate_address;
iterate_address: iterate_address:
ASSERT(fc->ac.alist); ASSERT(fc->ac.alist);
...@@ -410,16 +387,35 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc) ...@@ -410,16 +387,35 @@ bool afs_select_fileserver(struct afs_fs_cursor *fc)
/* Iterate over the current server's address list to try and find an /* Iterate over the current server's address list to try and find an
* address on which it will respond to us. * address on which it will respond to us.
*/ */
if (afs_iterate_addresses(&fc->ac)) { if (!afs_iterate_addresses(&fc->ac))
_leave(" = t"); goto next_server;
return true;
} _leave(" = t");
return true;
next_server:
_debug("next");
afs_end_cursor(&fc->ac); afs_end_cursor(&fc->ac);
goto next_server; afs_put_cb_interest(afs_v2net(vnode), fc->cbi);
fc->cbi = NULL;
fc->index++;
if (fc->index >= fc->server_list->nr_servers)
fc->index = 0;
if (fc->index != fc->start)
goto use_server;
/* That's all the servers poked to no good effect. Try again if some
* of them were busy.
*/
if (fc->flags & AFS_FS_CURSOR_VBUSY)
goto restart_from_beginning;
fc->ac.error = -EDESTADDRREQ;
goto failed;
failed: failed:
fc->flags |= AFS_FS_CURSOR_STOP; fc->flags |= AFS_FS_CURSOR_STOP;
afs_end_cursor(&fc->ac);
_leave(" = f [failed %d]", fc->ac.error); _leave(" = f [failed %d]", fc->ac.error);
return false; return false;
} }
...@@ -458,12 +454,10 @@ bool afs_select_current_fileserver(struct afs_fs_cursor *fc) ...@@ -458,12 +454,10 @@ bool afs_select_current_fileserver(struct afs_fs_cursor *fc)
return false; return false;
} }
memset(&fc->ac, 0, sizeof(fc->ac));
fc->ac.alist = alist; fc->ac.alist = alist;
fc->ac.addr = NULL;
fc->ac.start = READ_ONCE(alist->index); fc->ac.start = READ_ONCE(alist->index);
fc->ac.index = fc->ac.start; fc->ac.index = fc->ac.start;
fc->ac.error = 0;
fc->ac.begun = false;
goto iterate_address; goto iterate_address;
case 0: case 0:
...@@ -520,238 +514,3 @@ int afs_end_vnode_operation(struct afs_fs_cursor *fc) ...@@ -520,238 +514,3 @@ int afs_end_vnode_operation(struct afs_fs_cursor *fc)
return fc->ac.error; return fc->ac.error;
} }
#if 0
/*
* Set a filesystem server cursor for using a specific FS server.
*/
int afs_set_fs_cursor(struct afs_fs_cursor *fc, struct afs_vnode *vnode)
{
afs_init_fs_cursor(fc, vnode);
read_seqlock_excl(&vnode->cb_lock);
if (vnode->cb_interest) {
if (vnode->cb_interest->server->fs_state == 0)
fc->server = afs_get_server(vnode->cb_interest->server);
else
fc->ac.error = vnode->cb_interest->server->fs_state;
} else {
fc->ac.error = -ESTALE;
}
read_sequnlock_excl(&vnode->cb_lock);
return fc->ac.error;
}
/*
* pick a server to use to try accessing this volume
* - returns with an elevated usage count on the server chosen
*/
bool afs_volume_pick_fileserver(struct afs_fs_cursor *fc, struct afs_vnode *vnode)
{
struct afs_volume *volume = vnode->volume;
struct afs_server *server;
int ret, state, loop;
_enter("%s", volume->vlocation->vldb.name);
/* stick with the server we're already using if we can */
if (vnode->cb_interest && vnode->cb_interest->server->fs_state == 0) {
fc->server = afs_get_server(vnode->cb_interest->server);
goto set_server;
}
down_read(&volume->server_sem);
/* handle the no-server case */
if (volume->nservers == 0) {
fc->ac.error = volume->rjservers ? -ENOMEDIUM : -ESTALE;
up_read(&volume->server_sem);
_leave(" = f [no servers %d]", fc->ac.error);
return false;
}
/* basically, just search the list for the first live server and use
* that */
ret = 0;
for (loop = 0; loop < volume->nservers; loop++) {
server = volume->servers[loop];
state = server->fs_state;
_debug("consider %d [%d]", loop, state);
switch (state) {
case 0:
goto picked_server;
case -ENETUNREACH:
if (ret == 0)
ret = state;
break;
case -EHOSTUNREACH:
if (ret == 0 ||
ret == -ENETUNREACH)
ret = state;
break;
case -ECONNREFUSED:
if (ret == 0 ||
ret == -ENETUNREACH ||
ret == -EHOSTUNREACH)
ret = state;
break;
default:
case -EREMOTEIO:
if (ret == 0 ||
ret == -ENETUNREACH ||
ret == -EHOSTUNREACH ||
ret == -ECONNREFUSED)
ret = state;
break;
}
}
error:
fc->ac.error = ret;
/* no available servers
* - TODO: handle the no active servers case better
*/
up_read(&volume->server_sem);
_leave(" = f [%d]", fc->ac.error);
return false;
picked_server:
/* Found an apparently healthy server. We need to register an interest
* in receiving callbacks before we talk to it.
*/
ret = afs_register_server_cb_interest(vnode,
&volume->cb_interests[loop], server);
if (ret < 0)
goto error;
fc->server = afs_get_server(server);
up_read(&volume->server_sem);
set_server:
fc->ac.alist = afs_get_addrlist(fc->server->addrs);
fc->ac.addr = &fc->ac.alist->addrs[0];
_debug("USING SERVER: %pIS\n", &fc->ac.addr->transport);
_leave(" = t (picked %pIS)", &fc->ac.addr->transport);
return true;
}
/*
* release a server after use
* - releases the ref on the server struct that was acquired by picking
* - records result of using a particular server to access a volume
* - return true to try again, false if okay or to issue error
* - the caller must release the server struct if result was false
*/
bool afs_iterate_fs_cursor(struct afs_fs_cursor *fc,
struct afs_vnode *vnode)
{
struct afs_volume *volume = vnode->volume;
struct afs_server *server = fc->server;
unsigned loop;
_enter("%s,%pIS,%d",
volume->vlocation->vldb.name, &fc->ac.addr->transport,
fc->ac.error);
switch (fc->ac.error) {
/* success */
case 0:
server->fs_state = 0;
_leave(" = f");
return false;
/* the fileserver denied all knowledge of the volume */
case -ENOMEDIUM:
down_write(&volume->server_sem);
/* firstly, find where the server is in the active list (if it
* is) */
for (loop = 0; loop < volume->nservers; loop++)
if (volume->servers[loop] == server)
goto present;
/* no longer there - may have been discarded by another op */
goto try_next_server_upw;
present:
volume->nservers--;
memmove(&volume->servers[loop],
&volume->servers[loop + 1],
sizeof(volume->servers[loop]) *
(volume->nservers - loop));
volume->servers[volume->nservers] = NULL;
afs_put_server(afs_v2net(vnode), server);
volume->rjservers++;
if (volume->nservers > 0)
/* another server might acknowledge its existence */
goto try_next_server_upw;
/* handle the case where all the fileservers have rejected the
* volume
* - TODO: try asking the fileservers for volume information
* - TODO: contact the VL server again to see if the volume is
* no longer registered
*/
up_write(&volume->server_sem);
afs_put_server(afs_v2net(vnode), server);
fc->server = NULL;
_leave(" = f [completely rejected]");
return false;
/* problem reaching the server */
case -ENETUNREACH:
case -EHOSTUNREACH:
case -ECONNREFUSED:
case -ETIME:
case -ETIMEDOUT:
case -EREMOTEIO:
/* mark the server as dead
* TODO: vary dead timeout depending on error
*/
spin_lock(&server->fs_lock);
if (!server->fs_state) {
server->fs_state = fc->ac.error;
printk("kAFS: SERVER DEAD state=%d\n", fc->ac.error);
}
spin_unlock(&server->fs_lock);
goto try_next_server;
/* miscellaneous error */
default:
case -ENOMEM:
case -ENONET:
/* tell the caller to accept the result */
afs_put_server(afs_v2net(vnode), server);
fc->server = NULL;
_leave(" = f [local failure]");
return false;
}
/* tell the caller to loop around and try the next server */
try_next_server_upw:
up_write(&volume->server_sem);
try_next_server:
afs_put_server(afs_v2net(vnode), server);
_leave(" = t [try next server]");
return true;
}
/*
* Clean up a fileserver cursor.
*/
int afs_end_fs_cursor(struct afs_fs_cursor *fc, struct afs_net *net)
{
afs_end_cursor(&fc->ac);
afs_put_server(net, fc->server);
return fc->ac.error;
}
#endif
...@@ -58,7 +58,8 @@ struct afs_server_list *afs_alloc_server_list(struct afs_cell *cell, ...@@ -58,7 +58,8 @@ struct afs_server_list *afs_alloc_server_list(struct afs_cell *cell,
server = afs_lookup_server(cell, key, &vldb->fs_server[i]); server = afs_lookup_server(cell, key, &vldb->fs_server[i]);
if (IS_ERR(server)) { if (IS_ERR(server)) {
ret = PTR_ERR(server); ret = PTR_ERR(server);
if (ret == -ENOENT) if (ret == -ENOENT ||
ret == -ENOMEDIUM)
continue; continue;
goto error_2; goto error_2;
} }
......
...@@ -64,6 +64,7 @@ static atomic_t afs_count_active_inodes; ...@@ -64,6 +64,7 @@ static atomic_t afs_count_active_inodes;
enum { enum {
afs_no_opt, afs_no_opt,
afs_opt_cell, afs_opt_cell,
afs_opt_dyn,
afs_opt_rwpath, afs_opt_rwpath,
afs_opt_vol, afs_opt_vol,
afs_opt_autocell, afs_opt_autocell,
...@@ -71,6 +72,7 @@ enum { ...@@ -71,6 +72,7 @@ enum {
static const match_table_t afs_options_list = { static const match_table_t afs_options_list = {
{ afs_opt_cell, "cell=%s" }, { afs_opt_cell, "cell=%s" },
{ afs_opt_dyn, "dyn" },
{ afs_opt_rwpath, "rwpath" }, { afs_opt_rwpath, "rwpath" },
{ afs_opt_vol, "vol=%s" }, { afs_opt_vol, "vol=%s" },
{ afs_opt_autocell, "autocell" }, { afs_opt_autocell, "autocell" },
...@@ -148,6 +150,11 @@ static int afs_show_devname(struct seq_file *m, struct dentry *root) ...@@ -148,6 +150,11 @@ static int afs_show_devname(struct seq_file *m, struct dentry *root)
const char *suf = ""; const char *suf = "";
char pref = '%'; char pref = '%';
if (as->dyn_root) {
seq_puts(m, "none");
return 0;
}
switch (volume->type) { switch (volume->type) {
case AFSVL_RWVOL: case AFSVL_RWVOL:
break; break;
...@@ -171,8 +178,12 @@ static int afs_show_devname(struct seq_file *m, struct dentry *root) ...@@ -171,8 +178,12 @@ static int afs_show_devname(struct seq_file *m, struct dentry *root)
*/ */
static int afs_show_options(struct seq_file *m, struct dentry *root) static int afs_show_options(struct seq_file *m, struct dentry *root)
{ {
struct afs_super_info *as = AFS_FS_S(root->d_sb);
if (as->dyn_root)
seq_puts(m, ",dyn");
if (test_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(d_inode(root))->flags)) if (test_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(d_inode(root))->flags))
seq_puts(m, "autocell"); seq_puts(m, ",autocell");
return 0; return 0;
} }
...@@ -212,7 +223,7 @@ static int afs_parse_options(struct afs_mount_params *params, ...@@ -212,7 +223,7 @@ static int afs_parse_options(struct afs_mount_params *params,
break; break;
case afs_opt_rwpath: case afs_opt_rwpath:
params->rwpath = 1; params->rwpath = true;
break; break;
case afs_opt_vol: case afs_opt_vol:
...@@ -220,7 +231,11 @@ static int afs_parse_options(struct afs_mount_params *params, ...@@ -220,7 +231,11 @@ static int afs_parse_options(struct afs_mount_params *params,
break; break;
case afs_opt_autocell: case afs_opt_autocell:
params->autocell = 1; params->autocell = true;
break;
case afs_opt_dyn:
params->dyn_root = true;
break; break;
default: default:
...@@ -254,7 +269,7 @@ static int afs_parse_device_name(struct afs_mount_params *params, ...@@ -254,7 +269,7 @@ static int afs_parse_device_name(struct afs_mount_params *params,
int cellnamesz; int cellnamesz;
_enter(",%s", name); _enter(",%s", name);
if (!name) { if (!name) {
printk(KERN_ERR "kAFS: no volume name specified\n"); printk(KERN_ERR "kAFS: no volume name specified\n");
return -EINVAL; return -EINVAL;
...@@ -336,7 +351,14 @@ static int afs_test_super(struct super_block *sb, void *data) ...@@ -336,7 +351,14 @@ static int afs_test_super(struct super_block *sb, void *data)
struct afs_super_info *as1 = data; struct afs_super_info *as1 = data;
struct afs_super_info *as = AFS_FS_S(sb); struct afs_super_info *as = AFS_FS_S(sb);
return as->net == as1->net && as->volume->vid == as1->volume->vid; return (as->net == as1->net &&
as->volume &&
as->volume->vid == as1->volume->vid);
}
static int afs_dynroot_test_super(struct super_block *sb, void *data)
{
return false;
} }
static int afs_set_super(struct super_block *sb, void *data) static int afs_set_super(struct super_block *sb, void *data)
...@@ -365,24 +387,30 @@ static int afs_fill_super(struct super_block *sb, ...@@ -365,24 +387,30 @@ static int afs_fill_super(struct super_block *sb,
sb->s_blocksize_bits = PAGE_SHIFT; sb->s_blocksize_bits = PAGE_SHIFT;
sb->s_magic = AFS_FS_MAGIC; sb->s_magic = AFS_FS_MAGIC;
sb->s_op = &afs_super_ops; sb->s_op = &afs_super_ops;
sb->s_xattr = afs_xattr_handlers; if (!as->dyn_root)
sb->s_xattr = afs_xattr_handlers;
ret = super_setup_bdi(sb); ret = super_setup_bdi(sb);
if (ret) if (ret)
return ret; return ret;
sb->s_bdi->ra_pages = VM_MAX_READAHEAD * 1024 / PAGE_SIZE; sb->s_bdi->ra_pages = VM_MAX_READAHEAD * 1024 / PAGE_SIZE;
sprintf(sb->s_id, "%u", as->volume->vid);
afs_activate_volume(as->volume);
/* allocate the root inode and dentry */ /* allocate the root inode and dentry */
fid.vid = as->volume->vid; if (as->dyn_root) {
fid.vnode = 1; inode = afs_iget_pseudo_dir(sb, true);
fid.unique = 1; sb->s_flags |= SB_RDONLY;
inode = afs_iget(sb, params->key, &fid, NULL, NULL, NULL); } else {
sprintf(sb->s_id, "%u", as->volume->vid);
afs_activate_volume(as->volume);
fid.vid = as->volume->vid;
fid.vnode = 1;
fid.unique = 1;
inode = afs_iget(sb, params->key, &fid, NULL, NULL, NULL);
}
if (IS_ERR(inode)) if (IS_ERR(inode))
return PTR_ERR(inode); return PTR_ERR(inode);
if (params->autocell) if (params->autocell || params->dyn_root)
set_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(inode)->flags); set_bit(AFS_VNODE_AUTOCELL, &AFS_FS_I(inode)->flags);
ret = -ENOMEM; ret = -ENOMEM;
...@@ -407,7 +435,10 @@ static struct afs_super_info *afs_alloc_sbi(struct afs_mount_params *params) ...@@ -407,7 +435,10 @@ static struct afs_super_info *afs_alloc_sbi(struct afs_mount_params *params)
as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL); as = kzalloc(sizeof(struct afs_super_info), GFP_KERNEL);
if (as) { if (as) {
as->net = afs_get_net(params->net); as->net = afs_get_net(params->net);
as->cell = afs_get_cell(params->cell); if (params->dyn_root)
as->dyn_root = true;
else
as->cell = afs_get_cell(params->cell);
} }
return as; return as;
} }
...@@ -451,18 +482,20 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, ...@@ -451,18 +482,20 @@ static struct dentry *afs_mount(struct file_system_type *fs_type,
goto error; goto error;
} }
ret = afs_parse_device_name(&params, dev_name); if (!params.dyn_root) {
if (ret < 0) ret = afs_parse_device_name(&params, dev_name);
goto error; if (ret < 0)
goto error;
/* try and do the mount securely */ /* try and do the mount securely */
key = afs_request_key(params.cell); key = afs_request_key(params.cell);
if (IS_ERR(key)) { if (IS_ERR(key)) {
_leave(" = %ld [key]", PTR_ERR(key)); _leave(" = %ld [key]", PTR_ERR(key));
ret = PTR_ERR(key); ret = PTR_ERR(key);
goto error; goto error;
}
params.key = key;
} }
params.key = key;
/* allocate a superblock info record */ /* allocate a superblock info record */
ret = -ENOMEM; ret = -ENOMEM;
...@@ -470,20 +503,25 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, ...@@ -470,20 +503,25 @@ static struct dentry *afs_mount(struct file_system_type *fs_type,
if (!as) if (!as)
goto error_key; goto error_key;
/* Assume we're going to need a volume record; at the very least we can if (!params.dyn_root) {
* use it to update the volume record if we have one already. This /* Assume we're going to need a volume record; at the very
* checks that the volume exists within the cell. * least we can use it to update the volume record if we have
*/ * one already. This checks that the volume exists within the
candidate = afs_create_volume(&params); * cell.
if (IS_ERR(candidate)) { */
ret = PTR_ERR(candidate); candidate = afs_create_volume(&params);
goto error_as; if (IS_ERR(candidate)) {
} ret = PTR_ERR(candidate);
goto error_as;
}
as->volume = candidate; as->volume = candidate;
}
/* allocate a deviceless superblock */ /* allocate a deviceless superblock */
sb = sget(fs_type, afs_test_super, afs_set_super, flags, as); sb = sget(fs_type,
as->dyn_root ? afs_dynroot_test_super : afs_test_super,
afs_set_super, flags, as);
if (IS_ERR(sb)) { if (IS_ERR(sb)) {
ret = PTR_ERR(sb); ret = PTR_ERR(sb);
goto error_as; goto error_as;
...@@ -529,9 +567,11 @@ static void afs_kill_super(struct super_block *sb) ...@@ -529,9 +567,11 @@ static void afs_kill_super(struct super_block *sb)
/* Clear the callback interests (which will do ilookup5) before /* Clear the callback interests (which will do ilookup5) before
* deactivating the superblock. * deactivating the superblock.
*/ */
afs_clear_callback_interests(as->net, as->volume->servers); if (as->volume)
afs_clear_callback_interests(as->net, as->volume->servers);
kill_anon_super(sb); kill_anon_super(sb);
afs_deactivate_volume(as->volume); if (as->volume)
afs_deactivate_volume(as->volume);
afs_destroy_sbi(as); afs_destroy_sbi(as);
} }
...@@ -619,12 +659,24 @@ static void afs_destroy_inode(struct inode *inode) ...@@ -619,12 +659,24 @@ static void afs_destroy_inode(struct inode *inode)
*/ */
static int afs_statfs(struct dentry *dentry, struct kstatfs *buf) static int afs_statfs(struct dentry *dentry, struct kstatfs *buf)
{ {
struct afs_super_info *as = AFS_FS_S(dentry->d_sb);
struct afs_fs_cursor fc; struct afs_fs_cursor fc;
struct afs_volume_status vs; struct afs_volume_status vs;
struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry)); struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
struct key *key; struct key *key;
int ret; int ret;
buf->f_type = dentry->d_sb->s_magic;
buf->f_bsize = AFS_BLOCK_SIZE;
buf->f_namelen = AFSNAMEMAX - 1;
if (as->dyn_root) {
buf->f_blocks = 1;
buf->f_bavail = 0;
buf->f_bfree = 0;
return 0;
}
key = afs_request_key(vnode->volume->cell); key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) if (IS_ERR(key))
return PTR_ERR(key); return PTR_ERR(key);
...@@ -645,10 +697,6 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf) ...@@ -645,10 +697,6 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf)
key_put(key); key_put(key);
if (ret == 0) { if (ret == 0) {
buf->f_type = dentry->d_sb->s_magic;
buf->f_bsize = AFS_BLOCK_SIZE;
buf->f_namelen = AFSNAMEMAX - 1;
if (vs.max_quota == 0) if (vs.max_quota == 0)
buf->f_blocks = vs.part_max_blocks; buf->f_blocks = vs.part_max_blocks;
else else
......
...@@ -23,7 +23,7 @@ static int afs_deliver_vl_get_entry_by_name_u(struct afs_call *call) ...@@ -23,7 +23,7 @@ static int afs_deliver_vl_get_entry_by_name_u(struct afs_call *call)
struct afs_uvldbentry__xdr *uvldb; struct afs_uvldbentry__xdr *uvldb;
struct afs_vldb_entry *entry; struct afs_vldb_entry *entry;
bool new_only = false; bool new_only = false;
u32 tmp; u32 tmp, nr_servers;
int i, ret; int i, ret;
_enter(""); _enter("");
...@@ -36,6 +36,10 @@ static int afs_deliver_vl_get_entry_by_name_u(struct afs_call *call) ...@@ -36,6 +36,10 @@ static int afs_deliver_vl_get_entry_by_name_u(struct afs_call *call)
uvldb = call->buffer; uvldb = call->buffer;
entry = call->reply[0]; entry = call->reply[0];
nr_servers = ntohl(uvldb->nServers);
if (nr_servers > AFS_NMAXNSERVERS)
nr_servers = AFS_NMAXNSERVERS;
for (i = 0; i < ARRAY_SIZE(uvldb->name) - 1; i++) for (i = 0; i < ARRAY_SIZE(uvldb->name) - 1; i++)
entry->name[i] = (u8)ntohl(uvldb->name[i]); entry->name[i] = (u8)ntohl(uvldb->name[i]);
entry->name[i] = 0; entry->name[i] = 0;
...@@ -44,14 +48,14 @@ static int afs_deliver_vl_get_entry_by_name_u(struct afs_call *call) ...@@ -44,14 +48,14 @@ static int afs_deliver_vl_get_entry_by_name_u(struct afs_call *call)
/* If there is a new replication site that we can use, ignore all the /* If there is a new replication site that we can use, ignore all the
* sites that aren't marked as new. * sites that aren't marked as new.
*/ */
for (i = 0; i < AFS_NMAXNSERVERS; i++) { for (i = 0; i < nr_servers; i++) {
tmp = ntohl(uvldb->serverFlags[i]); tmp = ntohl(uvldb->serverFlags[i]);
if (!(tmp & AFS_VLSF_DONTUSE) && if (!(tmp & AFS_VLSF_DONTUSE) &&
(tmp & AFS_VLSF_NEWREPSITE)) (tmp & AFS_VLSF_NEWREPSITE))
new_only = true; new_only = true;
} }
for (i = 0; i < AFS_NMAXNSERVERS; i++) { for (i = 0; i < nr_servers; i++) {
struct afs_uuid__xdr *xdr; struct afs_uuid__xdr *xdr;
struct afs_uuid *uuid; struct afs_uuid *uuid;
int j; int j;
......
...@@ -26,9 +26,8 @@ static struct afs_volume *afs_alloc_volume(struct afs_mount_params *params, ...@@ -26,9 +26,8 @@ static struct afs_volume *afs_alloc_volume(struct afs_mount_params *params,
unsigned long type_mask) unsigned long type_mask)
{ {
struct afs_server_list *slist; struct afs_server_list *slist;
struct afs_server *server;
struct afs_volume *volume; struct afs_volume *volume;
int ret = -ENOMEM, nr_servers = 0, i, j; int ret = -ENOMEM, nr_servers = 0, i;
for (i = 0; i < vldb->nr_servers; i++) for (i = 0; i < vldb->nr_servers; i++)
if (vldb->fs_mask[i] & type_mask) if (vldb->fs_mask[i] & type_mask)
...@@ -58,50 +57,10 @@ static struct afs_volume *afs_alloc_volume(struct afs_mount_params *params, ...@@ -58,50 +57,10 @@ static struct afs_volume *afs_alloc_volume(struct afs_mount_params *params,
refcount_set(&slist->usage, 1); refcount_set(&slist->usage, 1);
volume->servers = slist; volume->servers = slist;
/* Make sure a records exists for each server this volume occupies. */
for (i = 0; i < nr_servers; i++) {
if (!(vldb->fs_mask[i] & type_mask))
continue;
server = afs_lookup_server(params->cell, params->key,
&vldb->fs_server[i]);
if (IS_ERR(server)) {
ret = PTR_ERR(server);
if (ret == -ENOENT)
continue;
goto error_2;
}
/* Insertion-sort by server pointer */
for (j = 0; j < slist->nr_servers; j++)
if (slist->servers[j].server >= server)
break;
if (j < slist->nr_servers) {
if (slist->servers[j].server == server) {
afs_put_server(params->net, server);
continue;
}
memmove(slist->servers + j + 1,
slist->servers + j,
(slist->nr_servers - j) * sizeof(struct afs_server_entry));
}
slist->servers[j].server = server;
slist->nr_servers++;
}
if (slist->nr_servers == 0) {
ret = -EDESTADDRREQ;
goto error_2;
}
return volume; return volume;
error_2:
afs_put_serverlist(params->net, slist);
error_1: error_1:
afs_put_cell(params->net, volume->cell);
kfree(volume); kfree(volume);
error_0: error_0:
return ERR_PTR(ret); return ERR_PTR(ret);
...@@ -327,7 +286,7 @@ static int afs_update_volume_status(struct afs_volume *volume, struct key *key) ...@@ -327,7 +286,7 @@ static int afs_update_volume_status(struct afs_volume *volume, struct key *key)
/* See if the volume's server list got updated. */ /* See if the volume's server list got updated. */
new = afs_alloc_server_list(volume->cell, key, new = afs_alloc_server_list(volume->cell, key,
vldb, (1 << volume->type)); vldb, (1 << volume->type));
if (IS_ERR(new)) { if (IS_ERR(new)) {
ret = PTR_ERR(new); ret = PTR_ERR(new);
goto error_vldb; goto error_vldb;
......
...@@ -52,11 +52,11 @@ ...@@ -52,11 +52,11 @@
* @name: Name to look up * @name: Name to look up
* @namelen: Length of name * @namelen: Length of name
* @options: Request options (or NULL if no options) * @options: Request options (or NULL if no options)
* @_result: Where to place the returned data. * @_result: Where to place the returned data (or NULL)
* @_expiry: Where to store the result expiry time (or NULL) * @_expiry: Where to store the result expiry time (or NULL)
* *
* The data will be returned in the pointer at *result, and the caller is * The data will be returned in the pointer at *result, if provided, and the
* responsible for freeing it. * caller is responsible for freeing it.
* *
* The description should be of the form "[<query_type>:]<domain_name>", and * The description should be of the form "[<query_type>:]<domain_name>", and
* the options need to be appropriate for the query type requested. If no * the options need to be appropriate for the query type requested. If no
...@@ -81,7 +81,7 @@ int dns_query(const char *type, const char *name, size_t namelen, ...@@ -81,7 +81,7 @@ int dns_query(const char *type, const char *name, size_t namelen,
kenter("%s,%*.*s,%zu,%s", kenter("%s,%*.*s,%zu,%s",
type, (int)namelen, (int)namelen, name, namelen, options); type, (int)namelen, (int)namelen, name, namelen, options);
if (!name || namelen == 0 || !_result) if (!name || namelen == 0)
return -EINVAL; return -EINVAL;
/* construct the query key description as "[<type>:]<name>" */ /* construct the query key description as "[<type>:]<name>" */
...@@ -146,13 +146,15 @@ int dns_query(const char *type, const char *name, size_t namelen, ...@@ -146,13 +146,15 @@ int dns_query(const char *type, const char *name, size_t namelen,
upayload = user_key_payload_locked(rkey); upayload = user_key_payload_locked(rkey);
len = upayload->datalen; len = upayload->datalen;
ret = -ENOMEM; if (_result) {
*_result = kmalloc(len + 1, GFP_KERNEL); ret = -ENOMEM;
if (!*_result) *_result = kmalloc(len + 1, GFP_KERNEL);
goto put; if (!*_result)
goto put;
memcpy(*_result, upayload->data, len); memcpy(*_result, upayload->data, len);
(*_result)[len] = '\0'; (*_result)[len] = '\0';
}
if (_expiry) if (_expiry)
*_expiry = rkey->expiry; *_expiry = rkey->expiry;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment