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);
......
...@@ -17,240 +17,78 @@ ...@@ -17,240 +17,78 @@
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include "internal.h" #include "internal.h"
static inline struct afs_net *afs_proc2net(struct file *f) static inline struct afs_net *afs_seq2net(struct seq_file *m)
{ {
return &__afs_net; return afs_net(seq_file_net(m));
} }
static inline struct afs_net *afs_seq2net(struct seq_file *m) static inline struct afs_net *afs_seq2net_single(struct seq_file *m)
{ {
return &__afs_net; // TODO: use seq_file_net(m) return afs_net(seq_file_single_net(m));
} }
static int afs_proc_cells_open(struct inode *inode, struct file *file);
static void *afs_proc_cells_start(struct seq_file *p, loff_t *pos);
static void *afs_proc_cells_next(struct seq_file *p, void *v, loff_t *pos);
static void afs_proc_cells_stop(struct seq_file *p, void *v);
static int afs_proc_cells_show(struct seq_file *m, void *v);
static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
size_t size, loff_t *_pos);
static const struct seq_operations afs_proc_cells_ops = {
.start = afs_proc_cells_start,
.next = afs_proc_cells_next,
.stop = afs_proc_cells_stop,
.show = afs_proc_cells_show,
};
static const struct file_operations afs_proc_cells_fops = {
.open = afs_proc_cells_open,
.read = seq_read,
.write = afs_proc_cells_write,
.llseek = seq_lseek,
.release = seq_release,
};
static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf,
size_t size, loff_t *_pos);
static ssize_t afs_proc_rootcell_write(struct file *file,
const char __user *buf,
size_t size, loff_t *_pos);
static const struct file_operations afs_proc_rootcell_fops = {
.read = afs_proc_rootcell_read,
.write = afs_proc_rootcell_write,
.llseek = no_llseek,
};
static void *afs_proc_cell_volumes_start(struct seq_file *p, loff_t *pos);
static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v,
loff_t *pos);
static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v);
static int afs_proc_cell_volumes_show(struct seq_file *m, void *v);
static const struct seq_operations afs_proc_cell_volumes_ops = {
.start = afs_proc_cell_volumes_start,
.next = afs_proc_cell_volumes_next,
.stop = afs_proc_cell_volumes_stop,
.show = afs_proc_cell_volumes_show,
};
static void *afs_proc_cell_vlservers_start(struct seq_file *p, loff_t *pos);
static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v,
loff_t *pos);
static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v);
static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v);
static const struct seq_operations afs_proc_cell_vlservers_ops = {
.start = afs_proc_cell_vlservers_start,
.next = afs_proc_cell_vlservers_next,
.stop = afs_proc_cell_vlservers_stop,
.show = afs_proc_cell_vlservers_show,
};
static void *afs_proc_servers_start(struct seq_file *p, loff_t *pos);
static void *afs_proc_servers_next(struct seq_file *p, void *v,
loff_t *pos);
static void afs_proc_servers_stop(struct seq_file *p, void *v);
static int afs_proc_servers_show(struct seq_file *m, void *v);
static const struct seq_operations afs_proc_servers_ops = {
.start = afs_proc_servers_start,
.next = afs_proc_servers_next,
.stop = afs_proc_servers_stop,
.show = afs_proc_servers_show,
};
static int afs_proc_sysname_open(struct inode *inode, struct file *file);
static int afs_proc_sysname_release(struct inode *inode, struct file *file);
static void *afs_proc_sysname_start(struct seq_file *p, loff_t *pos);
static void *afs_proc_sysname_next(struct seq_file *p, void *v,
loff_t *pos);
static void afs_proc_sysname_stop(struct seq_file *p, void *v);
static int afs_proc_sysname_show(struct seq_file *m, void *v);
static ssize_t afs_proc_sysname_write(struct file *file,
const char __user *buf,
size_t size, loff_t *_pos);
static const struct seq_operations afs_proc_sysname_ops = {
.start = afs_proc_sysname_start,
.next = afs_proc_sysname_next,
.stop = afs_proc_sysname_stop,
.show = afs_proc_sysname_show,
};
static const struct file_operations afs_proc_sysname_fops = {
.open = afs_proc_sysname_open,
.read = seq_read,
.llseek = seq_lseek,
.release = afs_proc_sysname_release,
.write = afs_proc_sysname_write,
};
static int afs_proc_stats_show(struct seq_file *m, void *v);
/* /*
* initialise the /proc/fs/afs/ directory * Display the list of cells known to the namespace.
*/ */
int afs_proc_init(struct afs_net *net) static int afs_proc_cells_show(struct seq_file *m, void *v)
{ {
_enter(""); struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link);
struct afs_net *net = afs_seq2net(m);
net->proc_afs = proc_mkdir("fs/afs", NULL);
if (!net->proc_afs)
goto error_dir;
if (!proc_create("cells", 0644, net->proc_afs, &afs_proc_cells_fops) || if (v == &net->proc_cells) {
!proc_create("rootcell", 0644, net->proc_afs, &afs_proc_rootcell_fops) || /* display header on line 1 */
!proc_create_seq("servers", 0644, net->proc_afs, &afs_proc_servers_ops) || seq_puts(m, "USE NAME\n");
!proc_create_single("stats", 0644, net->proc_afs, afs_proc_stats_show) || return 0;
!proc_create("sysname", 0644, net->proc_afs, &afs_proc_sysname_fops)) }
goto error_tree;
_leave(" = 0"); /* display one cell per line on subsequent lines */
seq_printf(m, "%3u %s\n", atomic_read(&cell->usage), cell->name);
return 0; return 0;
error_tree:
proc_remove(net->proc_afs);
error_dir:
_leave(" = -ENOMEM");
return -ENOMEM;
}
/*
* clean up the /proc/fs/afs/ directory
*/
void afs_proc_cleanup(struct afs_net *net)
{
proc_remove(net->proc_afs);
net->proc_afs = NULL;
}
/*
* open "/proc/fs/afs/cells" which provides a summary of extant cells
*/
static int afs_proc_cells_open(struct inode *inode, struct file *file)
{
return seq_open(file, &afs_proc_cells_ops);
} }
/*
* set up the iterator to start reading from the cells list and return the
* first item
*/
static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos) static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
__acquires(rcu) __acquires(rcu)
{ {
struct afs_net *net = afs_seq2net(m);
rcu_read_lock(); rcu_read_lock();
return seq_list_start_head(&net->proc_cells, *_pos); return seq_list_start_head(&afs_seq2net(m)->proc_cells, *_pos);
} }
/*
* move to next cell in cells list
*/
static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos) static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
{ {
struct afs_net *net = afs_seq2net(m); return seq_list_next(v, &afs_seq2net(m)->proc_cells, pos);
return seq_list_next(v, &net->proc_cells, pos);
} }
/*
* clean up after reading from the cells list
*/
static void afs_proc_cells_stop(struct seq_file *m, void *v) static void afs_proc_cells_stop(struct seq_file *m, void *v)
__releases(rcu) __releases(rcu)
{ {
rcu_read_unlock(); rcu_read_unlock();
} }
/* static const struct seq_operations afs_proc_cells_ops = {
* display a header line followed by a load of cell lines .start = afs_proc_cells_start,
*/ .next = afs_proc_cells_next,
static int afs_proc_cells_show(struct seq_file *m, void *v) .stop = afs_proc_cells_stop,
{ .show = afs_proc_cells_show,
struct afs_cell *cell = list_entry(v, struct afs_cell, proc_link); };
struct afs_net *net = afs_seq2net(m);
if (v == &net->proc_cells) {
/* display header on line 1 */
seq_puts(m, "USE NAME\n");
return 0;
}
/* display one cell per line on subsequent lines */
seq_printf(m, "%3u %s\n", atomic_read(&cell->usage), cell->name);
return 0;
}
/* /*
* handle writes to /proc/fs/afs/cells * handle writes to /proc/fs/afs/cells
* - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]" * - to add cells: echo "add <cellname> <IP>[:<IP>][:<IP>]"
*/ */
static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf, static int afs_proc_cells_write(struct file *file, char *buf, size_t size)
size_t size, loff_t *_pos)
{ {
struct afs_net *net = afs_proc2net(file); struct seq_file *m = file->private_data;
char *kbuf, *name, *args; struct afs_net *net = afs_seq2net(m);
char *name, *args;
int ret; int ret;
/* start by dragging the command into memory */
if (size <= 1 || size >= PAGE_SIZE)
return -EINVAL;
kbuf = memdup_user_nul(buf, size);
if (IS_ERR(kbuf))
return PTR_ERR(kbuf);
/* trim to first NL */ /* trim to first NL */
name = memchr(kbuf, '\n', size); name = memchr(buf, '\n', size);
if (name) if (name)
*name = 0; *name = 0;
/* split into command, name and argslist */ /* split into command, name and argslist */
name = strchr(kbuf, ' '); name = strchr(buf, ' ');
if (!name) if (!name)
goto inval; goto inval;
do { do {
...@@ -269,9 +107,9 @@ static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf, ...@@ -269,9 +107,9 @@ static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
goto inval; goto inval;
/* determine command to perform */ /* determine command to perform */
_debug("cmd=%s name=%s args=%s", kbuf, name, args); _debug("cmd=%s name=%s args=%s", buf, name, args);
if (strcmp(kbuf, "add") == 0) { if (strcmp(buf, "add") == 0) {
struct afs_cell *cell; struct afs_cell *cell;
cell = afs_lookup_cell(net, name, strlen(name), args, true); cell = afs_lookup_cell(net, name, strlen(name), args, true);
...@@ -287,10 +125,9 @@ static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf, ...@@ -287,10 +125,9 @@ static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
goto inval; goto inval;
} }
ret = size; ret = 0;
done: done:
kfree(kbuf);
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
...@@ -300,200 +137,136 @@ static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf, ...@@ -300,200 +137,136 @@ static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
goto done; goto done;
} }
static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf, /*
size_t size, loff_t *_pos) * Display the name of the current workstation cell.
*/
static int afs_proc_rootcell_show(struct seq_file *m, void *v)
{ {
struct afs_cell *cell; struct afs_cell *cell;
struct afs_net *net = afs_proc2net(file); struct afs_net *net;
unsigned int seq = 0;
char name[AFS_MAXCELLNAME + 1]; net = afs_seq2net_single(m);
int len; if (rcu_access_pointer(net->ws_cell)) {
rcu_read_lock();
if (*_pos > 0) cell = rcu_dereference(net->ws_cell);
return 0; if (cell)
if (!net->ws_cell) seq_printf(m, "%s\n", cell->name);
return 0; rcu_read_unlock();
}
rcu_read_lock(); return 0;
do {
read_seqbegin_or_lock(&net->cells_lock, &seq);
len = 0;
cell = rcu_dereference_raw(net->ws_cell);
if (cell) {
len = cell->name_len;
memcpy(name, cell->name, len);
}
} while (need_seqretry(&net->cells_lock, seq));
done_seqretry(&net->cells_lock, seq);
rcu_read_unlock();
if (!len)
return 0;
name[len++] = '\n';
if (len > size)
len = size;
if (copy_to_user(buf, name, len) != 0)
return -EFAULT;
*_pos = 1;
return len;
} }
/* /*
* handle writes to /proc/fs/afs/rootcell * Set the current workstation cell and optionally supply its list of volume
* - to initialize rootcell: echo "cell.name:192.168.231.14" * location servers.
*
* echo "cell.name:192.168.231.14" >/proc/fs/afs/rootcell
*/ */
static ssize_t afs_proc_rootcell_write(struct file *file, static int afs_proc_rootcell_write(struct file *file, char *buf, size_t size)
const char __user *buf,
size_t size, loff_t *_pos)
{ {
struct afs_net *net = afs_proc2net(file); struct seq_file *m = file->private_data;
char *kbuf, *s; struct afs_net *net = afs_seq2net_single(m);
char *s;
int ret; int ret;
/* start by dragging the command into memory */
if (size <= 1 || size >= PAGE_SIZE)
return -EINVAL;
kbuf = memdup_user_nul(buf, size);
if (IS_ERR(kbuf))
return PTR_ERR(kbuf);
ret = -EINVAL; ret = -EINVAL;
if (kbuf[0] == '.') if (buf[0] == '.')
goto out; goto out;
if (memchr(kbuf, '/', size)) if (memchr(buf, '/', size))
goto out; goto out;
/* trim to first NL */ /* trim to first NL */
s = memchr(kbuf, '\n', size); s = memchr(buf, '\n', size);
if (s) if (s)
*s = 0; *s = 0;
/* determine command to perform */ /* determine command to perform */
_debug("rootcell=%s", kbuf); _debug("rootcell=%s", buf);
ret = afs_cell_init(net, kbuf); ret = afs_cell_init(net, buf);
if (ret >= 0)
ret = size; /* consume everything, always */
out: out:
kfree(kbuf);
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
} }
static const char afs_vol_types[3][3] = {
[AFSVL_RWVOL] = "RW",
[AFSVL_ROVOL] = "RO",
[AFSVL_BACKVOL] = "BK",
};
/* /*
* initialise /proc/fs/afs/<cell>/ * Display the list of volumes known to a cell.
*/ */
int afs_proc_cell_setup(struct afs_net *net, struct afs_cell *cell) static int afs_proc_cell_volumes_show(struct seq_file *m, void *v)
{ {
struct proc_dir_entry *dir; struct afs_cell *cell = PDE_DATA(file_inode(m->file));
struct afs_volume *vol = list_entry(v, struct afs_volume, proc_link);
_enter("%p{%s},%p", cell, cell->name, net->proc_afs);
dir = proc_mkdir(cell->name, net->proc_afs); /* Display header on line 1 */
if (!dir) if (v == &cell->proc_volumes) {
goto error_dir; seq_puts(m, "USE VID TY\n");
return 0;
}
if (!proc_create_seq_data("vlservers", 0, dir, seq_printf(m, "%3d %08x %s\n",
&afs_proc_cell_vlservers_ops, cell)) atomic_read(&vol->usage), vol->vid,
goto error_tree; afs_vol_types[vol->type]);
if (!proc_create_seq_data("volumes", 0, dir, &afs_proc_cell_volumes_ops,
cell))
goto error_tree;
_leave(" = 0");
return 0; return 0;
error_tree:
remove_proc_subtree(cell->name, net->proc_afs);
error_dir:
_leave(" = -ENOMEM");
return -ENOMEM;
} }
/*
* remove /proc/fs/afs/<cell>/
*/
void afs_proc_cell_remove(struct afs_net *net, struct afs_cell *cell)
{
_enter("");
remove_proc_subtree(cell->name, net->proc_afs);
_leave("");
}
/*
* set up the iterator to start reading from the cells list and return the
* first item
*/
static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos) static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
__acquires(cell->proc_lock) __acquires(cell->proc_lock)
{ {
struct afs_cell *cell = PDE_DATA(file_inode(m->file)); struct afs_cell *cell = PDE_DATA(file_inode(m->file));
_enter("cell=%p pos=%Ld", cell, *_pos);
read_lock(&cell->proc_lock); read_lock(&cell->proc_lock);
return seq_list_start_head(&cell->proc_volumes, *_pos); return seq_list_start_head(&cell->proc_volumes, *_pos);
} }
/* static void *afs_proc_cell_volumes_next(struct seq_file *m, void *v,
* move to next cell in cells list
*/
static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v,
loff_t *_pos) loff_t *_pos)
{ {
struct afs_cell *cell = PDE_DATA(file_inode(p->file)); struct afs_cell *cell = PDE_DATA(file_inode(m->file));
_enter("cell=%p pos=%Ld", cell, *_pos);
return seq_list_next(v, &cell->proc_volumes, _pos); return seq_list_next(v, &cell->proc_volumes, _pos);
} }
/* static void afs_proc_cell_volumes_stop(struct seq_file *m, void *v)
* clean up after reading from the cells list
*/
static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v)
__releases(cell->proc_lock) __releases(cell->proc_lock)
{ {
struct afs_cell *cell = PDE_DATA(file_inode(p->file)); struct afs_cell *cell = PDE_DATA(file_inode(m->file));
read_unlock(&cell->proc_lock); read_unlock(&cell->proc_lock);
} }
static const char afs_vol_types[3][3] = { static const struct seq_operations afs_proc_cell_volumes_ops = {
[AFSVL_RWVOL] = "RW", .start = afs_proc_cell_volumes_start,
[AFSVL_ROVOL] = "RO", .next = afs_proc_cell_volumes_next,
[AFSVL_BACKVOL] = "BK", .stop = afs_proc_cell_volumes_stop,
.show = afs_proc_cell_volumes_show,
}; };
/* /*
* display a header line followed by a load of volume lines * Display the list of Volume Location servers we're using for a cell.
*/ */
static int afs_proc_cell_volumes_show(struct seq_file *m, void *v) static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v)
{ {
struct afs_cell *cell = PDE_DATA(file_inode(m->file)); struct sockaddr_rxrpc *addr = v;
struct afs_volume *vol = list_entry(v, struct afs_volume, proc_link);
/* Display header on line 1 */ /* display header on line 1 */
if (v == &cell->proc_volumes) { if (v == (void *)1) {
seq_puts(m, "USE VID TY\n"); seq_puts(m, "ADDRESS\n");
return 0; return 0;
} }
seq_printf(m, "%3d %08x %s\n", /* display one cell per line on subsequent lines */
atomic_read(&vol->usage), vol->vid, seq_printf(m, "%pISp\n", &addr->transport);
afs_vol_types[vol->type]);
return 0; return 0;
} }
/*
* set up the iterator to start reading from the cells list and return the
* first item
*/
static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos) static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
__acquires(rcu) __acquires(rcu)
{ {
...@@ -516,14 +289,11 @@ static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos) ...@@ -516,14 +289,11 @@ static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
return alist->addrs + pos; return alist->addrs + pos;
} }
/* static void *afs_proc_cell_vlservers_next(struct seq_file *m, void *v,
* move to next cell in cells list
*/
static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v,
loff_t *_pos) loff_t *_pos)
{ {
struct afs_addr_list *alist; struct afs_addr_list *alist;
struct afs_cell *cell = PDE_DATA(file_inode(p->file)); struct afs_cell *cell = PDE_DATA(file_inode(m->file));
loff_t pos; loff_t pos;
alist = rcu_dereference(cell->vl_addrs); alist = rcu_dereference(cell->vl_addrs);
...@@ -536,161 +306,145 @@ static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v, ...@@ -536,161 +306,145 @@ static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v,
return alist->addrs + pos; return alist->addrs + pos;
} }
/* static void afs_proc_cell_vlservers_stop(struct seq_file *m, void *v)
* clean up after reading from the cells list
*/
static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v)
__releases(rcu) __releases(rcu)
{ {
rcu_read_unlock(); rcu_read_unlock();
} }
static const struct seq_operations afs_proc_cell_vlservers_ops = {
.start = afs_proc_cell_vlservers_start,
.next = afs_proc_cell_vlservers_next,
.stop = afs_proc_cell_vlservers_stop,
.show = afs_proc_cell_vlservers_show,
};
/* /*
* display a header line followed by a load of volume lines * Display the list of fileservers we're using within a namespace.
*/ */
static int afs_proc_cell_vlservers_show(struct seq_file *m, void *v) static int afs_proc_servers_show(struct seq_file *m, void *v)
{ {
struct sockaddr_rxrpc *addr = v; struct afs_server *server;
struct afs_addr_list *alist;
int i;
/* display header on line 1 */ if (v == SEQ_START_TOKEN) {
if (v == (void *)1) { seq_puts(m, "UUID USE ADDR\n");
seq_puts(m, "ADDRESS\n");
return 0; return 0;
} }
/* display one cell per line on subsequent lines */ server = list_entry(v, struct afs_server, proc_link);
seq_printf(m, "%pISp\n", &addr->transport); alist = rcu_dereference(server->addresses);
seq_printf(m, "%pU %3d %pISpc%s\n",
&server->uuid,
atomic_read(&server->usage),
&alist->addrs[0].transport,
alist->index == 0 ? "*" : "");
for (i = 1; i < alist->nr_addrs; i++)
seq_printf(m, " %pISpc%s\n",
&alist->addrs[i].transport,
alist->index == i ? "*" : "");
return 0; return 0;
} }
/*
* Set up the iterator to start reading from the server list and return the
* first item.
*/
static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos) static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
__acquires(rcu) __acquires(rcu)
{ {
struct afs_net *net = afs_seq2net(m);
rcu_read_lock(); rcu_read_lock();
return seq_hlist_start_head_rcu(&net->fs_proc, *_pos); return seq_hlist_start_head_rcu(&afs_seq2net(m)->fs_proc, *_pos);
} }
/*
* move to next cell in cells list
*/
static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos) static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos)
{ {
struct afs_net *net = afs_seq2net(m); return seq_hlist_next_rcu(v, &afs_seq2net(m)->fs_proc, _pos);
return seq_hlist_next_rcu(v, &net->fs_proc, _pos);
} }
/* static void afs_proc_servers_stop(struct seq_file *m, void *v)
* clean up after reading from the cells list
*/
static void afs_proc_servers_stop(struct seq_file *p, void *v)
__releases(rcu) __releases(rcu)
{ {
rcu_read_unlock(); rcu_read_unlock();
} }
static const struct seq_operations afs_proc_servers_ops = {
.start = afs_proc_servers_start,
.next = afs_proc_servers_next,
.stop = afs_proc_servers_stop,
.show = afs_proc_servers_show,
};
/* /*
* display a header line followed by a load of volume lines * Display the list of strings that may be substituted for the @sys pathname
* macro.
*/ */
static int afs_proc_servers_show(struct seq_file *m, void *v) static int afs_proc_sysname_show(struct seq_file *m, void *v)
{ {
struct afs_server *server; struct afs_net *net = afs_seq2net(m);
struct afs_addr_list *alist; struct afs_sysnames *sysnames = net->sysnames;
unsigned int i = (unsigned long)v - 1;
if (v == SEQ_START_TOKEN) {
seq_puts(m, "UUID USE ADDR\n");
return 0;
}
server = list_entry(v, struct afs_server, proc_link); if (i < sysnames->nr)
alist = rcu_dereference(server->addresses); seq_printf(m, "%s\n", sysnames->subs[i]);
seq_printf(m, "%pU %3d %pISp\n",
&server->uuid,
atomic_read(&server->usage),
&alist->addrs[alist->index].transport);
return 0; return 0;
} }
void afs_put_sysnames(struct afs_sysnames *sysnames) static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
__acquires(&net->sysnames_lock)
{ {
int i; struct afs_net *net = afs_seq2net(m);
struct afs_sysnames *names;
if (sysnames && refcount_dec_and_test(&sysnames->usage)) { read_lock(&net->sysnames_lock);
for (i = 0; i < sysnames->nr; i++)
if (sysnames->subs[i] != afs_init_sysname && names = net->sysnames;
sysnames->subs[i] != sysnames->blank) if (*pos >= names->nr)
kfree(sysnames->subs[i]); return NULL;
} return (void *)(unsigned long)(*pos + 1);
} }
/* static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
* Handle opening of /proc/fs/afs/sysname. If it is opened for writing, we
* assume the caller wants to change the substitution list and we allocate a
* buffer to hold the list.
*/
static int afs_proc_sysname_open(struct inode *inode, struct file *file)
{ {
struct afs_sysnames *sysnames; struct afs_net *net = afs_seq2net(m);
struct seq_file *m; struct afs_sysnames *names = net->sysnames;
int ret;
ret = seq_open(file, &afs_proc_sysname_ops);
if (ret < 0)
return ret;
if (file->f_mode & FMODE_WRITE) { *pos += 1;
sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL); if (*pos >= names->nr)
if (!sysnames) { return NULL;
seq_release(inode, file); return (void *)(unsigned long)(*pos + 1);
return -ENOMEM; }
}
refcount_set(&sysnames->usage, 1); static void afs_proc_sysname_stop(struct seq_file *m, void *v)
m = file->private_data; __releases(&net->sysnames_lock)
m->private = sysnames; {
} struct afs_net *net = afs_seq2net(m);
return 0; read_unlock(&net->sysnames_lock);
} }
static const struct seq_operations afs_proc_sysname_ops = {
.start = afs_proc_sysname_start,
.next = afs_proc_sysname_next,
.stop = afs_proc_sysname_stop,
.show = afs_proc_sysname_show,
};
/* /*
* Handle writes to /proc/fs/afs/sysname to set the @sys substitution. * Allow the @sys substitution to be configured.
*/ */
static ssize_t afs_proc_sysname_write(struct file *file, static int afs_proc_sysname_write(struct file *file, char *buf, size_t size)
const char __user *buf,
size_t size, loff_t *_pos)
{ {
struct afs_sysnames *sysnames; struct afs_sysnames *sysnames, *kill;
struct seq_file *m = file->private_data; struct seq_file *m = file->private_data;
char *kbuf = NULL, *s, *p, *sub; struct afs_net *net = afs_seq2net(m);
char *s, *p, *sub;
int ret, len; int ret, len;
sysnames = m->private; sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
if (!sysnames) if (!sysnames)
return -EINVAL; return -ENOMEM;
if (sysnames->error) refcount_set(&sysnames->usage, 1);
return sysnames->error; kill = sysnames;
if (size >= PAGE_SIZE - 1) {
sysnames->error = -EINVAL;
return -EINVAL;
}
if (size == 0)
return 0;
kbuf = memdup_user_nul(buf, size);
if (IS_ERR(kbuf))
return PTR_ERR(kbuf);
inode_lock(file_inode(file));
p = kbuf; p = buf;
while ((s = strsep(&p, " \t\n"))) { while ((s = strsep(&p, " \t\n"))) {
len = strlen(s); len = strlen(s);
if (len == 0) if (len == 0)
...@@ -731,85 +485,36 @@ static ssize_t afs_proc_sysname_write(struct file *file, ...@@ -731,85 +485,36 @@ static ssize_t afs_proc_sysname_write(struct file *file,
sysnames->nr++; sysnames->nr++;
} }
ret = size; /* consume everything, always */ if (sysnames->nr == 0) {
sysnames->subs[0] = sysnames->blank;
sysnames->nr++;
}
write_lock(&net->sysnames_lock);
kill = net->sysnames;
net->sysnames = sysnames;
write_unlock(&net->sysnames_lock);
ret = 0;
out: out:
inode_unlock(file_inode(file)); afs_put_sysnames(kill);
kfree(kbuf);
return ret; return ret;
invalid: invalid:
ret = -EINVAL; ret = -EINVAL;
error: error:
sysnames->error = ret;
goto out; goto out;
} }
static int afs_proc_sysname_release(struct inode *inode, struct file *file) void afs_put_sysnames(struct afs_sysnames *sysnames)
{ {
struct afs_sysnames *sysnames, *kill = NULL; int i;
struct seq_file *m = file->private_data;
struct afs_net *net = afs_seq2net(m);
sysnames = m->private; if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
if (sysnames) { for (i = 0; i < sysnames->nr; i++)
if (!sysnames->error) { if (sysnames->subs[i] != afs_init_sysname &&
kill = sysnames; sysnames->subs[i] != sysnames->blank)
if (sysnames->nr == 0) { kfree(sysnames->subs[i]);
sysnames->subs[0] = sysnames->blank;
sysnames->nr++;
}
write_lock(&net->sysnames_lock);
kill = net->sysnames;
net->sysnames = sysnames;
write_unlock(&net->sysnames_lock);
}
afs_put_sysnames(kill);
} }
return seq_release(inode, file);
}
static void *afs_proc_sysname_start(struct seq_file *m, loff_t *pos)
__acquires(&net->sysnames_lock)
{
struct afs_net *net = afs_seq2net(m);
struct afs_sysnames *names = net->sysnames;
read_lock(&net->sysnames_lock);
if (*pos >= names->nr)
return NULL;
return (void *)(unsigned long)(*pos + 1);
}
static void *afs_proc_sysname_next(struct seq_file *m, void *v, loff_t *pos)
{
struct afs_net *net = afs_seq2net(m);
struct afs_sysnames *names = net->sysnames;
*pos += 1;
if (*pos >= names->nr)
return NULL;
return (void *)(unsigned long)(*pos + 1);
}
static void afs_proc_sysname_stop(struct seq_file *m, void *v)
__releases(&net->sysnames_lock)
{
struct afs_net *net = afs_seq2net(m);
read_unlock(&net->sysnames_lock);
}
static int afs_proc_sysname_show(struct seq_file *m, void *v)
{
struct afs_net *net = afs_seq2net(m);
struct afs_sysnames *sysnames = net->sysnames;
unsigned int i = (unsigned long)v - 1;
if (i < sysnames->nr)
seq_printf(m, "%s\n", sysnames->subs[i]);
return 0;
} }
/* /*
...@@ -817,7 +522,7 @@ static int afs_proc_sysname_show(struct seq_file *m, void *v) ...@@ -817,7 +522,7 @@ static int afs_proc_sysname_show(struct seq_file *m, void *v)
*/ */
static int afs_proc_stats_show(struct seq_file *m, void *v) static int afs_proc_stats_show(struct seq_file *m, void *v)
{ {
struct afs_net *net = afs_seq2net(m); struct afs_net *net = afs_seq2net_single(m);
seq_puts(m, "kAFS statistics\n"); seq_puts(m, "kAFS statistics\n");
...@@ -842,3 +547,101 @@ static int afs_proc_stats_show(struct seq_file *m, void *v) ...@@ -842,3 +547,101 @@ static int afs_proc_stats_show(struct seq_file *m, void *v)
atomic_long_read(&net->n_store_bytes)); atomic_long_read(&net->n_store_bytes));
return 0; return 0;
} }
/*
* initialise /proc/fs/afs/<cell>/
*/
int afs_proc_cell_setup(struct afs_cell *cell)
{
struct proc_dir_entry *dir;
struct afs_net *net = cell->net;
_enter("%p{%s},%p", cell, cell->name, net->proc_afs);
dir = proc_net_mkdir(net->net, cell->name, net->proc_afs);
if (!dir)
goto error_dir;
if (!proc_create_net_data("vlservers", 0444, dir,
&afs_proc_cell_vlservers_ops,
sizeof(struct seq_net_private),
cell) ||
!proc_create_net_data("volumes", 0444, dir,
&afs_proc_cell_volumes_ops,
sizeof(struct seq_net_private),
cell))
goto error_tree;
_leave(" = 0");
return 0;
error_tree:
remove_proc_subtree(cell->name, net->proc_afs);
error_dir:
_leave(" = -ENOMEM");
return -ENOMEM;
}
/*
* remove /proc/fs/afs/<cell>/
*/
void afs_proc_cell_remove(struct afs_cell *cell)
{
struct afs_net *net = cell->net;
_enter("");
remove_proc_subtree(cell->name, net->proc_afs);
_leave("");
}
/*
* initialise the /proc/fs/afs/ directory
*/
int afs_proc_init(struct afs_net *net)
{
struct proc_dir_entry *p;
_enter("");
p = proc_net_mkdir(net->net, "afs", net->net->proc_net);
if (!p)
goto error_dir;
if (!proc_create_net_data_write("cells", 0644, p,
&afs_proc_cells_ops,
afs_proc_cells_write,
sizeof(struct seq_net_private),
NULL) ||
!proc_create_net_single_write("rootcell", 0644, p,
afs_proc_rootcell_show,
afs_proc_rootcell_write,
NULL) ||
!proc_create_net("servers", 0444, p, &afs_proc_servers_ops,
sizeof(struct seq_net_private)) ||
!proc_create_net_single("stats", 0444, p, afs_proc_stats_show, NULL) ||
!proc_create_net_data_write("sysname", 0644, p,
&afs_proc_sysname_ops,
afs_proc_sysname_write,
sizeof(struct seq_net_private),
NULL))
goto error_tree;
net->proc_afs = p;
_leave(" = 0");
return 0;
error_tree:
proc_remove(p);
error_dir:
_leave(" = -ENOMEM");
return -ENOMEM;
}
/*
* clean up the /proc/fs/afs/ directory
*/
void afs_proc_cleanup(struct afs_net *net)
{
proc_remove(net->proc_afs);
net->proc_afs = NULL;
}
...@@ -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