Commit 35773c93 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'afs-proc' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull AFS updates from Al Viro:
 "Assorted AFS stuff - ended up in vfs.git since most of that consists
  of David's AFS-related followups to Christoph's procfs series"

* 'afs-proc' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  afs: Optimise callback breaking by not repeating volume lookup
  afs: Display manually added cells in dynamic root mount
  afs: Enable IPv6 DNS lookups
  afs: Show all of a server's addresses in /proc/fs/afs/servers
  afs: Handle CONFIG_PROC_FS=n
  proc: Make inline name size calculation automatic
  afs: Implement network namespacing
  afs: Mark afs_net::ws_cell as __rcu and set using rcu functions
  afs: Fix a Sparse warning in xdr_decode_AFSFetchStatus()
  proc: Add a way to make network proc files writable
  afs: Rearrange fs/afs/proc.c to remove remaining predeclarations.
  afs: Rearrange fs/afs/proc.c to move the show routines up
  afs: Rearrange fs/afs/proc.c by moving fops and open functions down
  afs: Move /proc management functions to the end of the file
parents 29d6849d 47ea0f2e
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
afs-cache-$(CONFIG_AFS_FSCACHE) := cache.o afs-cache-$(CONFIG_AFS_FSCACHE) := cache.o
kafs-objs := \ kafs-y := \
$(afs-cache-y) \ $(afs-cache-y) \
addr_list.o \ addr_list.o \
callback.o \ callback.o \
...@@ -21,7 +21,6 @@ kafs-objs := \ ...@@ -21,7 +21,6 @@ kafs-objs := \
main.o \ main.o \
misc.o \ misc.o \
mntpt.o \ mntpt.o \
proc.o \
rotate.o \ rotate.o \
rxrpc.o \ rxrpc.o \
security.o \ security.o \
...@@ -34,4 +33,5 @@ kafs-objs := \ ...@@ -34,4 +33,5 @@ kafs-objs := \
write.o \ write.o \
xattr.o xattr.o
kafs-$(CONFIG_PROC_FS) += proc.o
obj-$(CONFIG_AFS_FS) := kafs.o obj-$(CONFIG_AFS_FS) := kafs.o
...@@ -215,7 +215,7 @@ struct afs_addr_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry) ...@@ -215,7 +215,7 @@ struct afs_addr_list *afs_dns_query(struct afs_cell *cell, time64_t *_expiry)
_enter("%s", cell->name); _enter("%s", cell->name);
ret = dns_query("afsdb", cell->name, cell->name_len, ret = dns_query("afsdb", cell->name, cell->name_len,
"ipv4", &vllist, _expiry); "", &vllist, _expiry);
if (ret < 0) if (ret < 0)
return ERR_PTR(ret); return ERR_PTR(ret);
......
...@@ -20,6 +20,66 @@ ...@@ -20,6 +20,66 @@
#include <linux/sched.h> #include <linux/sched.h>
#include "internal.h" #include "internal.h"
/*
* Create volume and callback interests on a server.
*/
static struct afs_cb_interest *afs_create_interest(struct afs_server *server,
struct afs_vnode *vnode)
{
struct afs_vol_interest *new_vi, *vi;
struct afs_cb_interest *new;
struct hlist_node **pp;
new_vi = kzalloc(sizeof(struct afs_vol_interest), GFP_KERNEL);
if (!new_vi)
return NULL;
new = kzalloc(sizeof(struct afs_cb_interest), GFP_KERNEL);
if (!new) {
kfree(new_vi);
return NULL;
}
new_vi->usage = 1;
new_vi->vid = vnode->volume->vid;
INIT_HLIST_NODE(&new_vi->srv_link);
INIT_HLIST_HEAD(&new_vi->cb_interests);
refcount_set(&new->usage, 1);
new->sb = vnode->vfs_inode.i_sb;
new->vid = vnode->volume->vid;
new->server = afs_get_server(server);
INIT_HLIST_NODE(&new->cb_vlink);
write_lock(&server->cb_break_lock);
for (pp = &server->cb_volumes.first; *pp; pp = &(*pp)->next) {
vi = hlist_entry(*pp, struct afs_vol_interest, srv_link);
if (vi->vid < new_vi->vid)
continue;
if (vi->vid > new_vi->vid)
break;
vi->usage++;
goto found_vi;
}
new_vi->srv_link.pprev = pp;
new_vi->srv_link.next = *pp;
if (*pp)
(*pp)->pprev = &new_vi->srv_link.next;
*pp = &new_vi->srv_link;
vi = new_vi;
new_vi = NULL;
found_vi:
new->vol_interest = vi;
hlist_add_head(&new->cb_vlink, &vi->cb_interests);
write_unlock(&server->cb_break_lock);
kfree(new_vi);
return new;
}
/* /*
* Set up an interest-in-callbacks record for a volume on a server and * Set up an interest-in-callbacks record for a volume on a server and
* register it with the server. * register it with the server.
...@@ -77,20 +137,10 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode, ...@@ -77,20 +137,10 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode,
} }
if (!cbi) { if (!cbi) {
new = kzalloc(sizeof(struct afs_cb_interest), GFP_KERNEL); new = afs_create_interest(server, vnode);
if (!new) if (!new)
return -ENOMEM; return -ENOMEM;
refcount_set(&new->usage, 1);
new->sb = vnode->vfs_inode.i_sb;
new->vid = vnode->volume->vid;
new->server = afs_get_server(server);
INIT_LIST_HEAD(&new->cb_link);
write_lock(&server->cb_break_lock);
list_add_tail(&new->cb_link, &server->cb_interests);
write_unlock(&server->cb_break_lock);
write_lock(&slist->lock); write_lock(&slist->lock);
if (!entry->cb_interest) { if (!entry->cb_interest) {
entry->cb_interest = afs_get_cb_interest(new); entry->cb_interest = afs_get_cb_interest(new);
...@@ -126,11 +176,22 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode, ...@@ -126,11 +176,22 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode,
*/ */
void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi) void afs_put_cb_interest(struct afs_net *net, struct afs_cb_interest *cbi)
{ {
struct afs_vol_interest *vi;
if (cbi && refcount_dec_and_test(&cbi->usage)) { if (cbi && refcount_dec_and_test(&cbi->usage)) {
if (!list_empty(&cbi->cb_link)) { if (!hlist_unhashed(&cbi->cb_vlink)) {
write_lock(&cbi->server->cb_break_lock); write_lock(&cbi->server->cb_break_lock);
list_del_init(&cbi->cb_link);
hlist_del_init(&cbi->cb_vlink);
vi = cbi->vol_interest;
cbi->vol_interest = NULL;
if (--vi->usage == 0)
hlist_del(&vi->srv_link);
else
vi = NULL;
write_unlock(&cbi->server->cb_break_lock); write_unlock(&cbi->server->cb_break_lock);
kfree(vi);
afs_put_server(net, cbi->server); afs_put_server(net, cbi->server);
} }
kfree(cbi); kfree(cbi);
...@@ -182,20 +243,34 @@ void afs_break_callback(struct afs_vnode *vnode) ...@@ -182,20 +243,34 @@ void afs_break_callback(struct afs_vnode *vnode)
static void afs_break_one_callback(struct afs_server *server, static void afs_break_one_callback(struct afs_server *server,
struct afs_fid *fid) struct afs_fid *fid)
{ {
struct afs_vol_interest *vi;
struct afs_cb_interest *cbi; struct afs_cb_interest *cbi;
struct afs_iget_data data; struct afs_iget_data data;
struct afs_vnode *vnode; struct afs_vnode *vnode;
struct inode *inode; struct inode *inode;
read_lock(&server->cb_break_lock); read_lock(&server->cb_break_lock);
hlist_for_each_entry(vi, &server->cb_volumes, srv_link) {
if (vi->vid < fid->vid)
continue;
if (vi->vid > fid->vid) {
vi = NULL;
break;
}
//atomic_inc(&vi->usage);
break;
}
/* TODO: Find all matching volumes if we couldn't match the server and
* break them anyway.
*/
if (!vi)
goto out;
/* Step through all interested superblocks. There may be more than one /* Step through all interested superblocks. There may be more than one
* because of cell aliasing. * because of cell aliasing.
*/ */
list_for_each_entry(cbi, &server->cb_interests, cb_link) { hlist_for_each_entry(cbi, &vi->cb_interests, cb_vlink) {
if (cbi->vid != fid->vid)
continue;
if (fid->vnode == 0 && fid->unique == 0) { if (fid->vnode == 0 && fid->unique == 0) {
/* The callback break applies to an entire volume. */ /* The callback break applies to an entire volume. */
struct afs_super_info *as = AFS_FS_S(cbi->sb); struct afs_super_info *as = AFS_FS_S(cbi->sb);
...@@ -217,6 +292,7 @@ static void afs_break_one_callback(struct afs_server *server, ...@@ -217,6 +292,7 @@ static void afs_break_one_callback(struct afs_server *server,
} }
} }
out:
read_unlock(&server->cb_break_lock); read_unlock(&server->cb_break_lock);
} }
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/dns_resolver.h> #include <linux/dns_resolver.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/inet.h> #include <linux/inet.h>
#include <linux/namei.h>
#include <keys/rxrpc-type.h> #include <keys/rxrpc-type.h>
#include "internal.h" #include "internal.h"
...@@ -341,8 +342,8 @@ int afs_cell_init(struct afs_net *net, const char *rootcell) ...@@ -341,8 +342,8 @@ int afs_cell_init(struct afs_net *net, const char *rootcell)
/* install the new cell */ /* install the new cell */
write_seqlock(&net->cells_lock); write_seqlock(&net->cells_lock);
old_root = net->ws_cell; old_root = rcu_access_pointer(net->ws_cell);
net->ws_cell = new_root; rcu_assign_pointer(net->ws_cell, new_root);
write_sequnlock(&net->cells_lock); write_sequnlock(&net->cells_lock);
afs_put_cell(net, old_root); afs_put_cell(net, old_root);
...@@ -528,12 +529,14 @@ static int afs_activate_cell(struct afs_net *net, struct afs_cell *cell) ...@@ -528,12 +529,14 @@ static int afs_activate_cell(struct afs_net *net, struct afs_cell *cell)
NULL, 0, NULL, 0,
cell, 0, true); cell, 0, true);
#endif #endif
ret = afs_proc_cell_setup(net, cell); ret = afs_proc_cell_setup(cell);
if (ret < 0) if (ret < 0)
return ret; return ret;
spin_lock(&net->proc_cells_lock);
mutex_lock(&net->proc_cells_lock);
list_add_tail(&cell->proc_link, &net->proc_cells); list_add_tail(&cell->proc_link, &net->proc_cells);
spin_unlock(&net->proc_cells_lock); afs_dynroot_mkdir(net, cell);
mutex_unlock(&net->proc_cells_lock);
return 0; return 0;
} }
...@@ -544,11 +547,12 @@ static void afs_deactivate_cell(struct afs_net *net, struct afs_cell *cell) ...@@ -544,11 +547,12 @@ static void afs_deactivate_cell(struct afs_net *net, struct afs_cell *cell)
{ {
_enter("%s", cell->name); _enter("%s", cell->name);
afs_proc_cell_remove(net, cell); afs_proc_cell_remove(cell);
spin_lock(&net->proc_cells_lock); mutex_lock(&net->proc_cells_lock);
list_del_init(&cell->proc_link); list_del_init(&cell->proc_link);
spin_unlock(&net->proc_cells_lock); afs_dynroot_rmdir(net, cell);
mutex_unlock(&net->proc_cells_lock);
#ifdef CONFIG_AFS_FSCACHE #ifdef CONFIG_AFS_FSCACHE
fscache_relinquish_cookie(cell->cache, NULL, false); fscache_relinquish_cookie(cell->cache, NULL, false);
...@@ -755,8 +759,8 @@ void afs_cell_purge(struct afs_net *net) ...@@ -755,8 +759,8 @@ void afs_cell_purge(struct afs_net *net)
_enter(""); _enter("");
write_seqlock(&net->cells_lock); write_seqlock(&net->cells_lock);
ws = net->ws_cell; ws = rcu_access_pointer(net->ws_cell);
net->ws_cell = NULL; RCU_INIT_POINTER(net->ws_cell, NULL);
write_sequnlock(&net->cells_lock); write_sequnlock(&net->cells_lock);
afs_put_cell(net, ws); afs_put_cell(net, ws);
......
...@@ -526,7 +526,7 @@ static void SRXAFSCB_TellMeAboutYourself(struct work_struct *work) ...@@ -526,7 +526,7 @@ static void SRXAFSCB_TellMeAboutYourself(struct work_struct *work)
nifs = 0; nifs = 0;
ifs = kcalloc(32, sizeof(*ifs), GFP_KERNEL); ifs = kcalloc(32, sizeof(*ifs), GFP_KERNEL);
if (ifs) { if (ifs) {
nifs = afs_get_ipv4_interfaces(ifs, 32, false); nifs = afs_get_ipv4_interfaces(call->net, ifs, 32, false);
if (nifs < 0) { if (nifs < 0) {
kfree(ifs); kfree(ifs);
ifs = NULL; ifs = NULL;
......
/* dir.c: AFS dynamic root handling /* AFS dynamic root handling
* *
* Copyright (C) 2018 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
...@@ -46,7 +46,7 @@ static int afs_probe_cell_name(struct dentry *dentry) ...@@ -46,7 +46,7 @@ static int afs_probe_cell_name(struct dentry *dentry)
return 0; return 0;
} }
ret = dns_query("afsdb", name, len, "ipv4", NULL, NULL); ret = dns_query("afsdb", name, len, "", NULL, NULL);
if (ret == -ENODATA) if (ret == -ENODATA)
ret = -EDESTADDRREQ; ret = -EDESTADDRREQ;
return ret; return ret;
...@@ -207,3 +207,125 @@ const struct dentry_operations afs_dynroot_dentry_operations = { ...@@ -207,3 +207,125 @@ const struct dentry_operations afs_dynroot_dentry_operations = {
.d_release = afs_d_release, .d_release = afs_d_release,
.d_automount = afs_d_automount, .d_automount = afs_d_automount,
}; };
/*
* Create a manually added cell mount directory.
* - The caller must hold net->proc_cells_lock
*/
int afs_dynroot_mkdir(struct afs_net *net, struct afs_cell *cell)
{
struct super_block *sb = net->dynroot_sb;
struct dentry *root, *subdir;
int ret;
if (!sb || atomic_read(&sb->s_active) == 0)
return 0;
/* Let the ->lookup op do the creation */
root = sb->s_root;
inode_lock(root->d_inode);
subdir = lookup_one_len(cell->name, root, cell->name_len);
if (IS_ERR(subdir)) {
ret = PTR_ERR(subdir);
goto unlock;
}
/* Note that we're retaining an extra ref on the dentry */
subdir->d_fsdata = (void *)1UL;
ret = 0;
unlock:
inode_unlock(root->d_inode);
return ret;
}
/*
* Remove a manually added cell mount directory.
* - The caller must hold net->proc_cells_lock
*/
void afs_dynroot_rmdir(struct afs_net *net, struct afs_cell *cell)
{
struct super_block *sb = net->dynroot_sb;
struct dentry *root, *subdir;
if (!sb || atomic_read(&sb->s_active) == 0)
return;
root = sb->s_root;
inode_lock(root->d_inode);
/* Don't want to trigger a lookup call, which will re-add the cell */
subdir = try_lookup_one_len(cell->name, root, cell->name_len);
if (IS_ERR_OR_NULL(subdir)) {
_debug("lookup %ld", PTR_ERR(subdir));
goto no_dentry;
}
_debug("rmdir %pd %u", subdir, d_count(subdir));
if (subdir->d_fsdata) {
_debug("unpin %u", d_count(subdir));
subdir->d_fsdata = NULL;
dput(subdir);
}
dput(subdir);
no_dentry:
inode_unlock(root->d_inode);
_leave("");
}
/*
* Populate a newly created dynamic root with cell names.
*/
int afs_dynroot_populate(struct super_block *sb)
{
struct afs_cell *cell;
struct afs_net *net = afs_sb2net(sb);
int ret;
if (mutex_lock_interruptible(&net->proc_cells_lock) < 0)
return -ERESTARTSYS;
net->dynroot_sb = sb;
list_for_each_entry(cell, &net->proc_cells, proc_link) {
ret = afs_dynroot_mkdir(net, cell);
if (ret < 0)
goto error;
}
ret = 0;
out:
mutex_unlock(&net->proc_cells_lock);
return ret;
error:
net->dynroot_sb = NULL;
goto out;
}
/*
* When a dynamic root that's in the process of being destroyed, depopulate it
* of pinned directories.
*/
void afs_dynroot_depopulate(struct super_block *sb)
{
struct afs_net *net = afs_sb2net(sb);
struct dentry *root = sb->s_root, *subdir, *tmp;
/* Prevent more subdirs from being created */
mutex_lock(&net->proc_cells_lock);
if (net->dynroot_sb == sb)
net->dynroot_sb = NULL;
mutex_unlock(&net->proc_cells_lock);
inode_lock(root->d_inode);
/* Remove all the pins for dirs created for manually added cells */
list_for_each_entry_safe(subdir, tmp, &root->d_subdirs, d_child) {
if (subdir->d_fsdata) {
subdir->d_fsdata = NULL;
dput(subdir);
}
}
inode_unlock(root->d_inode);
}
...@@ -138,10 +138,6 @@ static int xdr_decode_AFSFetchStatus(struct afs_call *call, ...@@ -138,10 +138,6 @@ static int xdr_decode_AFSFetchStatus(struct afs_call *call,
u64 data_version, size; u64 data_version, size;
u32 type, abort_code; u32 type, abort_code;
u8 flags = 0; u8 flags = 0;
int ret;
if (vnode)
write_seqlock(&vnode->cb_lock);
abort_code = ntohl(xdr->abort_code); abort_code = ntohl(xdr->abort_code);
...@@ -154,8 +150,7 @@ static int xdr_decode_AFSFetchStatus(struct afs_call *call, ...@@ -154,8 +150,7 @@ static int xdr_decode_AFSFetchStatus(struct afs_call *call,
* case. * case.
*/ */
status->abort_code = abort_code; status->abort_code = abort_code;
ret = 0; return 0;
goto out;
} }
pr_warn("Unknown AFSFetchStatus version %u\n", ntohl(xdr->if_version)); pr_warn("Unknown AFSFetchStatus version %u\n", ntohl(xdr->if_version));
...@@ -164,8 +159,7 @@ static int xdr_decode_AFSFetchStatus(struct afs_call *call, ...@@ -164,8 +159,7 @@ static int xdr_decode_AFSFetchStatus(struct afs_call *call,
if (abort_code != 0 && inline_error) { if (abort_code != 0 && inline_error) {
status->abort_code = abort_code; status->abort_code = abort_code;
ret = 0; return 0;
goto out;
} }
type = ntohl(xdr->type); type = ntohl(xdr->type);
...@@ -235,17 +229,35 @@ static int xdr_decode_AFSFetchStatus(struct afs_call *call, ...@@ -235,17 +229,35 @@ static int xdr_decode_AFSFetchStatus(struct afs_call *call,
flags); flags);
} }
ret = 0; return 0;
out:
if (vnode)
write_sequnlock(&vnode->cb_lock);
return ret;
bad: bad:
xdr_dump_bad(*_bp); xdr_dump_bad(*_bp);
ret = afs_protocol_error(call, -EBADMSG); return afs_protocol_error(call, -EBADMSG);
goto out; }
/*
* Decode the file status. We need to lock the target vnode if we're going to
* update its status so that stat() sees the attributes update atomically.
*/
static int afs_decode_status(struct afs_call *call,
const __be32 **_bp,
struct afs_file_status *status,
struct afs_vnode *vnode,
const afs_dataversion_t *expected_version,
struct afs_read *read_req)
{
int ret;
if (!vnode)
return xdr_decode_AFSFetchStatus(call, _bp, status, vnode,
expected_version, read_req);
write_seqlock(&vnode->cb_lock);
ret = xdr_decode_AFSFetchStatus(call, _bp, status, vnode,
expected_version, read_req);
write_sequnlock(&vnode->cb_lock);
return ret;
} }
/* /*
...@@ -387,8 +399,8 @@ static int afs_deliver_fs_fetch_status_vnode(struct afs_call *call) ...@@ -387,8 +399,8 @@ static int afs_deliver_fs_fetch_status_vnode(struct afs_call *call)
/* unmarshall the reply once we've received all of it */ /* unmarshall the reply once we've received all of it */
bp = call->buffer; bp = call->buffer;
if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode, if (afs_decode_status(call, &bp, &vnode->status, vnode,
&call->expected_version, NULL) < 0) &call->expected_version, NULL) < 0)
return afs_protocol_error(call, -EBADMSG); return afs_protocol_error(call, -EBADMSG);
xdr_decode_AFSCallBack(call, vnode, &bp); xdr_decode_AFSCallBack(call, vnode, &bp);
if (call->reply[1]) if (call->reply[1])
...@@ -568,8 +580,8 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) ...@@ -568,8 +580,8 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
return ret; return ret;
bp = call->buffer; bp = call->buffer;
if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode, if (afs_decode_status(call, &bp, &vnode->status, vnode,
&vnode->status.data_version, req) < 0) &vnode->status.data_version, req) < 0)
return afs_protocol_error(call, -EBADMSG); return afs_protocol_error(call, -EBADMSG);
xdr_decode_AFSCallBack(call, vnode, &bp); xdr_decode_AFSCallBack(call, vnode, &bp);
if (call->reply[1]) if (call->reply[1])
...@@ -721,9 +733,9 @@ static int afs_deliver_fs_create_vnode(struct afs_call *call) ...@@ -721,9 +733,9 @@ static int afs_deliver_fs_create_vnode(struct afs_call *call)
/* unmarshall the reply once we've received all of it */ /* unmarshall the reply once we've received all of it */
bp = call->buffer; bp = call->buffer;
xdr_decode_AFSFid(&bp, call->reply[1]); xdr_decode_AFSFid(&bp, call->reply[1]);
if (xdr_decode_AFSFetchStatus(call, &bp, call->reply[2], NULL, NULL, NULL) < 0 || if (afs_decode_status(call, &bp, call->reply[2], NULL, NULL, NULL) < 0 ||
xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode, afs_decode_status(call, &bp, &vnode->status, vnode,
&call->expected_version, NULL) < 0) &call->expected_version, NULL) < 0)
return afs_protocol_error(call, -EBADMSG); return afs_protocol_error(call, -EBADMSG);
xdr_decode_AFSCallBack_raw(&bp, call->reply[3]); xdr_decode_AFSCallBack_raw(&bp, call->reply[3]);
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */ /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
...@@ -827,8 +839,8 @@ static int afs_deliver_fs_remove(struct afs_call *call) ...@@ -827,8 +839,8 @@ static int afs_deliver_fs_remove(struct afs_call *call)
/* unmarshall the reply once we've received all of it */ /* unmarshall the reply once we've received all of it */
bp = call->buffer; bp = call->buffer;
if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode, if (afs_decode_status(call, &bp, &vnode->status, vnode,
&call->expected_version, NULL) < 0) &call->expected_version, NULL) < 0)
return afs_protocol_error(call, -EBADMSG); return afs_protocol_error(call, -EBADMSG);
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */ /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
...@@ -917,9 +929,9 @@ static int afs_deliver_fs_link(struct afs_call *call) ...@@ -917,9 +929,9 @@ static int afs_deliver_fs_link(struct afs_call *call)
/* unmarshall the reply once we've received all of it */ /* unmarshall the reply once we've received all of it */
bp = call->buffer; bp = call->buffer;
if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode, NULL, NULL) < 0 || if (afs_decode_status(call, &bp, &vnode->status, vnode, NULL, NULL) < 0 ||
xdr_decode_AFSFetchStatus(call, &bp, &dvnode->status, dvnode, afs_decode_status(call, &bp, &dvnode->status, dvnode,
&call->expected_version, NULL) < 0) &call->expected_version, NULL) < 0)
return afs_protocol_error(call, -EBADMSG); return afs_protocol_error(call, -EBADMSG);
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */ /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
...@@ -1004,9 +1016,9 @@ static int afs_deliver_fs_symlink(struct afs_call *call) ...@@ -1004,9 +1016,9 @@ static int afs_deliver_fs_symlink(struct afs_call *call)
/* unmarshall the reply once we've received all of it */ /* unmarshall the reply once we've received all of it */
bp = call->buffer; bp = call->buffer;
xdr_decode_AFSFid(&bp, call->reply[1]); xdr_decode_AFSFid(&bp, call->reply[1]);
if (xdr_decode_AFSFetchStatus(call, &bp, call->reply[2], NULL, NULL, NULL) || if (afs_decode_status(call, &bp, call->reply[2], NULL, NULL, NULL) ||
xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode, afs_decode_status(call, &bp, &vnode->status, vnode,
&call->expected_version, NULL) < 0) &call->expected_version, NULL) < 0)
return afs_protocol_error(call, -EBADMSG); return afs_protocol_error(call, -EBADMSG);
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */ /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
...@@ -1110,12 +1122,12 @@ static int afs_deliver_fs_rename(struct afs_call *call) ...@@ -1110,12 +1122,12 @@ static int afs_deliver_fs_rename(struct afs_call *call)
/* unmarshall the reply once we've received all of it */ /* unmarshall the reply once we've received all of it */
bp = call->buffer; bp = call->buffer;
if (xdr_decode_AFSFetchStatus(call, &bp, &orig_dvnode->status, orig_dvnode, if (afs_decode_status(call, &bp, &orig_dvnode->status, orig_dvnode,
&call->expected_version, NULL) < 0) &call->expected_version, NULL) < 0)
return afs_protocol_error(call, -EBADMSG); return afs_protocol_error(call, -EBADMSG);
if (new_dvnode != orig_dvnode && if (new_dvnode != orig_dvnode &&
xdr_decode_AFSFetchStatus(call, &bp, &new_dvnode->status, new_dvnode, afs_decode_status(call, &bp, &new_dvnode->status, new_dvnode,
&call->expected_version_2, NULL) < 0) &call->expected_version_2, NULL) < 0)
return afs_protocol_error(call, -EBADMSG); return afs_protocol_error(call, -EBADMSG);
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */ /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
...@@ -1219,8 +1231,8 @@ static int afs_deliver_fs_store_data(struct afs_call *call) ...@@ -1219,8 +1231,8 @@ static int afs_deliver_fs_store_data(struct afs_call *call)
/* unmarshall the reply once we've received all of it */ /* unmarshall the reply once we've received all of it */
bp = call->buffer; bp = call->buffer;
if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode, if (afs_decode_status(call, &bp, &vnode->status, vnode,
&call->expected_version, NULL) < 0) &call->expected_version, NULL) < 0)
return afs_protocol_error(call, -EBADMSG); return afs_protocol_error(call, -EBADMSG);
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */ /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
...@@ -1395,8 +1407,8 @@ static int afs_deliver_fs_store_status(struct afs_call *call) ...@@ -1395,8 +1407,8 @@ static int afs_deliver_fs_store_status(struct afs_call *call)
/* unmarshall the reply once we've received all of it */ /* unmarshall the reply once we've received all of it */
bp = call->buffer; bp = call->buffer;
if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode, if (afs_decode_status(call, &bp, &vnode->status, vnode,
&call->expected_version, NULL) < 0) &call->expected_version, NULL) < 0)
return afs_protocol_error(call, -EBADMSG); return afs_protocol_error(call, -EBADMSG);
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */ /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
...@@ -2097,8 +2109,8 @@ static int afs_deliver_fs_fetch_status(struct afs_call *call) ...@@ -2097,8 +2109,8 @@ static int afs_deliver_fs_fetch_status(struct afs_call *call)
/* unmarshall the reply once we've received all of it */ /* unmarshall the reply once we've received all of it */
bp = call->buffer; bp = call->buffer;
xdr_decode_AFSFetchStatus(call, &bp, status, vnode, afs_decode_status(call, &bp, status, vnode,
&call->expected_version, NULL); &call->expected_version, NULL);
callback[call->count].version = ntohl(bp[0]); callback[call->count].version = ntohl(bp[0]);
callback[call->count].expiry = ntohl(bp[1]); callback[call->count].expiry = ntohl(bp[1]);
callback[call->count].type = ntohl(bp[2]); callback[call->count].type = ntohl(bp[2]);
...@@ -2209,9 +2221,9 @@ static int afs_deliver_fs_inline_bulk_status(struct afs_call *call) ...@@ -2209,9 +2221,9 @@ static int afs_deliver_fs_inline_bulk_status(struct afs_call *call)
bp = call->buffer; bp = call->buffer;
statuses = call->reply[1]; statuses = call->reply[1];
if (xdr_decode_AFSFetchStatus(call, &bp, &statuses[call->count], if (afs_decode_status(call, &bp, &statuses[call->count],
call->count == 0 ? vnode : NULL, call->count == 0 ? vnode : NULL,
NULL, NULL) < 0) NULL, NULL) < 0)
return afs_protocol_error(call, -EBADMSG); return afs_protocol_error(call, -EBADMSG);
call->count++; call->count++;
......
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <linux/uuid.h> #include <linux/uuid.h>
#include <net/net_namespace.h> #include <net/net_namespace.h>
#include <net/netns/generic.h>
#include <net/sock.h>
#include <net/af_rxrpc.h> #include <net/af_rxrpc.h>
#include "afs.h" #include "afs.h"
...@@ -40,7 +42,8 @@ struct afs_mount_params { ...@@ -40,7 +42,8 @@ struct afs_mount_params {
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 */
struct afs_net *net; /* Network namespace in effect */ struct net *net_ns; /* Network namespace in effect */
struct afs_net *net; /* the AFS net namespace stuff */
struct afs_cell *cell; /* cell in which to find volume */ struct afs_cell *cell; /* cell in which to find volume */
struct afs_volume *volume; /* volume record */ struct afs_volume *volume; /* volume record */
struct key *key; /* key to use for secure mounting */ struct key *key; /* key to use for secure mounting */
...@@ -189,7 +192,7 @@ struct afs_read { ...@@ -189,7 +192,7 @@ struct afs_read {
* - there's one superblock per volume * - there's one superblock per volume
*/ */
struct afs_super_info { struct afs_super_info {
struct afs_net *net; /* Network namespace */ struct net *net_ns; /* 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 */ bool dyn_root; /* True if dynamic root */
...@@ -210,7 +213,6 @@ struct afs_sysnames { ...@@ -210,7 +213,6 @@ struct afs_sysnames {
char *subs[AFS_NR_SYSNAME]; char *subs[AFS_NR_SYSNAME];
refcount_t usage; refcount_t usage;
unsigned short nr; unsigned short nr;
short error;
char blank[1]; char blank[1];
}; };
...@@ -218,6 +220,7 @@ struct afs_sysnames { ...@@ -218,6 +220,7 @@ struct afs_sysnames {
* AFS network namespace record. * AFS network namespace record.
*/ */
struct afs_net { struct afs_net {
struct net *net; /* Backpointer to the owning net namespace */
struct afs_uuid uuid; struct afs_uuid uuid;
bool live; /* F if this namespace is being removed */ bool live; /* F if this namespace is being removed */
...@@ -231,13 +234,13 @@ struct afs_net { ...@@ -231,13 +234,13 @@ struct afs_net {
/* Cell database */ /* Cell database */
struct rb_root cells; struct rb_root cells;
struct afs_cell *ws_cell; struct afs_cell __rcu *ws_cell;
struct work_struct cells_manager; struct work_struct cells_manager;
struct timer_list cells_timer; struct timer_list cells_timer;
atomic_t cells_outstanding; atomic_t cells_outstanding;
seqlock_t cells_lock; seqlock_t cells_lock;
spinlock_t proc_cells_lock; struct mutex proc_cells_lock;
struct list_head proc_cells; struct list_head proc_cells;
/* Known servers. Theoretically each fileserver can only be in one /* Known servers. Theoretically each fileserver can only be in one
...@@ -261,6 +264,7 @@ struct afs_net { ...@@ -261,6 +264,7 @@ struct afs_net {
struct mutex lock_manager_mutex; struct mutex lock_manager_mutex;
/* Misc */ /* Misc */
struct super_block *dynroot_sb; /* Dynamic root mount superblock */
struct proc_dir_entry *proc_afs; /* /proc/net/afs directory */ struct proc_dir_entry *proc_afs; /* /proc/net/afs directory */
struct afs_sysnames *sysnames; struct afs_sysnames *sysnames;
rwlock_t sysnames_lock; rwlock_t sysnames_lock;
...@@ -280,7 +284,6 @@ struct afs_net { ...@@ -280,7 +284,6 @@ struct afs_net {
}; };
extern const char afs_init_sysname[]; extern const char afs_init_sysname[];
extern struct afs_net __afs_net;// Dummy AFS network namespace; TODO: replace with real netns
enum afs_cell_state { enum afs_cell_state {
AFS_CELL_UNSET, AFS_CELL_UNSET,
...@@ -404,16 +407,27 @@ struct afs_server { ...@@ -404,16 +407,27 @@ struct afs_server {
rwlock_t fs_lock; /* access lock */ rwlock_t fs_lock; /* access lock */
/* callback promise management */ /* callback promise management */
struct list_head cb_interests; /* List of superblocks using this server */ struct hlist_head cb_volumes; /* List of volume interests on this server */
unsigned cb_s_break; /* Break-everything counter. */ unsigned cb_s_break; /* Break-everything counter. */
rwlock_t cb_break_lock; /* Volume finding lock */ rwlock_t cb_break_lock; /* Volume finding lock */
}; };
/*
* Volume collation in the server's callback interest list.
*/
struct afs_vol_interest {
struct hlist_node srv_link; /* Link in server->cb_volumes */
struct hlist_head cb_interests; /* List of callback interests on the server */
afs_volid_t vid; /* Volume ID to match */
unsigned int usage;
};
/* /*
* Interest by a superblock on a server. * Interest by a superblock on a server.
*/ */
struct afs_cb_interest { struct afs_cb_interest {
struct list_head cb_link; /* Link in server->cb_interests */ struct hlist_node cb_vlink; /* Link in vol_interest->cb_interests */
struct afs_vol_interest *vol_interest;
struct afs_server *server; /* Server on which this interest resides */ struct afs_server *server; /* Server on which this interest resides */
struct super_block *sb; /* Superblock on which inodes reside */ struct super_block *sb; /* Superblock on which inodes reside */
afs_volid_t vid; /* Volume ID to match */ afs_volid_t vid; /* Volume ID to match */
...@@ -720,6 +734,10 @@ extern const struct inode_operations afs_dynroot_inode_operations; ...@@ -720,6 +734,10 @@ extern const struct inode_operations afs_dynroot_inode_operations;
extern const struct dentry_operations afs_dynroot_dentry_operations; extern const struct dentry_operations afs_dynroot_dentry_operations;
extern struct inode *afs_try_auto_mntpt(struct dentry *, struct inode *); extern struct inode *afs_try_auto_mntpt(struct dentry *, struct inode *);
extern int afs_dynroot_mkdir(struct afs_net *, struct afs_cell *);
extern void afs_dynroot_rmdir(struct afs_net *, struct afs_cell *);
extern int afs_dynroot_populate(struct super_block *);
extern void afs_dynroot_depopulate(struct super_block *);
/* /*
* file.c * file.c
...@@ -806,34 +824,36 @@ extern int afs_drop_inode(struct inode *); ...@@ -806,34 +824,36 @@ extern int afs_drop_inode(struct inode *);
* main.c * main.c
*/ */
extern struct workqueue_struct *afs_wq; extern struct workqueue_struct *afs_wq;
extern int afs_net_id;
static inline struct afs_net *afs_d2net(struct dentry *dentry) static inline struct afs_net *afs_net(struct net *net)
{ {
return &__afs_net; return net_generic(net, afs_net_id);
} }
static inline struct afs_net *afs_i2net(struct inode *inode) static inline struct afs_net *afs_sb2net(struct super_block *sb)
{ {
return &__afs_net; return afs_net(AFS_FS_S(sb)->net_ns);
} }
static inline struct afs_net *afs_v2net(struct afs_vnode *vnode) static inline struct afs_net *afs_d2net(struct dentry *dentry)
{ {
return &__afs_net; return afs_sb2net(dentry->d_sb);
} }
static inline struct afs_net *afs_sock2net(struct sock *sk) static inline struct afs_net *afs_i2net(struct inode *inode)
{ {
return &__afs_net; return afs_sb2net(inode->i_sb);
} }
static inline struct afs_net *afs_get_net(struct afs_net *net) static inline struct afs_net *afs_v2net(struct afs_vnode *vnode)
{ {
return net; return afs_i2net(&vnode->vfs_inode);
} }
static inline void afs_put_net(struct afs_net *net) static inline struct afs_net *afs_sock2net(struct sock *sk)
{ {
return net_generic(sock_net(sk), afs_net_id);
} }
static inline void __afs_stat(atomic_t *s) static inline void __afs_stat(atomic_t *s)
...@@ -861,16 +881,25 @@ extern void afs_mntpt_kill_timer(void); ...@@ -861,16 +881,25 @@ extern void afs_mntpt_kill_timer(void);
/* /*
* netdevices.c * netdevices.c
*/ */
extern int afs_get_ipv4_interfaces(struct afs_interface *, size_t, bool); extern int afs_get_ipv4_interfaces(struct afs_net *, struct afs_interface *,
size_t, bool);
/* /*
* proc.c * proc.c
*/ */
#ifdef CONFIG_PROC_FS
extern int __net_init afs_proc_init(struct afs_net *); extern int __net_init afs_proc_init(struct afs_net *);
extern void __net_exit afs_proc_cleanup(struct afs_net *); extern void __net_exit afs_proc_cleanup(struct afs_net *);
extern int afs_proc_cell_setup(struct afs_net *, struct afs_cell *); extern int afs_proc_cell_setup(struct afs_cell *);
extern void afs_proc_cell_remove(struct afs_net *, struct afs_cell *); extern void afs_proc_cell_remove(struct afs_cell *);
extern void afs_put_sysnames(struct afs_sysnames *); extern void afs_put_sysnames(struct afs_sysnames *);
#else
static inline int afs_proc_init(struct afs_net *net) { return 0; }
static inline void afs_proc_cleanup(struct afs_net *net) {}
static inline int afs_proc_cell_setup(struct afs_cell *cell) { return 0; }
static inline void afs_proc_cell_remove(struct afs_cell *cell) {}
static inline void afs_put_sysnames(struct afs_sysnames *sysnames) {}
#endif
/* /*
* rotate.c * rotate.c
...@@ -1002,7 +1031,7 @@ extern bool afs_annotate_server_list(struct afs_server_list *, struct afs_server ...@@ -1002,7 +1031,7 @@ extern bool afs_annotate_server_list(struct afs_server_list *, struct afs_server
* super.c * super.c
*/ */
extern int __init afs_fs_init(void); extern int __init afs_fs_init(void);
extern void __exit afs_fs_exit(void); extern void afs_fs_exit(void);
/* /*
* vlclient.c * vlclient.c
......
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/random.h> #include <linux/random.h>
#include <linux/proc_fs.h>
#define CREATE_TRACE_POINTS #define CREATE_TRACE_POINTS
#include "internal.h" #include "internal.h"
...@@ -32,7 +33,7 @@ module_param(rootcell, charp, 0); ...@@ -32,7 +33,7 @@ module_param(rootcell, charp, 0);
MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list"); MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list");
struct workqueue_struct *afs_wq; struct workqueue_struct *afs_wq;
struct afs_net __afs_net; static struct proc_dir_entry *afs_proc_symlink;
#if defined(CONFIG_ALPHA) #if defined(CONFIG_ALPHA)
const char afs_init_sysname[] = "alpha_linux26"; const char afs_init_sysname[] = "alpha_linux26";
...@@ -67,11 +68,13 @@ const char afs_init_sysname[] = "unknown_linux26"; ...@@ -67,11 +68,13 @@ const char afs_init_sysname[] = "unknown_linux26";
/* /*
* Initialise an AFS network namespace record. * Initialise an AFS network namespace record.
*/ */
static int __net_init afs_net_init(struct afs_net *net) static int __net_init afs_net_init(struct net *net_ns)
{ {
struct afs_sysnames *sysnames; struct afs_sysnames *sysnames;
struct afs_net *net = afs_net(net_ns);
int ret; int ret;
net->net = net_ns;
net->live = true; net->live = true;
generate_random_uuid((unsigned char *)&net->uuid); generate_random_uuid((unsigned char *)&net->uuid);
...@@ -83,7 +86,7 @@ static int __net_init afs_net_init(struct afs_net *net) ...@@ -83,7 +86,7 @@ static int __net_init afs_net_init(struct afs_net *net)
INIT_WORK(&net->cells_manager, afs_manage_cells); INIT_WORK(&net->cells_manager, afs_manage_cells);
timer_setup(&net->cells_timer, afs_cells_timer, 0); timer_setup(&net->cells_timer, afs_cells_timer, 0);
spin_lock_init(&net->proc_cells_lock); mutex_init(&net->proc_cells_lock);
INIT_LIST_HEAD(&net->proc_cells); INIT_LIST_HEAD(&net->proc_cells);
seqlock_init(&net->fs_lock); seqlock_init(&net->fs_lock);
...@@ -142,8 +145,10 @@ static int __net_init afs_net_init(struct afs_net *net) ...@@ -142,8 +145,10 @@ static int __net_init afs_net_init(struct afs_net *net)
/* /*
* Clean up and destroy an AFS network namespace record. * Clean up and destroy an AFS network namespace record.
*/ */
static void __net_exit afs_net_exit(struct afs_net *net) static void __net_exit afs_net_exit(struct net *net_ns)
{ {
struct afs_net *net = afs_net(net_ns);
net->live = false; net->live = false;
afs_cell_purge(net); afs_cell_purge(net);
afs_purge_servers(net); afs_purge_servers(net);
...@@ -152,6 +157,13 @@ static void __net_exit afs_net_exit(struct afs_net *net) ...@@ -152,6 +157,13 @@ static void __net_exit afs_net_exit(struct afs_net *net)
afs_put_sysnames(net->sysnames); afs_put_sysnames(net->sysnames);
} }
static struct pernet_operations afs_net_ops = {
.init = afs_net_init,
.exit = afs_net_exit,
.id = &afs_net_id,
.size = sizeof(struct afs_net),
};
/* /*
* initialise the AFS client FS module * initialise the AFS client FS module
*/ */
...@@ -178,7 +190,7 @@ static int __init afs_init(void) ...@@ -178,7 +190,7 @@ static int __init afs_init(void)
goto error_cache; goto error_cache;
#endif #endif
ret = afs_net_init(&__afs_net); ret = register_pernet_subsys(&afs_net_ops);
if (ret < 0) if (ret < 0)
goto error_net; goto error_net;
...@@ -187,10 +199,18 @@ static int __init afs_init(void) ...@@ -187,10 +199,18 @@ static int __init afs_init(void)
if (ret < 0) if (ret < 0)
goto error_fs; goto error_fs;
afs_proc_symlink = proc_symlink("fs/afs", NULL, "../self/net/afs");
if (IS_ERR(afs_proc_symlink)) {
ret = PTR_ERR(afs_proc_symlink);
goto error_proc;
}
return ret; return ret;
error_proc:
afs_fs_exit();
error_fs: error_fs:
afs_net_exit(&__afs_net); unregister_pernet_subsys(&afs_net_ops);
error_net: error_net:
#ifdef CONFIG_AFS_FSCACHE #ifdef CONFIG_AFS_FSCACHE
fscache_unregister_netfs(&afs_cache_netfs); fscache_unregister_netfs(&afs_cache_netfs);
...@@ -219,8 +239,9 @@ static void __exit afs_exit(void) ...@@ -219,8 +239,9 @@ static void __exit afs_exit(void)
{ {
printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 unregistering.\n"); printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 unregistering.\n");
proc_remove(afs_proc_symlink);
afs_fs_exit(); afs_fs_exit();
afs_net_exit(&__afs_net); unregister_pernet_subsys(&afs_net_ops);
#ifdef CONFIG_AFS_FSCACHE #ifdef CONFIG_AFS_FSCACHE
fscache_unregister_netfs(&afs_cache_netfs); fscache_unregister_netfs(&afs_cache_netfs);
#endif #endif
......
...@@ -17,8 +17,8 @@ ...@@ -17,8 +17,8 @@
* - maxbufs must be at least 1 * - maxbufs must be at least 1
* - returns the number of interface records in the buffer * - returns the number of interface records in the buffer
*/ */
int afs_get_ipv4_interfaces(struct afs_interface *bufs, size_t maxbufs, int afs_get_ipv4_interfaces(struct afs_net *net, struct afs_interface *bufs,
bool wantloopback) size_t maxbufs, bool wantloopback)
{ {
struct net_device *dev; struct net_device *dev;
struct in_device *idev; struct in_device *idev;
...@@ -27,7 +27,7 @@ int afs_get_ipv4_interfaces(struct afs_interface *bufs, size_t maxbufs, ...@@ -27,7 +27,7 @@ int afs_get_ipv4_interfaces(struct afs_interface *bufs, size_t maxbufs,
ASSERT(maxbufs > 0); ASSERT(maxbufs > 0);
rtnl_lock(); rtnl_lock();
for_each_netdev(&init_net, dev) { for_each_netdev(net->net, dev) {
if (dev->type == ARPHRD_LOOPBACK && !wantloopback) if (dev->type == ARPHRD_LOOPBACK && !wantloopback)
continue; continue;
idev = __in_dev_get_rtnl(dev); idev = __in_dev_get_rtnl(dev);
......
This diff is collapsed.
...@@ -46,7 +46,7 @@ int afs_open_socket(struct afs_net *net) ...@@ -46,7 +46,7 @@ int afs_open_socket(struct afs_net *net)
_enter(""); _enter("");
ret = sock_create_kern(&init_net, AF_RXRPC, SOCK_DGRAM, PF_INET6, &socket); ret = sock_create_kern(net->net, AF_RXRPC, SOCK_DGRAM, PF_INET6, &socket);
if (ret < 0) if (ret < 0)
goto error_1; goto error_1;
......
...@@ -228,7 +228,7 @@ static struct afs_server *afs_alloc_server(struct afs_net *net, ...@@ -228,7 +228,7 @@ static struct afs_server *afs_alloc_server(struct afs_net *net,
server->flags = (1UL << AFS_SERVER_FL_NEW); server->flags = (1UL << AFS_SERVER_FL_NEW);
server->update_at = ktime_get_real_seconds() + afs_server_update_delay; server->update_at = ktime_get_real_seconds() + afs_server_update_delay;
rwlock_init(&server->fs_lock); rwlock_init(&server->fs_lock);
INIT_LIST_HEAD(&server->cb_interests); INIT_HLIST_HEAD(&server->cb_volumes);
rwlock_init(&server->cb_break_lock); rwlock_init(&server->cb_break_lock);
afs_inc_servers_outstanding(net); afs_inc_servers_outstanding(net);
......
...@@ -48,6 +48,8 @@ struct file_system_type afs_fs_type = { ...@@ -48,6 +48,8 @@ struct file_system_type afs_fs_type = {
}; };
MODULE_ALIAS_FS("afs"); MODULE_ALIAS_FS("afs");
int afs_net_id;
static const struct super_operations afs_super_ops = { static const struct super_operations afs_super_ops = {
.statfs = afs_statfs, .statfs = afs_statfs,
.alloc_inode = afs_alloc_inode, .alloc_inode = afs_alloc_inode,
...@@ -117,7 +119,7 @@ int __init afs_fs_init(void) ...@@ -117,7 +119,7 @@ int __init afs_fs_init(void)
/* /*
* clean up the filesystem * clean up the filesystem
*/ */
void __exit afs_fs_exit(void) void afs_fs_exit(void)
{ {
_enter(""); _enter("");
...@@ -351,14 +353,19 @@ static int afs_test_super(struct super_block *sb, void *data) ...@@ -351,14 +353,19 @@ 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 && return (as->net_ns == as1->net_ns &&
as->volume && as->volume &&
as->volume->vid == as1->volume->vid); as->volume->vid == as1->volume->vid &&
!as->dyn_root);
} }
static int afs_dynroot_test_super(struct super_block *sb, void *data) static int afs_dynroot_test_super(struct super_block *sb, void *data)
{ {
return false; struct afs_super_info *as1 = data;
struct afs_super_info *as = AFS_FS_S(sb);
return (as->net_ns == as1->net_ns &&
as->dyn_root);
} }
static int afs_set_super(struct super_block *sb, void *data) static int afs_set_super(struct super_block *sb, void *data)
...@@ -418,10 +425,14 @@ static int afs_fill_super(struct super_block *sb, ...@@ -418,10 +425,14 @@ static int afs_fill_super(struct super_block *sb,
if (!sb->s_root) if (!sb->s_root)
goto error; goto error;
if (params->dyn_root) if (as->dyn_root) {
sb->s_d_op = &afs_dynroot_dentry_operations; sb->s_d_op = &afs_dynroot_dentry_operations;
else ret = afs_dynroot_populate(sb);
if (ret < 0)
goto error;
} else {
sb->s_d_op = &afs_fs_dentry_operations; sb->s_d_op = &afs_fs_dentry_operations;
}
_leave(" = 0"); _leave(" = 0");
return 0; return 0;
...@@ -437,7 +448,7 @@ static struct afs_super_info *afs_alloc_sbi(struct afs_mount_params *params) ...@@ -437,7 +448,7 @@ 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_ns = get_net(params->net_ns);
if (params->dyn_root) if (params->dyn_root)
as->dyn_root = true; as->dyn_root = true;
else else
...@@ -450,12 +461,31 @@ static void afs_destroy_sbi(struct afs_super_info *as) ...@@ -450,12 +461,31 @@ static void afs_destroy_sbi(struct afs_super_info *as)
{ {
if (as) { if (as) {
afs_put_volume(as->cell, as->volume); afs_put_volume(as->cell, as->volume);
afs_put_cell(as->net, as->cell); afs_put_cell(afs_net(as->net_ns), as->cell);
afs_put_net(as->net); put_net(as->net_ns);
kfree(as); kfree(as);
} }
} }
static void afs_kill_super(struct super_block *sb)
{
struct afs_super_info *as = AFS_FS_S(sb);
struct afs_net *net = afs_net(as->net_ns);
if (as->dyn_root)
afs_dynroot_depopulate(sb);
/* Clear the callback interests (which will do ilookup5) before
* deactivating the superblock.
*/
if (as->volume)
afs_clear_callback_interests(net, as->volume->servers);
kill_anon_super(sb);
if (as->volume)
afs_deactivate_volume(as->volume);
afs_destroy_sbi(as);
}
/* /*
* get an AFS superblock * get an AFS superblock
*/ */
...@@ -472,12 +502,13 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, ...@@ -472,12 +502,13 @@ static struct dentry *afs_mount(struct file_system_type *fs_type,
_enter(",,%s,%p", dev_name, options); _enter(",,%s,%p", dev_name, options);
memset(&params, 0, sizeof(params)); memset(&params, 0, sizeof(params));
params.net = &__afs_net;
ret = -EINVAL; ret = -EINVAL;
if (current->nsproxy->net_ns != &init_net) if (current->nsproxy->net_ns != &init_net)
goto error; goto error;
params.net_ns = current->nsproxy->net_ns;
params.net = afs_net(params.net_ns);
/* parse the options and device name */ /* parse the options and device name */
if (options) { if (options) {
ret = afs_parse_options(&params, options, &dev_name); ret = afs_parse_options(&params, options, &dev_name);
...@@ -563,21 +594,6 @@ static struct dentry *afs_mount(struct file_system_type *fs_type, ...@@ -563,21 +594,6 @@ static struct dentry *afs_mount(struct file_system_type *fs_type,
return ERR_PTR(ret); return ERR_PTR(ret);
} }
static void afs_kill_super(struct super_block *sb)
{
struct afs_super_info *as = AFS_FS_S(sb);
/* Clear the callback interests (which will do ilookup5) before
* deactivating the superblock.
*/
if (as->volume)
afs_clear_callback_interests(as->net, as->volume->servers);
kill_anon_super(sb);
if (as->volume)
afs_deactivate_volume(as->volume);
afs_destroy_sbi(as);
}
/* /*
* Initialise an inode cache slab element prior to any use. Note that * Initialise an inode cache slab element prior to any use. Note that
* afs_alloc_inode() *must* reset anything that could incorrectly leak from one * afs_alloc_inode() *must* reset anything that could incorrectly leak from one
......
...@@ -2463,6 +2463,35 @@ static int lookup_one_len_common(const char *name, struct dentry *base, ...@@ -2463,6 +2463,35 @@ static int lookup_one_len_common(const char *name, struct dentry *base,
return inode_permission(base->d_inode, MAY_EXEC); return inode_permission(base->d_inode, MAY_EXEC);
} }
/**
* try_lookup_one_len - filesystem helper to lookup single pathname component
* @name: pathname component to lookup
* @base: base directory to lookup from
* @len: maximum length @len should be interpreted to
*
* Look up a dentry by name in the dcache, returning NULL if it does not
* currently exist. The function does not try to create a dentry.
*
* Note that this routine is purely a helper for filesystem usage and should
* not be called by generic code.
*
* The caller must hold base->i_mutex.
*/
struct dentry *try_lookup_one_len(const char *name, struct dentry *base, int len)
{
struct qstr this;
int err;
WARN_ON_ONCE(!inode_is_locked(base->d_inode));
err = lookup_one_len_common(name, base, len, &this);
if (err)
return ERR_PTR(err);
return lookup_dcache(&this, base, 0);
}
EXPORT_SYMBOL(try_lookup_one_len);
/** /**
* lookup_one_len - filesystem helper to lookup single pathname component * lookup_one_len - filesystem helper to lookup single pathname component
* @name: pathname component to lookup * @name: pathname component to lookup
......
...@@ -409,7 +409,7 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent, ...@@ -409,7 +409,7 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
if (!ent) if (!ent)
goto out; goto out;
if (qstr.len + 1 <= sizeof(ent->inline_name)) { if (qstr.len + 1 <= SIZEOF_PDE_INLINE_NAME) {
ent->name = ent->inline_name; ent->name = ent->inline_name;
} else { } else {
ent->name = kmalloc(qstr.len + 1, GFP_KERNEL); ent->name = kmalloc(qstr.len + 1, GFP_KERNEL);
...@@ -740,3 +740,27 @@ void *PDE_DATA(const struct inode *inode) ...@@ -740,3 +740,27 @@ void *PDE_DATA(const struct inode *inode)
return __PDE_DATA(inode); return __PDE_DATA(inode);
} }
EXPORT_SYMBOL(PDE_DATA); EXPORT_SYMBOL(PDE_DATA);
/*
* Pull a user buffer into memory and pass it to the file's write handler if
* one is supplied. The ->write() method is permitted to modify the
* kernel-side buffer.
*/
ssize_t proc_simple_write(struct file *f, const char __user *ubuf, size_t size,
loff_t *_pos)
{
struct proc_dir_entry *pde = PDE(file_inode(f));
char *buf;
int ret;
if (!pde->write)
return -EACCES;
if (size == 0 || size > PAGE_SIZE - 1)
return -EINVAL;
buf = memdup_user_nul(ubuf, size);
if (IS_ERR(buf))
return PTR_ERR(buf);
ret = pde->write(f, buf, size);
kfree(buf);
return ret == 0 ? size : ret;
}
...@@ -105,9 +105,8 @@ void __init proc_init_kmemcache(void) ...@@ -105,9 +105,8 @@ void __init proc_init_kmemcache(void)
kmem_cache_create("pde_opener", sizeof(struct pde_opener), 0, kmem_cache_create("pde_opener", sizeof(struct pde_opener), 0,
SLAB_ACCOUNT|SLAB_PANIC, NULL); SLAB_ACCOUNT|SLAB_PANIC, NULL);
proc_dir_entry_cache = kmem_cache_create_usercopy( proc_dir_entry_cache = kmem_cache_create_usercopy(
"proc_dir_entry", sizeof(struct proc_dir_entry), 0, SLAB_PANIC, "proc_dir_entry", SIZEOF_PDE_SLOT, 0, SLAB_PANIC,
offsetof(struct proc_dir_entry, inline_name), OFFSETOF_PDE_NAME, SIZEOF_PDE_INLINE_NAME, NULL);
sizeof_field(struct proc_dir_entry, inline_name), NULL);
} }
static int proc_show_options(struct seq_file *seq, struct dentry *root) static int proc_show_options(struct seq_file *seq, struct dentry *root)
......
...@@ -48,6 +48,7 @@ struct proc_dir_entry { ...@@ -48,6 +48,7 @@ struct proc_dir_entry {
const struct seq_operations *seq_ops; const struct seq_operations *seq_ops;
int (*single_show)(struct seq_file *, void *); int (*single_show)(struct seq_file *, void *);
}; };
proc_write_t write;
void *data; void *data;
unsigned int state_size; unsigned int state_size;
unsigned int low_ino; unsigned int low_ino;
...@@ -61,14 +62,20 @@ struct proc_dir_entry { ...@@ -61,14 +62,20 @@ struct proc_dir_entry {
char *name; char *name;
umode_t mode; umode_t mode;
u8 namelen; u8 namelen;
#ifdef CONFIG_64BIT char inline_name[];
#define SIZEOF_PDE_INLINE_NAME (192-155)
#else
#define SIZEOF_PDE_INLINE_NAME (128-95)
#endif
char inline_name[SIZEOF_PDE_INLINE_NAME];
} __randomize_layout; } __randomize_layout;
#define OFFSETOF_PDE_NAME offsetof(struct proc_dir_entry, inline_name)
#define SIZEOF_PDE_SLOT \
(OFFSETOF_PDE_NAME + 34 <= 64 ? 64 : \
OFFSETOF_PDE_NAME + 34 <= 128 ? 128 : \
OFFSETOF_PDE_NAME + 34 <= 192 ? 192 : \
OFFSETOF_PDE_NAME + 34 <= 256 ? 256 : \
OFFSETOF_PDE_NAME + 34 <= 512 ? 512 : \
0)
#define SIZEOF_PDE_INLINE_NAME (SIZEOF_PDE_SLOT - OFFSETOF_PDE_NAME)
extern struct kmem_cache *proc_dir_entry_cache; extern struct kmem_cache *proc_dir_entry_cache;
void pde_free(struct proc_dir_entry *pde); void pde_free(struct proc_dir_entry *pde);
...@@ -189,6 +196,7 @@ static inline bool is_empty_pde(const struct proc_dir_entry *pde) ...@@ -189,6 +196,7 @@ static inline bool is_empty_pde(const struct proc_dir_entry *pde)
{ {
return S_ISDIR(pde->mode) && !pde->proc_iops; return S_ISDIR(pde->mode) && !pde->proc_iops;
} }
extern ssize_t proc_simple_write(struct file *, const char __user *, size_t, loff_t *);
/* /*
* inode.c * inode.c
......
...@@ -46,6 +46,9 @@ static int seq_open_net(struct inode *inode, struct file *file) ...@@ -46,6 +46,9 @@ static int seq_open_net(struct inode *inode, struct file *file)
WARN_ON_ONCE(state_size < sizeof(*p)); WARN_ON_ONCE(state_size < sizeof(*p));
if (file->f_mode & FMODE_WRITE && !PDE(inode)->write)
return -EACCES;
net = get_proc_net(inode); net = get_proc_net(inode);
if (!net) if (!net)
return -ENXIO; return -ENXIO;
...@@ -73,6 +76,7 @@ static int seq_release_net(struct inode *ino, struct file *f) ...@@ -73,6 +76,7 @@ static int seq_release_net(struct inode *ino, struct file *f)
static const struct file_operations proc_net_seq_fops = { static const struct file_operations proc_net_seq_fops = {
.open = seq_open_net, .open = seq_open_net,
.read = seq_read, .read = seq_read,
.write = proc_simple_write,
.llseek = seq_lseek, .llseek = seq_lseek,
.release = seq_release_net, .release = seq_release_net,
}; };
...@@ -93,6 +97,50 @@ struct proc_dir_entry *proc_create_net_data(const char *name, umode_t mode, ...@@ -93,6 +97,50 @@ struct proc_dir_entry *proc_create_net_data(const char *name, umode_t mode,
} }
EXPORT_SYMBOL_GPL(proc_create_net_data); EXPORT_SYMBOL_GPL(proc_create_net_data);
/**
* proc_create_net_data_write - Create a writable net_ns-specific proc file
* @name: The name of the file.
* @mode: The file's access mode.
* @parent: The parent directory in which to create.
* @ops: The seq_file ops with which to read the file.
* @write: The write method which which to 'modify' the file.
* @data: Data for retrieval by PDE_DATA().
*
* Create a network namespaced proc file in the @parent directory with the
* specified @name and @mode that allows reading of a file that displays a
* series of elements and also provides for the file accepting writes that have
* some arbitrary effect.
*
* The functions in the @ops table are used to iterate over items to be
* presented and extract the readable content using the seq_file interface.
*
* The @write function is called with the data copied into a kernel space
* scratch buffer and has a NUL appended for convenience. The buffer may be
* modified by the @write function. @write should return 0 on success.
*
* The @data value is accessible from the @show and @write functions by calling
* PDE_DATA() on the file inode. The network namespace must be accessed by
* calling seq_file_net() on the seq_file struct.
*/
struct proc_dir_entry *proc_create_net_data_write(const char *name, umode_t mode,
struct proc_dir_entry *parent,
const struct seq_operations *ops,
proc_write_t write,
unsigned int state_size, void *data)
{
struct proc_dir_entry *p;
p = proc_create_reg(name, mode, &parent, data);
if (!p)
return NULL;
p->proc_fops = &proc_net_seq_fops;
p->seq_ops = ops;
p->state_size = state_size;
p->write = write;
return proc_register(parent, p);
}
EXPORT_SYMBOL_GPL(proc_create_net_data_write);
static int single_open_net(struct inode *inode, struct file *file) static int single_open_net(struct inode *inode, struct file *file)
{ {
struct proc_dir_entry *de = PDE(inode); struct proc_dir_entry *de = PDE(inode);
...@@ -119,6 +167,7 @@ static int single_release_net(struct inode *ino, struct file *f) ...@@ -119,6 +167,7 @@ static int single_release_net(struct inode *ino, struct file *f)
static const struct file_operations proc_net_single_fops = { static const struct file_operations proc_net_single_fops = {
.open = single_open_net, .open = single_open_net,
.read = seq_read, .read = seq_read,
.write = proc_simple_write,
.llseek = seq_lseek, .llseek = seq_lseek,
.release = single_release_net, .release = single_release_net,
}; };
...@@ -138,6 +187,49 @@ struct proc_dir_entry *proc_create_net_single(const char *name, umode_t mode, ...@@ -138,6 +187,49 @@ struct proc_dir_entry *proc_create_net_single(const char *name, umode_t mode,
} }
EXPORT_SYMBOL_GPL(proc_create_net_single); EXPORT_SYMBOL_GPL(proc_create_net_single);
/**
* proc_create_net_single_write - Create a writable net_ns-specific proc file
* @name: The name of the file.
* @mode: The file's access mode.
* @parent: The parent directory in which to create.
* @show: The seqfile show method with which to read the file.
* @write: The write method which which to 'modify' the file.
* @data: Data for retrieval by PDE_DATA().
*
* Create a network-namespaced proc file in the @parent directory with the
* specified @name and @mode that allows reading of a file that displays a
* single element rather than a series and also provides for the file accepting
* writes that have some arbitrary effect.
*
* The @show function is called to extract the readable content via the
* seq_file interface.
*
* The @write function is called with the data copied into a kernel space
* scratch buffer and has a NUL appended for convenience. The buffer may be
* modified by the @write function. @write should return 0 on success.
*
* The @data value is accessible from the @show and @write functions by calling
* PDE_DATA() on the file inode. The network namespace must be accessed by
* calling seq_file_single_net() on the seq_file struct.
*/
struct proc_dir_entry *proc_create_net_single_write(const char *name, umode_t mode,
struct proc_dir_entry *parent,
int (*show)(struct seq_file *, void *),
proc_write_t write,
void *data)
{
struct proc_dir_entry *p;
p = proc_create_reg(name, mode, &parent, data);
if (!p)
return NULL;
p->proc_fops = &proc_net_single_fops;
p->single_show = show;
p->write = write;
return proc_register(parent, p);
}
EXPORT_SYMBOL_GPL(proc_create_net_single_write);
static struct net *get_proc_task_net(struct inode *dir) static struct net *get_proc_task_net(struct inode *dir)
{ {
struct task_struct *task; struct task_struct *task;
......
...@@ -204,8 +204,7 @@ struct proc_dir_entry proc_root = { ...@@ -204,8 +204,7 @@ struct proc_dir_entry proc_root = {
.proc_fops = &proc_root_operations, .proc_fops = &proc_root_operations,
.parent = &proc_root, .parent = &proc_root,
.subdir = RB_ROOT, .subdir = RB_ROOT,
.name = proc_root.inline_name, .name = "/proc",
.inline_name = "/proc",
}; };
int pid_ns_prepare_proc(struct pid_namespace *ns) int pid_ns_prepare_proc(struct pid_namespace *ns)
......
...@@ -81,6 +81,7 @@ extern void done_path_create(struct path *, struct dentry *); ...@@ -81,6 +81,7 @@ extern void done_path_create(struct path *, struct dentry *);
extern struct dentry *kern_path_locked(const char *, struct path *); extern struct dentry *kern_path_locked(const char *, struct path *);
extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int); extern int kern_path_mountpoint(int, const char *, struct path *, unsigned int);
extern struct dentry *try_lookup_one_len(const char *, struct dentry *, int);
extern struct dentry *lookup_one_len(const char *, struct dentry *, int); extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int); extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int);
......
...@@ -14,6 +14,8 @@ struct seq_operations; ...@@ -14,6 +14,8 @@ struct seq_operations;
#ifdef CONFIG_PROC_FS #ifdef CONFIG_PROC_FS
typedef int (*proc_write_t)(struct file *, char *, size_t);
extern void proc_root_init(void); extern void proc_root_init(void);
extern void proc_flush_task(struct task_struct *); extern void proc_flush_task(struct task_struct *);
...@@ -61,6 +63,16 @@ struct proc_dir_entry *proc_create_net_data(const char *name, umode_t mode, ...@@ -61,6 +63,16 @@ struct proc_dir_entry *proc_create_net_data(const char *name, umode_t mode,
struct proc_dir_entry *proc_create_net_single(const char *name, umode_t mode, struct proc_dir_entry *proc_create_net_single(const char *name, umode_t mode,
struct proc_dir_entry *parent, struct proc_dir_entry *parent,
int (*show)(struct seq_file *, void *), void *data); int (*show)(struct seq_file *, void *), void *data);
struct proc_dir_entry *proc_create_net_data_write(const char *name, umode_t mode,
struct proc_dir_entry *parent,
const struct seq_operations *ops,
proc_write_t write,
unsigned int state_size, void *data);
struct proc_dir_entry *proc_create_net_single_write(const char *name, umode_t mode,
struct proc_dir_entry *parent,
int (*show)(struct seq_file *, void *),
proc_write_t write,
void *data);
#else /* CONFIG_PROC_FS */ #else /* CONFIG_PROC_FS */
......
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