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
This commit is contained in:
commit
35773c9381
|
@ -5,7 +5,7 @@
|
|||
|
||||
afs-cache-$(CONFIG_AFS_FSCACHE) := cache.o
|
||||
|
||||
kafs-objs := \
|
||||
kafs-y := \
|
||||
$(afs-cache-y) \
|
||||
addr_list.o \
|
||||
callback.o \
|
||||
|
@ -21,7 +21,6 @@ kafs-objs := \
|
|||
main.o \
|
||||
misc.o \
|
||||
mntpt.o \
|
||||
proc.o \
|
||||
rotate.o \
|
||||
rxrpc.o \
|
||||
security.o \
|
||||
|
@ -34,4 +33,5 @@ kafs-objs := \
|
|||
write.o \
|
||||
xattr.o
|
||||
|
||||
kafs-$(CONFIG_PROC_FS) += proc.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)
|
|||
_enter("%s", cell->name);
|
||||
|
||||
ret = dns_query("afsdb", cell->name, cell->name_len,
|
||||
"ipv4", &vllist, _expiry);
|
||||
"", &vllist, _expiry);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
|
|
|
@ -20,6 +20,66 @@
|
|||
#include <linux/sched.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
|
||||
* register it with the server.
|
||||
|
@ -77,20 +137,10 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode,
|
|||
}
|
||||
|
||||
if (!cbi) {
|
||||
new = kzalloc(sizeof(struct afs_cb_interest), GFP_KERNEL);
|
||||
new = afs_create_interest(server, vnode);
|
||||
if (!new)
|
||||
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);
|
||||
if (!entry->cb_interest) {
|
||||
entry->cb_interest = afs_get_cb_interest(new);
|
||||
|
@ -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)
|
||||
{
|
||||
struct afs_vol_interest *vi;
|
||||
|
||||
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);
|
||||
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);
|
||||
kfree(vi);
|
||||
afs_put_server(net, cbi->server);
|
||||
}
|
||||
kfree(cbi);
|
||||
|
@ -182,20 +243,34 @@ void afs_break_callback(struct afs_vnode *vnode)
|
|||
static void afs_break_one_callback(struct afs_server *server,
|
||||
struct afs_fid *fid)
|
||||
{
|
||||
struct afs_vol_interest *vi;
|
||||
struct afs_cb_interest *cbi;
|
||||
struct afs_iget_data data;
|
||||
struct afs_vnode *vnode;
|
||||
struct inode *inode;
|
||||
|
||||
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
|
||||
* because of cell aliasing.
|
||||
*/
|
||||
list_for_each_entry(cbi, &server->cb_interests, cb_link) {
|
||||
if (cbi->vid != fid->vid)
|
||||
continue;
|
||||
|
||||
hlist_for_each_entry(cbi, &vi->cb_interests, cb_vlink) {
|
||||
if (fid->vnode == 0 && fid->unique == 0) {
|
||||
/* The callback break applies to an entire volume. */
|
||||
struct afs_super_info *as = AFS_FS_S(cbi->sb);
|
||||
|
@ -217,6 +292,7 @@ static void afs_break_one_callback(struct afs_server *server,
|
|||
}
|
||||
}
|
||||
|
||||
out:
|
||||
read_unlock(&server->cb_break_lock);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/dns_resolver.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/namei.h>
|
||||
#include <keys/rxrpc-type.h>
|
||||
#include "internal.h"
|
||||
|
||||
|
@ -341,8 +342,8 @@ int afs_cell_init(struct afs_net *net, const char *rootcell)
|
|||
|
||||
/* install the new cell */
|
||||
write_seqlock(&net->cells_lock);
|
||||
old_root = net->ws_cell;
|
||||
net->ws_cell = new_root;
|
||||
old_root = rcu_access_pointer(net->ws_cell);
|
||||
rcu_assign_pointer(net->ws_cell, new_root);
|
||||
write_sequnlock(&net->cells_lock);
|
||||
|
||||
afs_put_cell(net, old_root);
|
||||
|
@ -528,12 +529,14 @@ static int afs_activate_cell(struct afs_net *net, struct afs_cell *cell)
|
|||
NULL, 0,
|
||||
cell, 0, true);
|
||||
#endif
|
||||
ret = afs_proc_cell_setup(net, cell);
|
||||
ret = afs_proc_cell_setup(cell);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
spin_lock(&net->proc_cells_lock);
|
||||
|
||||
mutex_lock(&net->proc_cells_lock);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -544,11 +547,12 @@ static void afs_deactivate_cell(struct afs_net *net, struct afs_cell *cell)
|
|||
{
|
||||
_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);
|
||||
spin_unlock(&net->proc_cells_lock);
|
||||
afs_dynroot_rmdir(net, cell);
|
||||
mutex_unlock(&net->proc_cells_lock);
|
||||
|
||||
#ifdef CONFIG_AFS_FSCACHE
|
||||
fscache_relinquish_cookie(cell->cache, NULL, false);
|
||||
|
@ -755,8 +759,8 @@ void afs_cell_purge(struct afs_net *net)
|
|||
_enter("");
|
||||
|
||||
write_seqlock(&net->cells_lock);
|
||||
ws = net->ws_cell;
|
||||
net->ws_cell = NULL;
|
||||
ws = rcu_access_pointer(net->ws_cell);
|
||||
RCU_INIT_POINTER(net->ws_cell, NULL);
|
||||
write_sequnlock(&net->cells_lock);
|
||||
afs_put_cell(net, ws);
|
||||
|
||||
|
|
|
@ -526,7 +526,7 @@ static void SRXAFSCB_TellMeAboutYourself(struct work_struct *work)
|
|||
nifs = 0;
|
||||
ifs = kcalloc(32, sizeof(*ifs), GFP_KERNEL);
|
||||
if (ifs) {
|
||||
nifs = afs_get_ipv4_interfaces(ifs, 32, false);
|
||||
nifs = afs_get_ipv4_interfaces(call->net, ifs, 32, false);
|
||||
if (nifs < 0) {
|
||||
kfree(ifs);
|
||||
ifs = NULL;
|
||||
|
|
126
fs/afs/dynroot.c
126
fs/afs/dynroot.c
|
@ -1,4 +1,4 @@
|
|||
/* dir.c: AFS dynamic root handling
|
||||
/* AFS dynamic root handling
|
||||
*
|
||||
* Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
|
@ -46,7 +46,7 @@ static int afs_probe_cell_name(struct dentry *dentry)
|
|||
return 0;
|
||||
}
|
||||
|
||||
ret = dns_query("afsdb", name, len, "ipv4", NULL, NULL);
|
||||
ret = dns_query("afsdb", name, len, "", NULL, NULL);
|
||||
if (ret == -ENODATA)
|
||||
ret = -EDESTADDRREQ;
|
||||
return ret;
|
||||
|
@ -207,3 +207,125 @@ const struct dentry_operations afs_dynroot_dentry_operations = {
|
|||
.d_release = afs_d_release,
|
||||
.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,
|
|||
u64 data_version, size;
|
||||
u32 type, abort_code;
|
||||
u8 flags = 0;
|
||||
int ret;
|
||||
|
||||
if (vnode)
|
||||
write_seqlock(&vnode->cb_lock);
|
||||
|
||||
abort_code = ntohl(xdr->abort_code);
|
||||
|
||||
|
@ -154,8 +150,7 @@ static int xdr_decode_AFSFetchStatus(struct afs_call *call,
|
|||
* case.
|
||||
*/
|
||||
status->abort_code = abort_code;
|
||||
ret = 0;
|
||||
goto out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_warn("Unknown AFSFetchStatus version %u\n", ntohl(xdr->if_version));
|
||||
|
@ -164,8 +159,7 @@ static int xdr_decode_AFSFetchStatus(struct afs_call *call,
|
|||
|
||||
if (abort_code != 0 && inline_error) {
|
||||
status->abort_code = abort_code;
|
||||
ret = 0;
|
||||
goto out;
|
||||
return 0;
|
||||
}
|
||||
|
||||
type = ntohl(xdr->type);
|
||||
|
@ -235,17 +229,35 @@ static int xdr_decode_AFSFetchStatus(struct afs_call *call,
|
|||
flags);
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
if (vnode)
|
||||
write_sequnlock(&vnode->cb_lock);
|
||||
return ret;
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
xdr_dump_bad(*_bp);
|
||||
ret = afs_protocol_error(call, -EBADMSG);
|
||||
goto out;
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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,7 +399,7 @@ static int afs_deliver_fs_fetch_status_vnode(struct afs_call *call)
|
|||
|
||||
/* unmarshall the reply once we've received all of it */
|
||||
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)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
xdr_decode_AFSCallBack(call, vnode, &bp);
|
||||
|
@ -568,7 +580,7 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
|
|||
return ret;
|
||||
|
||||
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)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
xdr_decode_AFSCallBack(call, vnode, &bp);
|
||||
|
@ -721,8 +733,8 @@ static int afs_deliver_fs_create_vnode(struct afs_call *call)
|
|||
/* unmarshall the reply once we've received all of it */
|
||||
bp = call->buffer;
|
||||
xdr_decode_AFSFid(&bp, call->reply[1]);
|
||||
if (xdr_decode_AFSFetchStatus(call, &bp, call->reply[2], NULL, NULL, NULL) < 0 ||
|
||||
xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode,
|
||||
if (afs_decode_status(call, &bp, call->reply[2], NULL, NULL, NULL) < 0 ||
|
||||
afs_decode_status(call, &bp, &vnode->status, vnode,
|
||||
&call->expected_version, NULL) < 0)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
xdr_decode_AFSCallBack_raw(&bp, call->reply[3]);
|
||||
|
@ -827,7 +839,7 @@ static int afs_deliver_fs_remove(struct afs_call *call)
|
|||
|
||||
/* unmarshall the reply once we've received all of it */
|
||||
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)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
|
||||
|
@ -917,8 +929,8 @@ static int afs_deliver_fs_link(struct afs_call *call)
|
|||
|
||||
/* unmarshall the reply once we've received all of it */
|
||||
bp = call->buffer;
|
||||
if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode, NULL, NULL) < 0 ||
|
||||
xdr_decode_AFSFetchStatus(call, &bp, &dvnode->status, dvnode,
|
||||
if (afs_decode_status(call, &bp, &vnode->status, vnode, NULL, NULL) < 0 ||
|
||||
afs_decode_status(call, &bp, &dvnode->status, dvnode,
|
||||
&call->expected_version, NULL) < 0)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
|
||||
|
@ -1004,8 +1016,8 @@ static int afs_deliver_fs_symlink(struct afs_call *call)
|
|||
/* unmarshall the reply once we've received all of it */
|
||||
bp = call->buffer;
|
||||
xdr_decode_AFSFid(&bp, call->reply[1]);
|
||||
if (xdr_decode_AFSFetchStatus(call, &bp, call->reply[2], NULL, NULL, NULL) ||
|
||||
xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode,
|
||||
if (afs_decode_status(call, &bp, call->reply[2], NULL, NULL, NULL) ||
|
||||
afs_decode_status(call, &bp, &vnode->status, vnode,
|
||||
&call->expected_version, NULL) < 0)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
|
||||
|
@ -1110,11 +1122,11 @@ static int afs_deliver_fs_rename(struct afs_call *call)
|
|||
|
||||
/* unmarshall the reply once we've received all of it */
|
||||
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)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
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)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
|
||||
|
@ -1219,7 +1231,7 @@ static int afs_deliver_fs_store_data(struct afs_call *call)
|
|||
|
||||
/* unmarshall the reply once we've received all of it */
|
||||
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)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
|
||||
|
@ -1395,7 +1407,7 @@ static int afs_deliver_fs_store_status(struct afs_call *call)
|
|||
|
||||
/* unmarshall the reply once we've received all of it */
|
||||
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)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
|
||||
|
@ -2097,7 +2109,7 @@ static int afs_deliver_fs_fetch_status(struct afs_call *call)
|
|||
|
||||
/* unmarshall the reply once we've received all of it */
|
||||
bp = call->buffer;
|
||||
xdr_decode_AFSFetchStatus(call, &bp, status, vnode,
|
||||
afs_decode_status(call, &bp, status, vnode,
|
||||
&call->expected_version, NULL);
|
||||
callback[call->count].version = ntohl(bp[0]);
|
||||
callback[call->count].expiry = ntohl(bp[1]);
|
||||
|
@ -2209,7 +2221,7 @@ static int afs_deliver_fs_inline_bulk_status(struct afs_call *call)
|
|||
|
||||
bp = call->buffer;
|
||||
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,
|
||||
NULL, NULL) < 0)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include <linux/backing-dev.h>
|
||||
#include <linux/uuid.h>
|
||||
#include <net/net_namespace.h>
|
||||
#include <net/netns/generic.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/af_rxrpc.h>
|
||||
|
||||
#include "afs.h"
|
||||
|
@ -40,7 +42,8 @@ struct afs_mount_params {
|
|||
afs_voltype_t type; /* type of volume requested */
|
||||
int volnamesz; /* size of volume name */
|
||||
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_volume *volume; /* volume record */
|
||||
struct key *key; /* key to use for secure mounting */
|
||||
|
@ -189,7 +192,7 @@ struct afs_read {
|
|||
* - there's one superblock per volume
|
||||
*/
|
||||
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_volume *volume; /* volume record */
|
||||
bool dyn_root; /* True if dynamic root */
|
||||
|
@ -210,7 +213,6 @@ struct afs_sysnames {
|
|||
char *subs[AFS_NR_SYSNAME];
|
||||
refcount_t usage;
|
||||
unsigned short nr;
|
||||
short error;
|
||||
char blank[1];
|
||||
};
|
||||
|
||||
|
@ -218,6 +220,7 @@ struct afs_sysnames {
|
|||
* AFS network namespace record.
|
||||
*/
|
||||
struct afs_net {
|
||||
struct net *net; /* Backpointer to the owning net namespace */
|
||||
struct afs_uuid uuid;
|
||||
bool live; /* F if this namespace is being removed */
|
||||
|
||||
|
@ -231,13 +234,13 @@ struct afs_net {
|
|||
|
||||
/* Cell database */
|
||||
struct rb_root cells;
|
||||
struct afs_cell *ws_cell;
|
||||
struct afs_cell __rcu *ws_cell;
|
||||
struct work_struct cells_manager;
|
||||
struct timer_list cells_timer;
|
||||
atomic_t cells_outstanding;
|
||||
seqlock_t cells_lock;
|
||||
|
||||
spinlock_t proc_cells_lock;
|
||||
struct mutex proc_cells_lock;
|
||||
struct list_head proc_cells;
|
||||
|
||||
/* Known servers. Theoretically each fileserver can only be in one
|
||||
|
@ -261,6 +264,7 @@ struct afs_net {
|
|||
struct mutex lock_manager_mutex;
|
||||
|
||||
/* Misc */
|
||||
struct super_block *dynroot_sb; /* Dynamic root mount superblock */
|
||||
struct proc_dir_entry *proc_afs; /* /proc/net/afs directory */
|
||||
struct afs_sysnames *sysnames;
|
||||
rwlock_t sysnames_lock;
|
||||
|
@ -280,7 +284,6 @@ struct afs_net {
|
|||
};
|
||||
|
||||
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 {
|
||||
AFS_CELL_UNSET,
|
||||
|
@ -404,16 +407,27 @@ struct afs_server {
|
|||
rwlock_t fs_lock; /* access lock */
|
||||
|
||||
/* 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. */
|
||||
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.
|
||||
*/
|
||||
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 super_block *sb; /* Superblock on which inodes reside */
|
||||
afs_volid_t vid; /* Volume ID to match */
|
||||
|
@ -720,6 +734,10 @@ extern const struct inode_operations afs_dynroot_inode_operations;
|
|||
extern const struct dentry_operations afs_dynroot_dentry_operations;
|
||||
|
||||
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
|
||||
|
@ -806,34 +824,36 @@ extern int afs_drop_inode(struct inode *);
|
|||
* main.c
|
||||
*/
|
||||
extern struct workqueue_struct *afs_wq;
|
||||
extern int afs_net_id;
|
||||
|
||||
static inline struct afs_net *afs_net(struct net *net)
|
||||
{
|
||||
return net_generic(net, afs_net_id);
|
||||
}
|
||||
|
||||
static inline struct afs_net *afs_sb2net(struct super_block *sb)
|
||||
{
|
||||
return afs_net(AFS_FS_S(sb)->net_ns);
|
||||
}
|
||||
|
||||
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_i2net(struct inode *inode)
|
||||
{
|
||||
return &__afs_net;
|
||||
return afs_sb2net(inode->i_sb);
|
||||
}
|
||||
|
||||
static inline struct afs_net *afs_v2net(struct afs_vnode *vnode)
|
||||
{
|
||||
return &__afs_net;
|
||||
return afs_i2net(&vnode->vfs_inode);
|
||||
}
|
||||
|
||||
static inline struct afs_net *afs_sock2net(struct sock *sk)
|
||||
{
|
||||
return &__afs_net;
|
||||
}
|
||||
|
||||
static inline struct afs_net *afs_get_net(struct afs_net *net)
|
||||
{
|
||||
return net;
|
||||
}
|
||||
|
||||
static inline void afs_put_net(struct afs_net *net)
|
||||
{
|
||||
return net_generic(sock_net(sk), afs_net_id);
|
||||
}
|
||||
|
||||
static inline void __afs_stat(atomic_t *s)
|
||||
|
@ -861,16 +881,25 @@ extern void afs_mntpt_kill_timer(void);
|
|||
/*
|
||||
* 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
|
||||
*/
|
||||
#ifdef CONFIG_PROC_FS
|
||||
extern int __net_init afs_proc_init(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 void afs_proc_cell_remove(struct afs_net *, struct afs_cell *);
|
||||
extern int afs_proc_cell_setup(struct afs_cell *);
|
||||
extern void afs_proc_cell_remove(struct afs_cell *);
|
||||
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
|
||||
|
@ -1002,7 +1031,7 @@ extern bool afs_annotate_server_list(struct afs_server_list *, struct afs_server
|
|||
* super.c
|
||||
*/
|
||||
extern int __init afs_fs_init(void);
|
||||
extern void __exit afs_fs_exit(void);
|
||||
extern void afs_fs_exit(void);
|
||||
|
||||
/*
|
||||
* vlclient.c
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/completion.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "internal.h"
|
||||
|
||||
|
@ -32,7 +33,7 @@ module_param(rootcell, charp, 0);
|
|||
MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list");
|
||||
|
||||
struct workqueue_struct *afs_wq;
|
||||
struct afs_net __afs_net;
|
||||
static struct proc_dir_entry *afs_proc_symlink;
|
||||
|
||||
#if defined(CONFIG_ALPHA)
|
||||
const char afs_init_sysname[] = "alpha_linux26";
|
||||
|
@ -67,11 +68,13 @@ const char afs_init_sysname[] = "unknown_linux26";
|
|||
/*
|
||||
* 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_net *net = afs_net(net_ns);
|
||||
int ret;
|
||||
|
||||
net->net = net_ns;
|
||||
net->live = true;
|
||||
generate_random_uuid((unsigned char *)&net->uuid);
|
||||
|
||||
|
@ -83,7 +86,7 @@ static int __net_init afs_net_init(struct afs_net *net)
|
|||
INIT_WORK(&net->cells_manager, afs_manage_cells);
|
||||
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);
|
||||
|
||||
seqlock_init(&net->fs_lock);
|
||||
|
@ -142,8 +145,10 @@ static int __net_init afs_net_init(struct afs_net *net)
|
|||
/*
|
||||
* 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;
|
||||
afs_cell_purge(net);
|
||||
afs_purge_servers(net);
|
||||
|
@ -152,6 +157,13 @@ static void __net_exit afs_net_exit(struct afs_net *net)
|
|||
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
|
||||
*/
|
||||
|
@ -178,7 +190,7 @@ static int __init afs_init(void)
|
|||
goto error_cache;
|
||||
#endif
|
||||
|
||||
ret = afs_net_init(&__afs_net);
|
||||
ret = register_pernet_subsys(&afs_net_ops);
|
||||
if (ret < 0)
|
||||
goto error_net;
|
||||
|
||||
|
@ -187,10 +199,18 @@ static int __init afs_init(void)
|
|||
if (ret < 0)
|
||||
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;
|
||||
|
||||
error_proc:
|
||||
afs_fs_exit();
|
||||
error_fs:
|
||||
afs_net_exit(&__afs_net);
|
||||
unregister_pernet_subsys(&afs_net_ops);
|
||||
error_net:
|
||||
#ifdef CONFIG_AFS_FSCACHE
|
||||
fscache_unregister_netfs(&afs_cache_netfs);
|
||||
|
@ -219,8 +239,9 @@ static void __exit afs_exit(void)
|
|||
{
|
||||
printk(KERN_INFO "kAFS: Red Hat AFS client v0.1 unregistering.\n");
|
||||
|
||||
proc_remove(afs_proc_symlink);
|
||||
afs_fs_exit();
|
||||
afs_net_exit(&__afs_net);
|
||||
unregister_pernet_subsys(&afs_net_ops);
|
||||
#ifdef CONFIG_AFS_FSCACHE
|
||||
fscache_unregister_netfs(&afs_cache_netfs);
|
||||
#endif
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
* - maxbufs must be at least 1
|
||||
* - returns the number of interface records in the buffer
|
||||
*/
|
||||
int afs_get_ipv4_interfaces(struct afs_interface *bufs, size_t maxbufs,
|
||||
bool wantloopback)
|
||||
int afs_get_ipv4_interfaces(struct afs_net *net, struct afs_interface *bufs,
|
||||
size_t maxbufs, bool wantloopback)
|
||||
{
|
||||
struct net_device *dev;
|
||||
struct in_device *idev;
|
||||
|
@ -27,7 +27,7 @@ int afs_get_ipv4_interfaces(struct afs_interface *bufs, size_t maxbufs,
|
|||
ASSERT(maxbufs > 0);
|
||||
|
||||
rtnl_lock();
|
||||
for_each_netdev(&init_net, dev) {
|
||||
for_each_netdev(net->net, dev) {
|
||||
if (dev->type == ARPHRD_LOOPBACK && !wantloopback)
|
||||
continue;
|
||||
idev = __in_dev_get_rtnl(dev);
|
||||
|
|
851
fs/afs/proc.c
851
fs/afs/proc.c
File diff suppressed because it is too large
Load Diff
|
@ -46,7 +46,7 @@ int afs_open_socket(struct afs_net *net)
|
|||
|
||||
_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)
|
||||
goto error_1;
|
||||
|
||||
|
|
|
@ -228,7 +228,7 @@ static struct afs_server *afs_alloc_server(struct afs_net *net,
|
|||
server->flags = (1UL << AFS_SERVER_FL_NEW);
|
||||
server->update_at = ktime_get_real_seconds() + afs_server_update_delay;
|
||||
rwlock_init(&server->fs_lock);
|
||||
INIT_LIST_HEAD(&server->cb_interests);
|
||||
INIT_HLIST_HEAD(&server->cb_volumes);
|
||||
rwlock_init(&server->cb_break_lock);
|
||||
|
||||
afs_inc_servers_outstanding(net);
|
||||
|
|
|
@ -48,6 +48,8 @@ struct file_system_type afs_fs_type = {
|
|||
};
|
||||
MODULE_ALIAS_FS("afs");
|
||||
|
||||
int afs_net_id;
|
||||
|
||||
static const struct super_operations afs_super_ops = {
|
||||
.statfs = afs_statfs,
|
||||
.alloc_inode = afs_alloc_inode,
|
||||
|
@ -117,7 +119,7 @@ int __init afs_fs_init(void)
|
|||
/*
|
||||
* clean up the filesystem
|
||||
*/
|
||||
void __exit afs_fs_exit(void)
|
||||
void afs_fs_exit(void)
|
||||
{
|
||||
_enter("");
|
||||
|
||||
|
@ -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 *as = AFS_FS_S(sb);
|
||||
|
||||
return (as->net == as1->net &&
|
||||
return (as->net_ns == as1->net_ns &&
|
||||
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)
|
||||
{
|
||||
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)
|
||||
|
@ -418,10 +425,14 @@ static int afs_fill_super(struct super_block *sb,
|
|||
if (!sb->s_root)
|
||||
goto error;
|
||||
|
||||
if (params->dyn_root)
|
||||
if (as->dyn_root) {
|
||||
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;
|
||||
}
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
|
@ -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);
|
||||
if (as) {
|
||||
as->net = afs_get_net(params->net);
|
||||
as->net_ns = get_net(params->net_ns);
|
||||
if (params->dyn_root)
|
||||
as->dyn_root = true;
|
||||
else
|
||||
|
@ -450,12 +461,31 @@ static void afs_destroy_sbi(struct afs_super_info *as)
|
|||
{
|
||||
if (as) {
|
||||
afs_put_volume(as->cell, as->volume);
|
||||
afs_put_cell(as->net, as->cell);
|
||||
afs_put_net(as->net);
|
||||
afs_put_cell(afs_net(as->net_ns), as->cell);
|
||||
put_net(as->net_ns);
|
||||
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
|
||||
*/
|
||||
|
@ -472,11 +502,12 @@ static struct dentry *afs_mount(struct file_system_type *fs_type,
|
|||
_enter(",,%s,%p", dev_name, options);
|
||||
|
||||
memset(¶ms, 0, sizeof(params));
|
||||
params.net = &__afs_net;
|
||||
|
||||
ret = -EINVAL;
|
||||
if (current->nsproxy->net_ns != &init_net)
|
||||
goto error;
|
||||
params.net_ns = current->nsproxy->net_ns;
|
||||
params.net = afs_net(params.net_ns);
|
||||
|
||||
/* parse the options and device name */
|
||||
if (options) {
|
||||
|
@ -563,21 +594,6 @@ static struct dentry *afs_mount(struct file_system_type *fs_type,
|
|||
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
|
||||
* afs_alloc_inode() *must* reset anything that could incorrectly leak from one
|
||||
|
|
29
fs/namei.c
29
fs/namei.c
|
@ -2463,6 +2463,35 @@ static int lookup_one_len_common(const char *name, struct dentry *base,
|
|||
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
|
||||
* @name: pathname component to lookup
|
||||
|
|
|
@ -409,7 +409,7 @@ static struct proc_dir_entry *__proc_create(struct proc_dir_entry **parent,
|
|||
if (!ent)
|
||||
goto out;
|
||||
|
||||
if (qstr.len + 1 <= sizeof(ent->inline_name)) {
|
||||
if (qstr.len + 1 <= SIZEOF_PDE_INLINE_NAME) {
|
||||
ent->name = ent->inline_name;
|
||||
} else {
|
||||
ent->name = kmalloc(qstr.len + 1, GFP_KERNEL);
|
||||
|
@ -740,3 +740,27 @@ void *PDE_DATA(const struct inode *inode)
|
|||
return __PDE_DATA(inode);
|
||||
}
|
||||
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)
|
|||
kmem_cache_create("pde_opener", sizeof(struct pde_opener), 0,
|
||||
SLAB_ACCOUNT|SLAB_PANIC, NULL);
|
||||
proc_dir_entry_cache = kmem_cache_create_usercopy(
|
||||
"proc_dir_entry", sizeof(struct proc_dir_entry), 0, SLAB_PANIC,
|
||||
offsetof(struct proc_dir_entry, inline_name),
|
||||
sizeof_field(struct proc_dir_entry, inline_name), NULL);
|
||||
"proc_dir_entry", SIZEOF_PDE_SLOT, 0, SLAB_PANIC,
|
||||
OFFSETOF_PDE_NAME, SIZEOF_PDE_INLINE_NAME, NULL);
|
||||
}
|
||||
|
||||
static int proc_show_options(struct seq_file *seq, struct dentry *root)
|
||||
|
|
|
@ -48,6 +48,7 @@ struct proc_dir_entry {
|
|||
const struct seq_operations *seq_ops;
|
||||
int (*single_show)(struct seq_file *, void *);
|
||||
};
|
||||
proc_write_t write;
|
||||
void *data;
|
||||
unsigned int state_size;
|
||||
unsigned int low_ino;
|
||||
|
@ -61,14 +62,20 @@ struct proc_dir_entry {
|
|||
char *name;
|
||||
umode_t mode;
|
||||
u8 namelen;
|
||||
#ifdef CONFIG_64BIT
|
||||
#define SIZEOF_PDE_INLINE_NAME (192-155)
|
||||
#else
|
||||
#define SIZEOF_PDE_INLINE_NAME (128-95)
|
||||
#endif
|
||||
char inline_name[SIZEOF_PDE_INLINE_NAME];
|
||||
char inline_name[];
|
||||
} __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;
|
||||
void pde_free(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;
|
||||
}
|
||||
extern ssize_t proc_simple_write(struct file *, const char __user *, size_t, loff_t *);
|
||||
|
||||
/*
|
||||
* inode.c
|
||||
|
|
|
@ -46,6 +46,9 @@ static int seq_open_net(struct inode *inode, struct file *file)
|
|||
|
||||
WARN_ON_ONCE(state_size < sizeof(*p));
|
||||
|
||||
if (file->f_mode & FMODE_WRITE && !PDE(inode)->write)
|
||||
return -EACCES;
|
||||
|
||||
net = get_proc_net(inode);
|
||||
if (!net)
|
||||
return -ENXIO;
|
||||
|
@ -73,6 +76,7 @@ static int seq_release_net(struct inode *ino, struct file *f)
|
|||
static const struct file_operations proc_net_seq_fops = {
|
||||
.open = seq_open_net,
|
||||
.read = seq_read,
|
||||
.write = proc_simple_write,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release_net,
|
||||
};
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
struct proc_dir_entry *de = PDE(inode);
|
||||
|
@ -119,6 +167,7 @@ static int single_release_net(struct inode *ino, struct file *f)
|
|||
static const struct file_operations proc_net_single_fops = {
|
||||
.open = single_open_net,
|
||||
.read = seq_read,
|
||||
.write = proc_simple_write,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release_net,
|
||||
};
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
struct task_struct *task;
|
||||
|
|
|
@ -204,8 +204,7 @@ struct proc_dir_entry proc_root = {
|
|||
.proc_fops = &proc_root_operations,
|
||||
.parent = &proc_root,
|
||||
.subdir = RB_ROOT,
|
||||
.name = proc_root.inline_name,
|
||||
.inline_name = "/proc",
|
||||
.name = "/proc",
|
||||
};
|
||||
|
||||
int pid_ns_prepare_proc(struct pid_namespace *ns)
|
||||
|
|
|
@ -81,6 +81,7 @@ extern void done_path_create(struct path *, struct dentry *);
|
|||
extern struct dentry *kern_path_locked(const char *, struct path *);
|
||||
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_unlocked(const char *, struct dentry *, int);
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ struct seq_operations;
|
|||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
typedef int (*proc_write_t)(struct file *, char *, size_t);
|
||||
|
||||
extern void proc_root_init(void);
|
||||
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,
|
|||
struct proc_dir_entry *proc_create_net_single(const char *name, umode_t mode,
|
||||
struct proc_dir_entry *parent,
|
||||
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 */
|
||||
|
||||
|
|
Loading…
Reference in New Issue