mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
Merge branch 'afs-dh' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull AFS updates from Al Viro: "The AFS series posted by dhowells depended upon lookup_one_len() rework; now that prereq is in the mainline, that series had been rebased on top of it and got some exposure and testing..." * 'afs-dh' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: afs: Do better accretion of small writes on newly created content afs: Add stats for data transfer operations afs: Trace protocol errors afs: Locally edit directory data for mkdir/create/unlink/... afs: Adjust the directory XDR structures afs: Split the directory content defs into a header afs: Fix directory handling afs: Split the dynroot stuff out and give it its own ops tables afs: Keep track of invalid-before version for dentry coherency afs: Rearrange status mapping afs: Make it possible to get the data version in readpage afs: Init inode before accessing cache afs: Introduce a statistics proc file afs: Dump bad status record afs: Implement @cell substitution handling afs: Implement @sys substitution handling afs: Prospectively look up extra files when doing a single lookup afs: Don't over-increment the cell usage count when pinning it afs: Fix checker warnings vfs: Remove the const from dir_context::actor
This commit is contained in:
commit
19e8a2f875
@ -11,7 +11,7 @@ Contents:
|
||||
- Proc filesystem.
|
||||
- The cell database.
|
||||
- Security.
|
||||
- Examples.
|
||||
- The @sys substitution.
|
||||
|
||||
|
||||
========
|
||||
@ -230,3 +230,29 @@ If a file is opened with a particular key and then the file descriptor is
|
||||
passed to a process that doesn't have that key (perhaps over an AF_UNIX
|
||||
socket), then the operations on the file will be made with key that was used to
|
||||
open the file.
|
||||
|
||||
|
||||
=====================
|
||||
THE @SYS SUBSTITUTION
|
||||
=====================
|
||||
|
||||
The list of up to 16 @sys substitutions for the current network namespace can
|
||||
be configured by writing a list to /proc/fs/afs/sysname:
|
||||
|
||||
[root@andromeda ~]# echo foo amd64_linux_26 >/proc/fs/afs/sysname
|
||||
|
||||
or cleared entirely by writing an empty list:
|
||||
|
||||
[root@andromeda ~]# echo >/proc/fs/afs/sysname
|
||||
|
||||
The current list for current network namespace can be retrieved by:
|
||||
|
||||
[root@andromeda ~]# cat /proc/fs/afs/sysname
|
||||
foo
|
||||
amd64_linux_26
|
||||
|
||||
When @sys is being substituted for, each element of the list is tried in the
|
||||
order given.
|
||||
|
||||
By default, the list will contain one item that conforms to the pattern
|
||||
"<arch>_linux_26", amd64 being the name for x86_64.
|
||||
|
@ -12,6 +12,8 @@ kafs-objs := \
|
||||
cell.o \
|
||||
cmservice.o \
|
||||
dir.o \
|
||||
dir_edit.o \
|
||||
dynroot.o \
|
||||
file.o \
|
||||
flock.o \
|
||||
fsclient.o \
|
||||
|
@ -243,9 +243,9 @@ void afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port)
|
||||
xport == a->sin6_port)
|
||||
return;
|
||||
if (xdr == a->sin6_addr.s6_addr32[3] &&
|
||||
xport < a->sin6_port)
|
||||
(u16 __force)xport < (u16 __force)a->sin6_port)
|
||||
break;
|
||||
if (xdr < a->sin6_addr.s6_addr32[3])
|
||||
if ((u32 __force)xdr < (u32 __force)a->sin6_addr.s6_addr32[3])
|
||||
break;
|
||||
}
|
||||
|
||||
@ -280,7 +280,7 @@ void afs_merge_fs_addr6(struct afs_addr_list *alist, __be32 *xdr, u16 port)
|
||||
xport == a->sin6_port)
|
||||
return;
|
||||
if (diff == 0 &&
|
||||
xport < a->sin6_port)
|
||||
(u16 __force)xport < (u16 __force)a->sin6_port)
|
||||
break;
|
||||
if (diff < 0)
|
||||
break;
|
||||
|
27
fs/afs/afs.h
27
fs/afs/afs.h
@ -67,10 +67,14 @@ typedef enum {
|
||||
} afs_callback_type_t;
|
||||
|
||||
struct afs_callback {
|
||||
struct afs_fid fid; /* file identifier */
|
||||
unsigned version; /* callback version */
|
||||
unsigned expiry; /* time at which expires */
|
||||
afs_callback_type_t type; /* type of callback */
|
||||
unsigned version; /* Callback version */
|
||||
unsigned expiry; /* Time at which expires */
|
||||
afs_callback_type_t type; /* Type of callback */
|
||||
};
|
||||
|
||||
struct afs_callback_break {
|
||||
struct afs_fid fid; /* File identifier */
|
||||
struct afs_callback cb; /* Callback details */
|
||||
};
|
||||
|
||||
#define AFSCBMAX 50 /* maximum callbacks transferred per bulk op */
|
||||
@ -123,21 +127,20 @@ typedef u32 afs_access_t;
|
||||
* AFS file status information
|
||||
*/
|
||||
struct afs_file_status {
|
||||
unsigned if_version; /* interface version */
|
||||
#define AFS_FSTATUS_VERSION 1
|
||||
u64 size; /* file size */
|
||||
afs_dataversion_t data_version; /* current data version */
|
||||
time_t mtime_client; /* last time client changed data */
|
||||
time_t mtime_server; /* last time server changed data */
|
||||
unsigned abort_code; /* Abort if bulk-fetching this failed */
|
||||
|
||||
afs_file_type_t type; /* file type */
|
||||
unsigned nlink; /* link count */
|
||||
u64 size; /* file size */
|
||||
afs_dataversion_t data_version; /* current data version */
|
||||
u32 author; /* author ID */
|
||||
kuid_t owner; /* owner ID */
|
||||
kgid_t group; /* group ID */
|
||||
u32 owner; /* owner ID */
|
||||
u32 group; /* group ID */
|
||||
afs_access_t caller_access; /* access rights for authenticated caller */
|
||||
afs_access_t anon_access; /* access rights for unauthenticated caller */
|
||||
umode_t mode; /* UNIX mode */
|
||||
time_t mtime_client; /* last time client changed data */
|
||||
time_t mtime_server; /* last time server changed data */
|
||||
s32 lock_count; /* file lock count (0=UNLK -1=WRLCK +ve=#RDLCK */
|
||||
};
|
||||
|
||||
|
@ -31,10 +31,12 @@ enum AFS_FS_Operations {
|
||||
FSGETVOLUMEINFO = 148, /* AFS Get information about a volume */
|
||||
FSGETVOLUMESTATUS = 149, /* AFS Get volume status information */
|
||||
FSGETROOTVOLUME = 151, /* AFS Get root volume name */
|
||||
FSBULKSTATUS = 155, /* AFS Fetch multiple file statuses */
|
||||
FSSETLOCK = 156, /* AFS Request a file lock */
|
||||
FSEXTENDLOCK = 157, /* AFS Extend a file lock */
|
||||
FSRELEASELOCK = 158, /* AFS Release a file lock */
|
||||
FSLOOKUP = 161, /* AFS lookup file in directory */
|
||||
FSINLINEBULKSTATUS = 65536, /* AFS Fetch multiple file statuses with inline errors */
|
||||
FSFETCHDATA64 = 65537, /* AFS Fetch file data */
|
||||
FSSTOREDATA64 = 65538, /* AFS Store file data */
|
||||
FSGIVEUPALLCALLBACKS = 65539, /* AFS Give up all outstanding callbacks on a server */
|
||||
|
@ -96,26 +96,6 @@ again:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a vnode's interest on a server.
|
||||
*/
|
||||
void afs_set_cb_interest(struct afs_vnode *vnode, struct afs_cb_interest *cbi)
|
||||
{
|
||||
struct afs_cb_interest *old_cbi = NULL;
|
||||
|
||||
if (vnode->cb_interest == cbi)
|
||||
return;
|
||||
|
||||
write_seqlock(&vnode->cb_lock);
|
||||
if (vnode->cb_interest != cbi) {
|
||||
afs_get_cb_interest(cbi);
|
||||
old_cbi = vnode->cb_interest;
|
||||
vnode->cb_interest = cbi;
|
||||
}
|
||||
write_sequnlock(&vnode->cb_lock);
|
||||
afs_put_cb_interest(afs_v2net(vnode), cbi);
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove an interest on a server.
|
||||
*/
|
||||
@ -150,6 +130,7 @@ void afs_break_callback(struct afs_vnode *vnode)
|
||||
|
||||
write_seqlock(&vnode->cb_lock);
|
||||
|
||||
clear_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
|
||||
if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
|
||||
vnode->cb_break++;
|
||||
afs_clear_permits(vnode);
|
||||
@ -207,7 +188,7 @@ static void afs_break_one_callback(struct afs_server *server,
|
||||
* allow the fileserver to break callback promises
|
||||
*/
|
||||
void afs_break_callbacks(struct afs_server *server, size_t count,
|
||||
struct afs_callback callbacks[])
|
||||
struct afs_callback_break *callbacks)
|
||||
{
|
||||
_enter("%p,%zu,", server, count);
|
||||
|
||||
@ -219,9 +200,9 @@ void afs_break_callbacks(struct afs_server *server, size_t count,
|
||||
callbacks->fid.vid,
|
||||
callbacks->fid.vnode,
|
||||
callbacks->fid.unique,
|
||||
callbacks->version,
|
||||
callbacks->expiry,
|
||||
callbacks->type
|
||||
callbacks->cb.version,
|
||||
callbacks->cb.expiry,
|
||||
callbacks->cb.type
|
||||
);
|
||||
afs_break_one_callback(server, &callbacks->fid);
|
||||
}
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include <keys/rxrpc-type.h>
|
||||
#include "internal.h"
|
||||
|
||||
unsigned __read_mostly afs_cell_gc_delay = 10;
|
||||
static unsigned __read_mostly afs_cell_gc_delay = 10;
|
||||
|
||||
static void afs_manage_cell(struct work_struct *);
|
||||
|
||||
@ -75,7 +75,7 @@ struct afs_cell *afs_lookup_cell_rcu(struct afs_net *net,
|
||||
cell = rcu_dereference_raw(net->ws_cell);
|
||||
if (cell) {
|
||||
afs_get_cell(cell);
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
ret = -EDESTADDRREQ;
|
||||
continue;
|
||||
@ -130,6 +130,8 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net,
|
||||
_leave(" = -ENAMETOOLONG");
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
}
|
||||
if (namelen == 5 && memcmp(name, "@cell", 5) == 0)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
_enter("%*.*s,%s", namelen, namelen, name, vllist);
|
||||
|
||||
@ -334,8 +336,8 @@ int afs_cell_init(struct afs_net *net, const char *rootcell)
|
||||
return PTR_ERR(new_root);
|
||||
}
|
||||
|
||||
set_bit(AFS_CELL_FL_NO_GC, &new_root->flags);
|
||||
afs_get_cell(new_root);
|
||||
if (!test_and_set_bit(AFS_CELL_FL_NO_GC, &new_root->flags))
|
||||
afs_get_cell(new_root);
|
||||
|
||||
/* install the new cell */
|
||||
write_seqlock(&net->cells_lock);
|
||||
@ -411,7 +413,7 @@ static void afs_cell_destroy(struct rcu_head *rcu)
|
||||
|
||||
ASSERTCMP(atomic_read(&cell->usage), ==, 0);
|
||||
|
||||
afs_put_addrlist(cell->vl_addrs);
|
||||
afs_put_addrlist(rcu_access_pointer(cell->vl_addrs));
|
||||
key_put(cell->anonymous_key);
|
||||
kfree(cell);
|
||||
|
||||
|
@ -178,8 +178,8 @@ static void SRXAFSCB_CallBack(struct work_struct *work)
|
||||
*/
|
||||
static int afs_deliver_cb_callback(struct afs_call *call)
|
||||
{
|
||||
struct afs_callback_break *cb;
|
||||
struct sockaddr_rxrpc srx;
|
||||
struct afs_callback *cb;
|
||||
struct afs_server *server;
|
||||
__be32 *bp;
|
||||
int ret, loop;
|
||||
@ -201,7 +201,7 @@ static int afs_deliver_cb_callback(struct afs_call *call)
|
||||
call->count = ntohl(call->tmp);
|
||||
_debug("FID count: %u", call->count);
|
||||
if (call->count > AFSCBMAX)
|
||||
return -EBADMSG;
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
|
||||
call->buffer = kmalloc(call->count * 3 * 4, GFP_KERNEL);
|
||||
if (!call->buffer)
|
||||
@ -218,7 +218,7 @@ static int afs_deliver_cb_callback(struct afs_call *call)
|
||||
|
||||
_debug("unmarshall FID array");
|
||||
call->request = kcalloc(call->count,
|
||||
sizeof(struct afs_callback),
|
||||
sizeof(struct afs_callback_break),
|
||||
GFP_KERNEL);
|
||||
if (!call->request)
|
||||
return -ENOMEM;
|
||||
@ -229,7 +229,7 @@ static int afs_deliver_cb_callback(struct afs_call *call)
|
||||
cb->fid.vid = ntohl(*bp++);
|
||||
cb->fid.vnode = ntohl(*bp++);
|
||||
cb->fid.unique = ntohl(*bp++);
|
||||
cb->type = AFSCM_CB_UNTYPED;
|
||||
cb->cb.type = AFSCM_CB_UNTYPED;
|
||||
}
|
||||
|
||||
call->offset = 0;
|
||||
@ -245,7 +245,7 @@ static int afs_deliver_cb_callback(struct afs_call *call)
|
||||
call->count2 = ntohl(call->tmp);
|
||||
_debug("CB count: %u", call->count2);
|
||||
if (call->count2 != call->count && call->count2 != 0)
|
||||
return -EBADMSG;
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
call->offset = 0;
|
||||
call->unmarshall++;
|
||||
|
||||
@ -260,9 +260,9 @@ static int afs_deliver_cb_callback(struct afs_call *call)
|
||||
cb = call->request;
|
||||
bp = call->buffer;
|
||||
for (loop = call->count2; loop > 0; loop--, cb++) {
|
||||
cb->version = ntohl(*bp++);
|
||||
cb->expiry = ntohl(*bp++);
|
||||
cb->type = ntohl(*bp++);
|
||||
cb->cb.version = ntohl(*bp++);
|
||||
cb->cb.expiry = ntohl(*bp++);
|
||||
cb->cb.type = ntohl(*bp++);
|
||||
}
|
||||
|
||||
call->offset = 0;
|
||||
@ -500,9 +500,9 @@ static int afs_deliver_cb_probe_uuid(struct afs_call *call)
|
||||
|
||||
b = call->buffer;
|
||||
r = call->request;
|
||||
r->time_low = ntohl(b[0]);
|
||||
r->time_mid = ntohl(b[1]);
|
||||
r->time_hi_and_version = ntohl(b[2]);
|
||||
r->time_low = b[0];
|
||||
r->time_mid = htons(ntohl(b[1]));
|
||||
r->time_hi_and_version = htons(ntohl(b[2]));
|
||||
r->clock_seq_hi_and_reserved = ntohl(b[3]);
|
||||
r->clock_seq_low = ntohl(b[4]);
|
||||
|
||||
|
933
fs/afs/dir.c
933
fs/afs/dir.c
File diff suppressed because it is too large
Load Diff
505
fs/afs/dir_edit.c
Normal file
505
fs/afs/dir_edit.c
Normal file
@ -0,0 +1,505 @@
|
||||
/* AFS filesystem directory editing
|
||||
*
|
||||
* Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/iversion.h>
|
||||
#include "internal.h"
|
||||
#include "xdr_fs.h"
|
||||
|
||||
/*
|
||||
* Find a number of contiguous clear bits in a directory block bitmask.
|
||||
*
|
||||
* There are 64 slots, which means we can load the entire bitmap into a
|
||||
* variable. The first bit doesn't count as it corresponds to the block header
|
||||
* slot. nr_slots is between 1 and 9.
|
||||
*/
|
||||
static int afs_find_contig_bits(union afs_xdr_dir_block *block, unsigned int nr_slots)
|
||||
{
|
||||
u64 bitmap;
|
||||
u32 mask;
|
||||
int bit, n;
|
||||
|
||||
bitmap = (u64)block->hdr.bitmap[0] << 0 * 8;
|
||||
bitmap |= (u64)block->hdr.bitmap[1] << 1 * 8;
|
||||
bitmap |= (u64)block->hdr.bitmap[2] << 2 * 8;
|
||||
bitmap |= (u64)block->hdr.bitmap[3] << 3 * 8;
|
||||
bitmap |= (u64)block->hdr.bitmap[4] << 4 * 8;
|
||||
bitmap |= (u64)block->hdr.bitmap[5] << 5 * 8;
|
||||
bitmap |= (u64)block->hdr.bitmap[6] << 6 * 8;
|
||||
bitmap |= (u64)block->hdr.bitmap[7] << 7 * 8;
|
||||
bitmap >>= 1; /* The first entry is metadata */
|
||||
bit = 1;
|
||||
mask = (1 << nr_slots) - 1;
|
||||
|
||||
do {
|
||||
if (sizeof(unsigned long) == 8)
|
||||
n = ffz(bitmap);
|
||||
else
|
||||
n = ((u32)bitmap) != 0 ?
|
||||
ffz((u32)bitmap) :
|
||||
ffz((u32)(bitmap >> 32)) + 32;
|
||||
bitmap >>= n;
|
||||
bit += n;
|
||||
|
||||
if ((bitmap & mask) == 0) {
|
||||
if (bit > 64 - nr_slots)
|
||||
return -1;
|
||||
return bit;
|
||||
}
|
||||
|
||||
n = __ffs(bitmap);
|
||||
bitmap >>= n;
|
||||
bit += n;
|
||||
} while (bitmap);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set a number of contiguous bits in the directory block bitmap.
|
||||
*/
|
||||
static void afs_set_contig_bits(union afs_xdr_dir_block *block,
|
||||
int bit, unsigned int nr_slots)
|
||||
{
|
||||
u64 mask, before, after;
|
||||
|
||||
mask = (1 << nr_slots) - 1;
|
||||
mask <<= bit;
|
||||
|
||||
before = *(u64 *)block->hdr.bitmap;
|
||||
|
||||
block->hdr.bitmap[0] |= (u8)(mask >> 0 * 8);
|
||||
block->hdr.bitmap[1] |= (u8)(mask >> 1 * 8);
|
||||
block->hdr.bitmap[2] |= (u8)(mask >> 2 * 8);
|
||||
block->hdr.bitmap[3] |= (u8)(mask >> 3 * 8);
|
||||
block->hdr.bitmap[4] |= (u8)(mask >> 4 * 8);
|
||||
block->hdr.bitmap[5] |= (u8)(mask >> 5 * 8);
|
||||
block->hdr.bitmap[6] |= (u8)(mask >> 6 * 8);
|
||||
block->hdr.bitmap[7] |= (u8)(mask >> 7 * 8);
|
||||
|
||||
after = *(u64 *)block->hdr.bitmap;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clear a number of contiguous bits in the directory block bitmap.
|
||||
*/
|
||||
static void afs_clear_contig_bits(union afs_xdr_dir_block *block,
|
||||
int bit, unsigned int nr_slots)
|
||||
{
|
||||
u64 mask, before, after;
|
||||
|
||||
mask = (1 << nr_slots) - 1;
|
||||
mask <<= bit;
|
||||
|
||||
before = *(u64 *)block->hdr.bitmap;
|
||||
|
||||
block->hdr.bitmap[0] &= ~(u8)(mask >> 0 * 8);
|
||||
block->hdr.bitmap[1] &= ~(u8)(mask >> 1 * 8);
|
||||
block->hdr.bitmap[2] &= ~(u8)(mask >> 2 * 8);
|
||||
block->hdr.bitmap[3] &= ~(u8)(mask >> 3 * 8);
|
||||
block->hdr.bitmap[4] &= ~(u8)(mask >> 4 * 8);
|
||||
block->hdr.bitmap[5] &= ~(u8)(mask >> 5 * 8);
|
||||
block->hdr.bitmap[6] &= ~(u8)(mask >> 6 * 8);
|
||||
block->hdr.bitmap[7] &= ~(u8)(mask >> 7 * 8);
|
||||
|
||||
after = *(u64 *)block->hdr.bitmap;
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan a directory block looking for a dirent of the right name.
|
||||
*/
|
||||
static int afs_dir_scan_block(union afs_xdr_dir_block *block, struct qstr *name,
|
||||
unsigned int blocknum)
|
||||
{
|
||||
union afs_xdr_dirent *de;
|
||||
u64 bitmap;
|
||||
int d, len, n;
|
||||
|
||||
_enter("");
|
||||
|
||||
bitmap = (u64)block->hdr.bitmap[0] << 0 * 8;
|
||||
bitmap |= (u64)block->hdr.bitmap[1] << 1 * 8;
|
||||
bitmap |= (u64)block->hdr.bitmap[2] << 2 * 8;
|
||||
bitmap |= (u64)block->hdr.bitmap[3] << 3 * 8;
|
||||
bitmap |= (u64)block->hdr.bitmap[4] << 4 * 8;
|
||||
bitmap |= (u64)block->hdr.bitmap[5] << 5 * 8;
|
||||
bitmap |= (u64)block->hdr.bitmap[6] << 6 * 8;
|
||||
bitmap |= (u64)block->hdr.bitmap[7] << 7 * 8;
|
||||
|
||||
for (d = (blocknum == 0 ? AFS_DIR_RESV_BLOCKS0 : AFS_DIR_RESV_BLOCKS);
|
||||
d < AFS_DIR_SLOTS_PER_BLOCK;
|
||||
d++) {
|
||||
if (!((bitmap >> d) & 1))
|
||||
continue;
|
||||
de = &block->dirents[d];
|
||||
if (de->u.valid != 1)
|
||||
continue;
|
||||
|
||||
/* The block was NUL-terminated by afs_dir_check_page(). */
|
||||
len = strlen(de->u.name);
|
||||
if (len == name->len &&
|
||||
memcmp(de->u.name, name->name, name->len) == 0)
|
||||
return d;
|
||||
|
||||
n = round_up(12 + len + 1 + 4, AFS_DIR_DIRENT_SIZE);
|
||||
n /= AFS_DIR_DIRENT_SIZE;
|
||||
d += n - 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialise a new directory block. Note that block 0 is special and contains
|
||||
* some extra metadata.
|
||||
*/
|
||||
static void afs_edit_init_block(union afs_xdr_dir_block *meta,
|
||||
union afs_xdr_dir_block *block, int block_num)
|
||||
{
|
||||
memset(block, 0, sizeof(*block));
|
||||
block->hdr.npages = htons(1);
|
||||
block->hdr.magic = AFS_DIR_MAGIC;
|
||||
block->hdr.bitmap[0] = 1;
|
||||
|
||||
if (block_num == 0) {
|
||||
block->hdr.bitmap[0] = 0xff;
|
||||
block->hdr.bitmap[1] = 0x1f;
|
||||
memset(block->meta.alloc_ctrs,
|
||||
AFS_DIR_SLOTS_PER_BLOCK,
|
||||
sizeof(block->meta.alloc_ctrs));
|
||||
meta->meta.alloc_ctrs[0] =
|
||||
AFS_DIR_SLOTS_PER_BLOCK - AFS_DIR_RESV_BLOCKS0;
|
||||
}
|
||||
|
||||
if (block_num < AFS_DIR_BLOCKS_WITH_CTR)
|
||||
meta->meta.alloc_ctrs[block_num] =
|
||||
AFS_DIR_SLOTS_PER_BLOCK - AFS_DIR_RESV_BLOCKS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Edit a directory's file data to add a new directory entry. Doing this after
|
||||
* create, mkdir, symlink, link or rename if the data version number is
|
||||
* incremented by exactly one avoids the need to re-download the entire
|
||||
* directory contents.
|
||||
*
|
||||
* The caller must hold the inode locked.
|
||||
*/
|
||||
void afs_edit_dir_add(struct afs_vnode *vnode,
|
||||
struct qstr *name, struct afs_fid *new_fid,
|
||||
enum afs_edit_dir_reason why)
|
||||
{
|
||||
union afs_xdr_dir_block *meta, *block;
|
||||
struct afs_xdr_dir_page *meta_page, *dir_page;
|
||||
union afs_xdr_dirent *de;
|
||||
struct page *page0, *page;
|
||||
unsigned int need_slots, nr_blocks, b;
|
||||
pgoff_t index;
|
||||
loff_t i_size;
|
||||
gfp_t gfp;
|
||||
int slot;
|
||||
|
||||
_enter(",,{%d,%s},", name->len, name->name);
|
||||
|
||||
i_size = i_size_read(&vnode->vfs_inode);
|
||||
if (i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS ||
|
||||
(i_size & (AFS_DIR_BLOCK_SIZE - 1))) {
|
||||
clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
|
||||
return;
|
||||
}
|
||||
|
||||
gfp = vnode->vfs_inode.i_mapping->gfp_mask;
|
||||
page0 = find_or_create_page(vnode->vfs_inode.i_mapping, 0, gfp);
|
||||
if (!page0) {
|
||||
clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
|
||||
_leave(" [fgp]");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Work out how many slots we're going to need. */
|
||||
need_slots = round_up(12 + name->len + 1 + 4, AFS_DIR_DIRENT_SIZE);
|
||||
need_slots /= AFS_DIR_DIRENT_SIZE;
|
||||
|
||||
meta_page = kmap(page0);
|
||||
meta = &meta_page->blocks[0];
|
||||
if (i_size == 0)
|
||||
goto new_directory;
|
||||
nr_blocks = i_size / AFS_DIR_BLOCK_SIZE;
|
||||
|
||||
/* Find a block that has sufficient slots available. Each VM page
|
||||
* contains two or more directory blocks.
|
||||
*/
|
||||
for (b = 0; b < nr_blocks + 1; b++) {
|
||||
/* If the directory extended into a new page, then we need to
|
||||
* tack a new page on the end.
|
||||
*/
|
||||
index = b / AFS_DIR_BLOCKS_PER_PAGE;
|
||||
if (index == 0) {
|
||||
page = page0;
|
||||
dir_page = meta_page;
|
||||
} else {
|
||||
if (nr_blocks >= AFS_DIR_MAX_BLOCKS)
|
||||
goto error;
|
||||
gfp = vnode->vfs_inode.i_mapping->gfp_mask;
|
||||
page = find_or_create_page(vnode->vfs_inode.i_mapping,
|
||||
index, gfp);
|
||||
if (!page)
|
||||
goto error;
|
||||
if (!PagePrivate(page)) {
|
||||
set_page_private(page, 1);
|
||||
SetPagePrivate(page);
|
||||
}
|
||||
dir_page = kmap(page);
|
||||
}
|
||||
|
||||
/* Abandon the edit if we got a callback break. */
|
||||
if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
|
||||
goto invalidated;
|
||||
|
||||
block = &dir_page->blocks[b % AFS_DIR_BLOCKS_PER_PAGE];
|
||||
|
||||
_debug("block %u: %2u %3u %u",
|
||||
b,
|
||||
(b < AFS_DIR_BLOCKS_WITH_CTR) ? meta->meta.alloc_ctrs[b] : 99,
|
||||
ntohs(block->hdr.npages),
|
||||
ntohs(block->hdr.magic));
|
||||
|
||||
/* Initialise the block if necessary. */
|
||||
if (b == nr_blocks) {
|
||||
_debug("init %u", b);
|
||||
afs_edit_init_block(meta, block, b);
|
||||
i_size_write(&vnode->vfs_inode, (b + 1) * AFS_DIR_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
/* Only lower dir pages have a counter in the header. */
|
||||
if (b >= AFS_DIR_BLOCKS_WITH_CTR ||
|
||||
meta->meta.alloc_ctrs[b] >= need_slots) {
|
||||
/* We need to try and find one or more consecutive
|
||||
* slots to hold the entry.
|
||||
*/
|
||||
slot = afs_find_contig_bits(block, need_slots);
|
||||
if (slot >= 0) {
|
||||
_debug("slot %u", slot);
|
||||
goto found_space;
|
||||
}
|
||||
}
|
||||
|
||||
if (page != page0) {
|
||||
unlock_page(page);
|
||||
kunmap(page);
|
||||
put_page(page);
|
||||
}
|
||||
}
|
||||
|
||||
/* There are no spare slots of sufficient size, yet the operation
|
||||
* succeeded. Download the directory again.
|
||||
*/
|
||||
trace_afs_edit_dir(vnode, why, afs_edit_dir_create_nospc, 0, 0, 0, 0, name->name);
|
||||
clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
|
||||
goto out_unmap;
|
||||
|
||||
new_directory:
|
||||
afs_edit_init_block(meta, meta, 0);
|
||||
i_size = AFS_DIR_BLOCK_SIZE;
|
||||
i_size_write(&vnode->vfs_inode, i_size);
|
||||
slot = AFS_DIR_RESV_BLOCKS0;
|
||||
page = page0;
|
||||
block = meta;
|
||||
nr_blocks = 1;
|
||||
b = 0;
|
||||
|
||||
found_space:
|
||||
/* Set the dirent slot. */
|
||||
trace_afs_edit_dir(vnode, why, afs_edit_dir_create, b, slot,
|
||||
new_fid->vnode, new_fid->unique, name->name);
|
||||
de = &block->dirents[slot];
|
||||
de->u.valid = 1;
|
||||
de->u.unused[0] = 0;
|
||||
de->u.hash_next = 0; // TODO: Really need to maintain this
|
||||
de->u.vnode = htonl(new_fid->vnode);
|
||||
de->u.unique = htonl(new_fid->unique);
|
||||
memcpy(de->u.name, name->name, name->len + 1);
|
||||
de->u.name[name->len] = 0;
|
||||
|
||||
/* Adjust the bitmap. */
|
||||
afs_set_contig_bits(block, slot, need_slots);
|
||||
if (page != page0) {
|
||||
unlock_page(page);
|
||||
kunmap(page);
|
||||
put_page(page);
|
||||
}
|
||||
|
||||
/* Adjust the allocation counter. */
|
||||
if (b < AFS_DIR_BLOCKS_WITH_CTR)
|
||||
meta->meta.alloc_ctrs[b] -= need_slots;
|
||||
|
||||
inode_inc_iversion_raw(&vnode->vfs_inode);
|
||||
afs_stat_v(vnode, n_dir_cr);
|
||||
_debug("Insert %s in %u[%u]", name->name, b, slot);
|
||||
|
||||
out_unmap:
|
||||
unlock_page(page0);
|
||||
kunmap(page0);
|
||||
put_page(page0);
|
||||
_leave("");
|
||||
return;
|
||||
|
||||
invalidated:
|
||||
trace_afs_edit_dir(vnode, why, afs_edit_dir_create_inval, 0, 0, 0, 0, name->name);
|
||||
clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
|
||||
if (page != page0) {
|
||||
kunmap(page);
|
||||
put_page(page);
|
||||
}
|
||||
goto out_unmap;
|
||||
|
||||
error:
|
||||
trace_afs_edit_dir(vnode, why, afs_edit_dir_create_error, 0, 0, 0, 0, name->name);
|
||||
clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
|
||||
goto out_unmap;
|
||||
}
|
||||
|
||||
/*
|
||||
* Edit a directory's file data to remove a new directory entry. Doing this
|
||||
* after unlink, rmdir or rename if the data version number is incremented by
|
||||
* exactly one avoids the need to re-download the entire directory contents.
|
||||
*
|
||||
* The caller must hold the inode locked.
|
||||
*/
|
||||
void afs_edit_dir_remove(struct afs_vnode *vnode,
|
||||
struct qstr *name, enum afs_edit_dir_reason why)
|
||||
{
|
||||
struct afs_xdr_dir_page *meta_page, *dir_page;
|
||||
union afs_xdr_dir_block *meta, *block;
|
||||
union afs_xdr_dirent *de;
|
||||
struct page *page0, *page;
|
||||
unsigned int need_slots, nr_blocks, b;
|
||||
pgoff_t index;
|
||||
loff_t i_size;
|
||||
int slot;
|
||||
|
||||
_enter(",,{%d,%s},", name->len, name->name);
|
||||
|
||||
i_size = i_size_read(&vnode->vfs_inode);
|
||||
if (i_size < AFS_DIR_BLOCK_SIZE ||
|
||||
i_size > AFS_DIR_BLOCK_SIZE * AFS_DIR_MAX_BLOCKS ||
|
||||
(i_size & (AFS_DIR_BLOCK_SIZE - 1))) {
|
||||
clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
|
||||
return;
|
||||
}
|
||||
nr_blocks = i_size / AFS_DIR_BLOCK_SIZE;
|
||||
|
||||
page0 = find_lock_page(vnode->vfs_inode.i_mapping, 0);
|
||||
if (!page0) {
|
||||
clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
|
||||
_leave(" [fgp]");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Work out how many slots we're going to discard. */
|
||||
need_slots = round_up(12 + name->len + 1 + 4, AFS_DIR_DIRENT_SIZE);
|
||||
need_slots /= AFS_DIR_DIRENT_SIZE;
|
||||
|
||||
meta_page = kmap(page0);
|
||||
meta = &meta_page->blocks[0];
|
||||
|
||||
/* Find a page that has sufficient slots available. Each VM page
|
||||
* contains two or more directory blocks.
|
||||
*/
|
||||
for (b = 0; b < nr_blocks; b++) {
|
||||
index = b / AFS_DIR_BLOCKS_PER_PAGE;
|
||||
if (index != 0) {
|
||||
page = find_lock_page(vnode->vfs_inode.i_mapping, index);
|
||||
if (!page)
|
||||
goto error;
|
||||
dir_page = kmap(page);
|
||||
} else {
|
||||
page = page0;
|
||||
dir_page = meta_page;
|
||||
}
|
||||
|
||||
/* Abandon the edit if we got a callback break. */
|
||||
if (!test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
|
||||
goto invalidated;
|
||||
|
||||
block = &dir_page->blocks[b % AFS_DIR_BLOCKS_PER_PAGE];
|
||||
|
||||
if (b > AFS_DIR_BLOCKS_WITH_CTR ||
|
||||
meta->meta.alloc_ctrs[b] <= AFS_DIR_SLOTS_PER_BLOCK - 1 - need_slots) {
|
||||
slot = afs_dir_scan_block(block, name, b);
|
||||
if (slot >= 0)
|
||||
goto found_dirent;
|
||||
}
|
||||
|
||||
if (page != page0) {
|
||||
unlock_page(page);
|
||||
kunmap(page);
|
||||
put_page(page);
|
||||
}
|
||||
}
|
||||
|
||||
/* Didn't find the dirent to clobber. Download the directory again. */
|
||||
trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_noent,
|
||||
0, 0, 0, 0, name->name);
|
||||
clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
|
||||
goto out_unmap;
|
||||
|
||||
found_dirent:
|
||||
de = &block->dirents[slot];
|
||||
|
||||
trace_afs_edit_dir(vnode, why, afs_edit_dir_delete, b, slot,
|
||||
ntohl(de->u.vnode), ntohl(de->u.unique),
|
||||
name->name);
|
||||
|
||||
memset(de, 0, sizeof(*de) * need_slots);
|
||||
|
||||
/* Adjust the bitmap. */
|
||||
afs_clear_contig_bits(block, slot, need_slots);
|
||||
if (page != page0) {
|
||||
unlock_page(page);
|
||||
kunmap(page);
|
||||
put_page(page);
|
||||
}
|
||||
|
||||
/* Adjust the allocation counter. */
|
||||
if (b < AFS_DIR_BLOCKS_WITH_CTR)
|
||||
meta->meta.alloc_ctrs[b] += need_slots;
|
||||
|
||||
inode_set_iversion_raw(&vnode->vfs_inode, vnode->status.data_version);
|
||||
afs_stat_v(vnode, n_dir_rm);
|
||||
_debug("Remove %s from %u[%u]", name->name, b, slot);
|
||||
|
||||
out_unmap:
|
||||
unlock_page(page0);
|
||||
kunmap(page0);
|
||||
put_page(page0);
|
||||
_leave("");
|
||||
return;
|
||||
|
||||
invalidated:
|
||||
trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_inval,
|
||||
0, 0, 0, 0, name->name);
|
||||
clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
|
||||
if (page != page0) {
|
||||
unlock_page(page);
|
||||
kunmap(page);
|
||||
put_page(page);
|
||||
}
|
||||
goto out_unmap;
|
||||
|
||||
error:
|
||||
trace_afs_edit_dir(vnode, why, afs_edit_dir_delete_error,
|
||||
0, 0, 0, 0, name->name);
|
||||
clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
|
||||
goto out_unmap;
|
||||
}
|
209
fs/afs/dynroot.c
Normal file
209
fs/afs/dynroot.c
Normal file
@ -0,0 +1,209 @@
|
||||
/* dir.c: AFS dynamic root handling
|
||||
*
|
||||
* Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/dns_resolver.h>
|
||||
#include "internal.h"
|
||||
|
||||
const struct file_operations afs_dynroot_file_operations = {
|
||||
.open = dcache_dir_open,
|
||||
.release = dcache_dir_close,
|
||||
.iterate_shared = dcache_readdir,
|
||||
.llseek = dcache_dir_lseek,
|
||||
};
|
||||
|
||||
/*
|
||||
* Probe to see if a cell may exist. This prevents positive dentries from
|
||||
* being created unnecessarily.
|
||||
*/
|
||||
static int afs_probe_cell_name(struct dentry *dentry)
|
||||
{
|
||||
struct afs_cell *cell;
|
||||
const char *name = dentry->d_name.name;
|
||||
size_t len = dentry->d_name.len;
|
||||
int ret;
|
||||
|
||||
/* Names prefixed with a dot are R/W mounts. */
|
||||
if (name[0] == '.') {
|
||||
if (len == 1)
|
||||
return -EINVAL;
|
||||
name++;
|
||||
len--;
|
||||
}
|
||||
|
||||
cell = afs_lookup_cell_rcu(afs_d2net(dentry), name, len);
|
||||
if (!IS_ERR(cell)) {
|
||||
afs_put_cell(afs_d2net(dentry), cell);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = dns_query("afsdb", name, len, "ipv4", NULL, NULL);
|
||||
if (ret == -ENODATA)
|
||||
ret = -EDESTADDRREQ;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to auto mount the mountpoint with pseudo directory, if the autocell
|
||||
* operation is setted.
|
||||
*/
|
||||
struct inode *afs_try_auto_mntpt(struct dentry *dentry, struct inode *dir)
|
||||
{
|
||||
struct afs_vnode *vnode = AFS_FS_I(dir);
|
||||
struct inode *inode;
|
||||
int ret = -ENOENT;
|
||||
|
||||
_enter("%p{%pd}, {%x:%u}",
|
||||
dentry, dentry, vnode->fid.vid, vnode->fid.vnode);
|
||||
|
||||
if (!test_bit(AFS_VNODE_AUTOCELL, &vnode->flags))
|
||||
goto out;
|
||||
|
||||
ret = afs_probe_cell_name(dentry);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
inode = afs_iget_pseudo_dir(dir->i_sb, false);
|
||||
if (IS_ERR(inode)) {
|
||||
ret = PTR_ERR(inode);
|
||||
goto out;
|
||||
}
|
||||
|
||||
_leave("= %p", inode);
|
||||
return inode;
|
||||
|
||||
out:
|
||||
_leave("= %d", ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up @cell in a dynroot directory. This is a substitution for the
|
||||
* local cell name for the net namespace.
|
||||
*/
|
||||
static struct dentry *afs_lookup_atcell(struct dentry *dentry)
|
||||
{
|
||||
struct afs_cell *cell;
|
||||
struct afs_net *net = afs_d2net(dentry);
|
||||
struct dentry *ret;
|
||||
unsigned int seq = 0;
|
||||
char *name;
|
||||
int len;
|
||||
|
||||
if (!net->ws_cell)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
ret = ERR_PTR(-ENOMEM);
|
||||
name = kmalloc(AFS_MAXCELLNAME + 1, GFP_KERNEL);
|
||||
if (!name)
|
||||
goto out_p;
|
||||
|
||||
rcu_read_lock();
|
||||
do {
|
||||
read_seqbegin_or_lock(&net->cells_lock, &seq);
|
||||
cell = rcu_dereference_raw(net->ws_cell);
|
||||
if (cell) {
|
||||
len = cell->name_len;
|
||||
memcpy(name, cell->name, len + 1);
|
||||
}
|
||||
} while (need_seqretry(&net->cells_lock, seq));
|
||||
done_seqretry(&net->cells_lock, seq);
|
||||
rcu_read_unlock();
|
||||
|
||||
ret = ERR_PTR(-ENOENT);
|
||||
if (!cell)
|
||||
goto out_n;
|
||||
|
||||
ret = lookup_one_len(name, dentry->d_parent, len);
|
||||
|
||||
/* We don't want to d_add() the @cell dentry here as we don't want to
|
||||
* the cached dentry to hide changes to the local cell name.
|
||||
*/
|
||||
|
||||
out_n:
|
||||
kfree(name);
|
||||
out_p:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Look up an entry in a dynroot directory.
|
||||
*/
|
||||
static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct afs_vnode *vnode;
|
||||
struct inode *inode;
|
||||
int ret;
|
||||
|
||||
vnode = AFS_FS_I(dir);
|
||||
|
||||
_enter("%pd", dentry);
|
||||
|
||||
ASSERTCMP(d_inode(dentry), ==, NULL);
|
||||
|
||||
if (dentry->d_name.len >= AFSNAMEMAX) {
|
||||
_leave(" = -ENAMETOOLONG");
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
}
|
||||
|
||||
if (dentry->d_name.len == 5 &&
|
||||
memcmp(dentry->d_name.name, "@cell", 5) == 0)
|
||||
return afs_lookup_atcell(dentry);
|
||||
|
||||
inode = afs_try_auto_mntpt(dentry, dir);
|
||||
if (IS_ERR(inode)) {
|
||||
ret = PTR_ERR(inode);
|
||||
if (ret == -ENOENT) {
|
||||
d_add(dentry, NULL);
|
||||
_leave(" = NULL [negative]");
|
||||
return NULL;
|
||||
}
|
||||
_leave(" = %d [do]", ret);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
d_add(dentry, inode);
|
||||
_leave(" = 0 { ino=%lu v=%u }",
|
||||
d_inode(dentry)->i_ino, d_inode(dentry)->i_generation);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const struct inode_operations afs_dynroot_inode_operations = {
|
||||
.lookup = afs_dynroot_lookup,
|
||||
};
|
||||
|
||||
/*
|
||||
* Dirs in the dynamic root don't need revalidation.
|
||||
*/
|
||||
static int afs_dynroot_d_revalidate(struct dentry *dentry, unsigned int flags)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allow the VFS to enquire as to whether a dentry should be unhashed (mustn't
|
||||
* sleep)
|
||||
* - called from dput() when d_count is going to 0.
|
||||
* - return 1 to request dentry be unhashed, 0 otherwise
|
||||
*/
|
||||
static int afs_dynroot_d_delete(const struct dentry *dentry)
|
||||
{
|
||||
return d_really_is_positive(dentry);
|
||||
}
|
||||
|
||||
const struct dentry_operations afs_dynroot_dentry_operations = {
|
||||
.d_revalidate = afs_dynroot_d_revalidate,
|
||||
.d_delete = afs_dynroot_d_delete,
|
||||
.d_release = afs_d_release,
|
||||
.d_automount = afs_d_automount,
|
||||
};
|
@ -30,7 +30,6 @@ static int afs_readpages(struct file *filp, struct address_space *mapping,
|
||||
|
||||
const struct file_operations afs_file_operations = {
|
||||
.open = afs_open,
|
||||
.flush = afs_flush,
|
||||
.release = afs_release,
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = generic_file_read_iter,
|
||||
@ -146,6 +145,9 @@ int afs_open(struct inode *inode, struct file *file)
|
||||
if (ret < 0)
|
||||
goto error_af;
|
||||
}
|
||||
|
||||
if (file->f_flags & O_TRUNC)
|
||||
set_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
|
||||
|
||||
file->private_data = af;
|
||||
_leave(" = 0");
|
||||
@ -170,6 +172,9 @@ int afs_release(struct inode *inode, struct file *file)
|
||||
|
||||
_enter("{%x:%u},", vnode->fid.vid, vnode->fid.vnode);
|
||||
|
||||
if ((file->f_mode & FMODE_WRITE))
|
||||
return vfs_fsync(file, 0);
|
||||
|
||||
file->private_data = NULL;
|
||||
if (af->wb)
|
||||
afs_put_wb_key(af->wb);
|
||||
@ -187,10 +192,12 @@ void afs_put_read(struct afs_read *req)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (atomic_dec_and_test(&req->usage)) {
|
||||
if (refcount_dec_and_test(&req->usage)) {
|
||||
for (i = 0; i < req->nr_pages; i++)
|
||||
if (req->pages[i])
|
||||
put_page(req->pages[i]);
|
||||
if (req->pages != req->array)
|
||||
kfree(req->pages);
|
||||
kfree(req);
|
||||
}
|
||||
}
|
||||
@ -240,6 +247,12 @@ int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *de
|
||||
ret = afs_end_vnode_operation(&fc);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
afs_stat_v(vnode, n_fetches);
|
||||
atomic_long_add(desc->actual_len,
|
||||
&afs_v2net(vnode)->n_fetch_bytes);
|
||||
}
|
||||
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
@ -297,10 +310,11 @@ int afs_page_filler(void *data, struct page *page)
|
||||
* end of the file, the server will return a short read and the
|
||||
* unmarshalling code will clear the unfilled space.
|
||||
*/
|
||||
atomic_set(&req->usage, 1);
|
||||
refcount_set(&req->usage, 1);
|
||||
req->pos = (loff_t)page->index << PAGE_SHIFT;
|
||||
req->len = PAGE_SIZE;
|
||||
req->nr_pages = 1;
|
||||
req->pages = req->array;
|
||||
req->pages[0] = page;
|
||||
get_page(page);
|
||||
|
||||
@ -309,10 +323,6 @@ int afs_page_filler(void *data, struct page *page)
|
||||
ret = afs_fetch_data(vnode, key, req);
|
||||
afs_put_read(req);
|
||||
|
||||
if (ret >= 0 && S_ISDIR(inode->i_mode) &&
|
||||
!afs_dir_check_page(inode, page))
|
||||
ret = -EIO;
|
||||
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOENT) {
|
||||
_debug("got NOENT from server"
|
||||
@ -447,10 +457,11 @@ static int afs_readpages_one(struct file *file, struct address_space *mapping,
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
atomic_set(&req->usage, 1);
|
||||
refcount_set(&req->usage, 1);
|
||||
req->page_done = afs_readpages_page_done;
|
||||
req->pos = first->index;
|
||||
req->pos <<= PAGE_SHIFT;
|
||||
req->pages = req->array;
|
||||
|
||||
/* Transfer the pages to the request. We add them in until one fails
|
||||
* to add to the LRU and then we stop (as that'll make a hole in the
|
||||
|
@ -613,7 +613,7 @@ static int afs_do_getlk(struct file *file, struct file_lock *fl)
|
||||
posix_test_lock(file, fl);
|
||||
if (fl->fl_type == F_UNLCK) {
|
||||
/* no local locks; consult the server */
|
||||
ret = afs_fetch_status(vnode, key);
|
||||
ret = afs_fetch_status(vnode, key, false);
|
||||
if (ret < 0)
|
||||
goto error;
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <linux/iversion.h>
|
||||
#include "internal.h"
|
||||
#include "afs_fs.h"
|
||||
#include "xdr_fs.h"
|
||||
|
||||
static const struct afs_fid afs_zero_fid;
|
||||
|
||||
@ -43,110 +44,195 @@ static void xdr_decode_AFSFid(const __be32 **_bp, struct afs_fid *fid)
|
||||
*_bp = bp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Dump a bad file status record.
|
||||
*/
|
||||
static void xdr_dump_bad(const __be32 *bp)
|
||||
{
|
||||
__be32 x[4];
|
||||
int i;
|
||||
|
||||
pr_notice("AFS XDR: Bad status record\n");
|
||||
for (i = 0; i < 5 * 4 * 4; i += 16) {
|
||||
memcpy(x, bp, 16);
|
||||
bp += 4;
|
||||
pr_notice("%03x: %08x %08x %08x %08x\n",
|
||||
i, ntohl(x[0]), ntohl(x[1]), ntohl(x[2]), ntohl(x[3]));
|
||||
}
|
||||
|
||||
memcpy(x, bp, 4);
|
||||
pr_notice("0x50: %08x\n", ntohl(x[0]));
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the core inode struct from a returned status record.
|
||||
*/
|
||||
void afs_update_inode_from_status(struct afs_vnode *vnode,
|
||||
struct afs_file_status *status,
|
||||
const afs_dataversion_t *expected_version,
|
||||
u8 flags)
|
||||
{
|
||||
struct timespec t;
|
||||
umode_t mode;
|
||||
|
||||
t.tv_sec = status->mtime_client;
|
||||
t.tv_nsec = 0;
|
||||
vnode->vfs_inode.i_ctime = t;
|
||||
vnode->vfs_inode.i_mtime = t;
|
||||
vnode->vfs_inode.i_atime = t;
|
||||
|
||||
if (flags & (AFS_VNODE_META_CHANGED | AFS_VNODE_NOT_YET_SET)) {
|
||||
vnode->vfs_inode.i_uid = make_kuid(&init_user_ns, status->owner);
|
||||
vnode->vfs_inode.i_gid = make_kgid(&init_user_ns, status->group);
|
||||
set_nlink(&vnode->vfs_inode, status->nlink);
|
||||
|
||||
mode = vnode->vfs_inode.i_mode;
|
||||
mode &= ~S_IALLUGO;
|
||||
mode |= status->mode;
|
||||
barrier();
|
||||
vnode->vfs_inode.i_mode = mode;
|
||||
}
|
||||
|
||||
if (!(flags & AFS_VNODE_NOT_YET_SET)) {
|
||||
if (expected_version &&
|
||||
*expected_version != status->data_version) {
|
||||
_debug("vnode modified %llx on {%x:%u} [exp %llx]",
|
||||
(unsigned long long) status->data_version,
|
||||
vnode->fid.vid, vnode->fid.vnode,
|
||||
(unsigned long long) *expected_version);
|
||||
vnode->invalid_before = status->data_version;
|
||||
if (vnode->status.type == AFS_FTYPE_DIR) {
|
||||
if (test_and_clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
|
||||
afs_stat_v(vnode, n_inval);
|
||||
} else {
|
||||
set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags);
|
||||
}
|
||||
} else if (vnode->status.type == AFS_FTYPE_DIR) {
|
||||
/* Expected directory change is handled elsewhere so
|
||||
* that we can locally edit the directory and save on a
|
||||
* download.
|
||||
*/
|
||||
if (test_bit(AFS_VNODE_DIR_VALID, &vnode->flags))
|
||||
flags &= ~AFS_VNODE_DATA_CHANGED;
|
||||
}
|
||||
}
|
||||
|
||||
if (flags & (AFS_VNODE_DATA_CHANGED | AFS_VNODE_NOT_YET_SET)) {
|
||||
inode_set_iversion_raw(&vnode->vfs_inode, status->data_version);
|
||||
i_size_write(&vnode->vfs_inode, status->size);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* decode an AFSFetchStatus block
|
||||
*/
|
||||
static void xdr_decode_AFSFetchStatus(const __be32 **_bp,
|
||||
struct afs_file_status *status,
|
||||
struct afs_vnode *vnode,
|
||||
afs_dataversion_t *store_version)
|
||||
static int xdr_decode_AFSFetchStatus(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)
|
||||
{
|
||||
afs_dataversion_t expected_version;
|
||||
const __be32 *bp = *_bp;
|
||||
umode_t mode;
|
||||
const struct afs_xdr_AFSFetchStatus *xdr = (const void *)*_bp;
|
||||
u64 data_version, size;
|
||||
bool changed = false;
|
||||
kuid_t owner;
|
||||
kgid_t group;
|
||||
u32 type, abort_code;
|
||||
u8 flags = 0;
|
||||
int ret;
|
||||
|
||||
if (vnode)
|
||||
write_seqlock(&vnode->cb_lock);
|
||||
|
||||
#define EXTRACT(DST) \
|
||||
do { \
|
||||
u32 x = ntohl(*bp++); \
|
||||
if (DST != x) \
|
||||
changed |= true; \
|
||||
DST = x; \
|
||||
if (xdr->if_version != htonl(AFS_FSTATUS_VERSION)) {
|
||||
pr_warn("Unknown AFSFetchStatus version %u\n", ntohl(xdr->if_version));
|
||||
goto bad;
|
||||
}
|
||||
|
||||
type = ntohl(xdr->type);
|
||||
abort_code = ntohl(xdr->abort_code);
|
||||
switch (type) {
|
||||
case AFS_FTYPE_FILE:
|
||||
case AFS_FTYPE_DIR:
|
||||
case AFS_FTYPE_SYMLINK:
|
||||
if (type != status->type &&
|
||||
vnode &&
|
||||
!test_bit(AFS_VNODE_UNSET, &vnode->flags)) {
|
||||
pr_warning("Vnode %x:%x:%x changed type %u to %u\n",
|
||||
vnode->fid.vid,
|
||||
vnode->fid.vnode,
|
||||
vnode->fid.unique,
|
||||
status->type, type);
|
||||
goto bad;
|
||||
}
|
||||
status->type = type;
|
||||
break;
|
||||
case AFS_FTYPE_INVALID:
|
||||
if (abort_code != 0) {
|
||||
status->abort_code = abort_code;
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
/* Fall through */
|
||||
default:
|
||||
goto bad;
|
||||
}
|
||||
|
||||
#define EXTRACT_M(FIELD) \
|
||||
do { \
|
||||
u32 x = ntohl(xdr->FIELD); \
|
||||
if (status->FIELD != x) { \
|
||||
flags |= AFS_VNODE_META_CHANGED; \
|
||||
status->FIELD = x; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
status->if_version = ntohl(*bp++);
|
||||
EXTRACT(status->type);
|
||||
EXTRACT(status->nlink);
|
||||
size = ntohl(*bp++);
|
||||
data_version = ntohl(*bp++);
|
||||
EXTRACT(status->author);
|
||||
owner = make_kuid(&init_user_ns, ntohl(*bp++));
|
||||
changed |= !uid_eq(owner, status->owner);
|
||||
status->owner = owner;
|
||||
EXTRACT(status->caller_access); /* call ticket dependent */
|
||||
EXTRACT(status->anon_access);
|
||||
EXTRACT(status->mode);
|
||||
bp++; /* parent.vnode */
|
||||
bp++; /* parent.unique */
|
||||
bp++; /* seg size */
|
||||
status->mtime_client = ntohl(*bp++);
|
||||
status->mtime_server = ntohl(*bp++);
|
||||
group = make_kgid(&init_user_ns, ntohl(*bp++));
|
||||
changed |= !gid_eq(group, status->group);
|
||||
status->group = group;
|
||||
bp++; /* sync counter */
|
||||
data_version |= (u64) ntohl(*bp++) << 32;
|
||||
EXTRACT(status->lock_count);
|
||||
size |= (u64) ntohl(*bp++) << 32;
|
||||
bp++; /* spare 4 */
|
||||
*_bp = bp;
|
||||
EXTRACT_M(nlink);
|
||||
EXTRACT_M(author);
|
||||
EXTRACT_M(owner);
|
||||
EXTRACT_M(caller_access); /* call ticket dependent */
|
||||
EXTRACT_M(anon_access);
|
||||
EXTRACT_M(mode);
|
||||
EXTRACT_M(group);
|
||||
|
||||
if (size != status->size) {
|
||||
status->size = size;
|
||||
changed |= true;
|
||||
status->mtime_client = ntohl(xdr->mtime_client);
|
||||
status->mtime_server = ntohl(xdr->mtime_server);
|
||||
status->lock_count = ntohl(xdr->lock_count);
|
||||
|
||||
size = (u64)ntohl(xdr->size_lo);
|
||||
size |= (u64)ntohl(xdr->size_hi) << 32;
|
||||
status->size = size;
|
||||
|
||||
data_version = (u64)ntohl(xdr->data_version_lo);
|
||||
data_version |= (u64)ntohl(xdr->data_version_hi) << 32;
|
||||
if (data_version != status->data_version) {
|
||||
status->data_version = data_version;
|
||||
flags |= AFS_VNODE_DATA_CHANGED;
|
||||
}
|
||||
status->mode &= S_IALLUGO;
|
||||
|
||||
_debug("vnode time %lx, %lx",
|
||||
status->mtime_client, status->mtime_server);
|
||||
if (read_req) {
|
||||
read_req->data_version = data_version;
|
||||
read_req->file_size = size;
|
||||
}
|
||||
|
||||
*_bp = (const void *)*_bp + sizeof(*xdr);
|
||||
|
||||
if (vnode) {
|
||||
if (changed && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) {
|
||||
_debug("vnode changed");
|
||||
i_size_write(&vnode->vfs_inode, size);
|
||||
vnode->vfs_inode.i_uid = status->owner;
|
||||
vnode->vfs_inode.i_gid = status->group;
|
||||
vnode->vfs_inode.i_generation = vnode->fid.unique;
|
||||
set_nlink(&vnode->vfs_inode, status->nlink);
|
||||
|
||||
mode = vnode->vfs_inode.i_mode;
|
||||
mode &= ~S_IALLUGO;
|
||||
mode |= status->mode;
|
||||
barrier();
|
||||
vnode->vfs_inode.i_mode = mode;
|
||||
}
|
||||
|
||||
vnode->vfs_inode.i_ctime.tv_sec = status->mtime_client;
|
||||
vnode->vfs_inode.i_mtime = vnode->vfs_inode.i_ctime;
|
||||
vnode->vfs_inode.i_atime = vnode->vfs_inode.i_ctime;
|
||||
inode_set_iversion_raw(&vnode->vfs_inode, data_version);
|
||||
if (test_bit(AFS_VNODE_UNSET, &vnode->flags))
|
||||
flags |= AFS_VNODE_NOT_YET_SET;
|
||||
afs_update_inode_from_status(vnode, status, expected_version,
|
||||
flags);
|
||||
}
|
||||
|
||||
expected_version = status->data_version;
|
||||
if (store_version)
|
||||
expected_version = *store_version;
|
||||
|
||||
if (expected_version != data_version) {
|
||||
status->data_version = data_version;
|
||||
if (vnode && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) {
|
||||
_debug("vnode modified %llx on {%x:%u}",
|
||||
(unsigned long long) data_version,
|
||||
vnode->fid.vid, vnode->fid.vnode);
|
||||
set_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags);
|
||||
set_bit(AFS_VNODE_ZAP_DATA, &vnode->flags);
|
||||
}
|
||||
} else if (store_version) {
|
||||
status->data_version = data_version;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
if (vnode)
|
||||
write_sequnlock(&vnode->cb_lock);
|
||||
return ret;
|
||||
|
||||
bad:
|
||||
xdr_dump_bad(*_bp);
|
||||
ret = afs_protocol_error(call, -EBADMSG);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -274,7 +360,7 @@ static void xdr_decode_AFSFetchVolumeStatus(const __be32 **_bp,
|
||||
/*
|
||||
* deliver reply data to an FS.FetchStatus
|
||||
*/
|
||||
static int afs_deliver_fs_fetch_status(struct afs_call *call)
|
||||
static int afs_deliver_fs_fetch_status_vnode(struct afs_call *call)
|
||||
{
|
||||
struct afs_vnode *vnode = call->reply[0];
|
||||
const __be32 *bp;
|
||||
@ -288,7 +374,9 @@ 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(&bp, &vnode->status, vnode, NULL);
|
||||
if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode,
|
||||
&call->expected_version, NULL) < 0)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
xdr_decode_AFSCallBack(call, vnode, &bp);
|
||||
if (call->reply[1])
|
||||
xdr_decode_AFSVolSync(&bp, call->reply[1]);
|
||||
@ -300,17 +388,18 @@ static int afs_deliver_fs_fetch_status(struct afs_call *call)
|
||||
/*
|
||||
* FS.FetchStatus operation type
|
||||
*/
|
||||
static const struct afs_call_type afs_RXFSFetchStatus = {
|
||||
.name = "FS.FetchStatus",
|
||||
static const struct afs_call_type afs_RXFSFetchStatus_vnode = {
|
||||
.name = "FS.FetchStatus(vnode)",
|
||||
.op = afs_FS_FetchStatus,
|
||||
.deliver = afs_deliver_fs_fetch_status,
|
||||
.deliver = afs_deliver_fs_fetch_status_vnode,
|
||||
.destructor = afs_flat_call_destructor,
|
||||
};
|
||||
|
||||
/*
|
||||
* fetch the status information for a file
|
||||
*/
|
||||
int afs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_volsync *volsync)
|
||||
int afs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_volsync *volsync,
|
||||
bool new_inode)
|
||||
{
|
||||
struct afs_vnode *vnode = fc->vnode;
|
||||
struct afs_call *call;
|
||||
@ -320,7 +409,8 @@ int afs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_volsync *volsy
|
||||
_enter(",%x,{%x:%u},,",
|
||||
key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode);
|
||||
|
||||
call = afs_alloc_flat_call(net, &afs_RXFSFetchStatus, 16, (21 + 3 + 6) * 4);
|
||||
call = afs_alloc_flat_call(net, &afs_RXFSFetchStatus_vnode,
|
||||
16, (21 + 3 + 6) * 4);
|
||||
if (!call) {
|
||||
fc->ac.error = -ENOMEM;
|
||||
return -ENOMEM;
|
||||
@ -329,6 +419,7 @@ int afs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_volsync *volsy
|
||||
call->key = fc->key;
|
||||
call->reply[0] = vnode;
|
||||
call->reply[1] = volsync;
|
||||
call->expected_version = new_inode ? 1 : vnode->status.data_version;
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = call->request;
|
||||
@ -464,7 +555,9 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
|
||||
return ret;
|
||||
|
||||
bp = call->buffer;
|
||||
xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
|
||||
if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode,
|
||||
&vnode->status.data_version, req) < 0)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
xdr_decode_AFSCallBack(call, vnode, &bp);
|
||||
if (call->reply[1])
|
||||
xdr_decode_AFSVolSync(&bp, call->reply[1]);
|
||||
@ -534,6 +627,7 @@ static int afs_fs_fetch_data64(struct afs_fs_cursor *fc, struct afs_read *req)
|
||||
call->reply[0] = vnode;
|
||||
call->reply[1] = NULL; /* volsync */
|
||||
call->reply[2] = req;
|
||||
call->expected_version = vnode->status.data_version;
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = call->request;
|
||||
@ -546,7 +640,7 @@ static int afs_fs_fetch_data64(struct afs_fs_cursor *fc, struct afs_read *req)
|
||||
bp[6] = 0;
|
||||
bp[7] = htonl(lower_32_bits(req->len));
|
||||
|
||||
atomic_inc(&req->usage);
|
||||
refcount_inc(&req->usage);
|
||||
call->cb_break = fc->cb_break;
|
||||
afs_use_fs_server(call, fc->cbi);
|
||||
trace_afs_make_fs_call(call, &vnode->fid);
|
||||
@ -578,6 +672,7 @@ int afs_fs_fetch_data(struct afs_fs_cursor *fc, struct afs_read *req)
|
||||
call->reply[0] = vnode;
|
||||
call->reply[1] = NULL; /* volsync */
|
||||
call->reply[2] = req;
|
||||
call->expected_version = vnode->status.data_version;
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = call->request;
|
||||
@ -588,7 +683,7 @@ int afs_fs_fetch_data(struct afs_fs_cursor *fc, struct afs_read *req)
|
||||
bp[4] = htonl(lower_32_bits(req->pos));
|
||||
bp[5] = htonl(lower_32_bits(req->len));
|
||||
|
||||
atomic_inc(&req->usage);
|
||||
refcount_inc(&req->usage);
|
||||
call->cb_break = fc->cb_break;
|
||||
afs_use_fs_server(call, fc->cbi);
|
||||
trace_afs_make_fs_call(call, &vnode->fid);
|
||||
@ -613,8 +708,10 @@ 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]);
|
||||
xdr_decode_AFSFetchStatus(&bp, call->reply[2], NULL, NULL);
|
||||
xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
|
||||
if (xdr_decode_AFSFetchStatus(call, &bp, call->reply[2], NULL, NULL, NULL) < 0 ||
|
||||
xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode,
|
||||
&call->expected_version, NULL) < 0)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
xdr_decode_AFSCallBack_raw(&bp, call->reply[3]);
|
||||
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
|
||||
|
||||
@ -645,6 +742,7 @@ static const struct afs_call_type afs_RXFSMakeDir = {
|
||||
int afs_fs_create(struct afs_fs_cursor *fc,
|
||||
const char *name,
|
||||
umode_t mode,
|
||||
u64 current_data_version,
|
||||
struct afs_fid *newfid,
|
||||
struct afs_file_status *newstatus,
|
||||
struct afs_callback *newcb)
|
||||
@ -672,6 +770,7 @@ int afs_fs_create(struct afs_fs_cursor *fc,
|
||||
call->reply[1] = newfid;
|
||||
call->reply[2] = newstatus;
|
||||
call->reply[3] = newcb;
|
||||
call->expected_version = current_data_version + 1;
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = call->request;
|
||||
@ -715,7 +814,9 @@ static int afs_deliver_fs_remove(struct afs_call *call)
|
||||
|
||||
/* unmarshall the reply once we've received all of it */
|
||||
bp = call->buffer;
|
||||
xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
|
||||
if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode,
|
||||
&call->expected_version, NULL) < 0)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
|
||||
|
||||
_leave(" = 0 [done]");
|
||||
@ -742,7 +843,8 @@ static const struct afs_call_type afs_RXFSRemoveDir = {
|
||||
/*
|
||||
* remove a file or directory
|
||||
*/
|
||||
int afs_fs_remove(struct afs_fs_cursor *fc, const char *name, bool isdir)
|
||||
int afs_fs_remove(struct afs_fs_cursor *fc, const char *name, bool isdir,
|
||||
u64 current_data_version)
|
||||
{
|
||||
struct afs_vnode *vnode = fc->vnode;
|
||||
struct afs_call *call;
|
||||
@ -764,6 +866,7 @@ int afs_fs_remove(struct afs_fs_cursor *fc, const char *name, bool isdir)
|
||||
|
||||
call->key = fc->key;
|
||||
call->reply[0] = vnode;
|
||||
call->expected_version = current_data_version + 1;
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = call->request;
|
||||
@ -801,8 +904,10 @@ static int afs_deliver_fs_link(struct afs_call *call)
|
||||
|
||||
/* unmarshall the reply once we've received all of it */
|
||||
bp = call->buffer;
|
||||
xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
|
||||
xdr_decode_AFSFetchStatus(&bp, &dvnode->status, dvnode, NULL);
|
||||
if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode, NULL, NULL) < 0 ||
|
||||
xdr_decode_AFSFetchStatus(call, &bp, &dvnode->status, dvnode,
|
||||
&call->expected_version, NULL) < 0)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
|
||||
|
||||
_leave(" = 0 [done]");
|
||||
@ -823,7 +928,7 @@ static const struct afs_call_type afs_RXFSLink = {
|
||||
* make a hard link
|
||||
*/
|
||||
int afs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode,
|
||||
const char *name)
|
||||
const char *name, u64 current_data_version)
|
||||
{
|
||||
struct afs_vnode *dvnode = fc->vnode;
|
||||
struct afs_call *call;
|
||||
@ -844,6 +949,7 @@ int afs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode,
|
||||
call->key = fc->key;
|
||||
call->reply[0] = dvnode;
|
||||
call->reply[1] = vnode;
|
||||
call->expected_version = current_data_version + 1;
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = call->request;
|
||||
@ -885,8 +991,10 @@ 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]);
|
||||
xdr_decode_AFSFetchStatus(&bp, call->reply[2], NULL, NULL);
|
||||
xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL);
|
||||
if (xdr_decode_AFSFetchStatus(call, &bp, call->reply[2], NULL, NULL, NULL) ||
|
||||
xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode,
|
||||
&call->expected_version, NULL) < 0)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
|
||||
|
||||
_leave(" = 0 [done]");
|
||||
@ -909,6 +1017,7 @@ static const struct afs_call_type afs_RXFSSymlink = {
|
||||
int afs_fs_symlink(struct afs_fs_cursor *fc,
|
||||
const char *name,
|
||||
const char *contents,
|
||||
u64 current_data_version,
|
||||
struct afs_fid *newfid,
|
||||
struct afs_file_status *newstatus)
|
||||
{
|
||||
@ -937,6 +1046,7 @@ int afs_fs_symlink(struct afs_fs_cursor *fc,
|
||||
call->reply[0] = vnode;
|
||||
call->reply[1] = newfid;
|
||||
call->reply[2] = newstatus;
|
||||
call->expected_version = current_data_version + 1;
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = call->request;
|
||||
@ -987,10 +1097,13 @@ static int afs_deliver_fs_rename(struct afs_call *call)
|
||||
|
||||
/* unmarshall the reply once we've received all of it */
|
||||
bp = call->buffer;
|
||||
xdr_decode_AFSFetchStatus(&bp, &orig_dvnode->status, orig_dvnode, NULL);
|
||||
if (new_dvnode != orig_dvnode)
|
||||
xdr_decode_AFSFetchStatus(&bp, &new_dvnode->status, new_dvnode,
|
||||
NULL);
|
||||
if (xdr_decode_AFSFetchStatus(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,
|
||||
&call->expected_version_2, NULL) < 0)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
|
||||
|
||||
_leave(" = 0 [done]");
|
||||
@ -1013,7 +1126,9 @@ static const struct afs_call_type afs_RXFSRename = {
|
||||
int afs_fs_rename(struct afs_fs_cursor *fc,
|
||||
const char *orig_name,
|
||||
struct afs_vnode *new_dvnode,
|
||||
const char *new_name)
|
||||
const char *new_name,
|
||||
u64 current_orig_data_version,
|
||||
u64 current_new_data_version)
|
||||
{
|
||||
struct afs_vnode *orig_dvnode = fc->vnode;
|
||||
struct afs_call *call;
|
||||
@ -1041,6 +1156,8 @@ int afs_fs_rename(struct afs_fs_cursor *fc,
|
||||
call->key = fc->key;
|
||||
call->reply[0] = orig_dvnode;
|
||||
call->reply[1] = new_dvnode;
|
||||
call->expected_version = current_orig_data_version + 1;
|
||||
call->expected_version_2 = current_new_data_version + 1;
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = call->request;
|
||||
@ -1089,8 +1206,9 @@ static int afs_deliver_fs_store_data(struct afs_call *call)
|
||||
|
||||
/* unmarshall the reply once we've received all of it */
|
||||
bp = call->buffer;
|
||||
xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode,
|
||||
&call->store_version);
|
||||
if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode,
|
||||
&call->expected_version, NULL) < 0)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
|
||||
|
||||
afs_pages_written_back(vnode, call);
|
||||
@ -1147,7 +1265,7 @@ static int afs_fs_store_data64(struct afs_fs_cursor *fc,
|
||||
call->first_offset = offset;
|
||||
call->last_to = to;
|
||||
call->send_pages = true;
|
||||
call->store_version = vnode->status.data_version + 1;
|
||||
call->expected_version = vnode->status.data_version + 1;
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = call->request;
|
||||
@ -1222,7 +1340,7 @@ int afs_fs_store_data(struct afs_fs_cursor *fc, struct address_space *mapping,
|
||||
call->first_offset = offset;
|
||||
call->last_to = to;
|
||||
call->send_pages = true;
|
||||
call->store_version = vnode->status.data_version + 1;
|
||||
call->expected_version = vnode->status.data_version + 1;
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = call->request;
|
||||
@ -1252,7 +1370,6 @@ int afs_fs_store_data(struct afs_fs_cursor *fc, struct address_space *mapping,
|
||||
*/
|
||||
static int afs_deliver_fs_store_status(struct afs_call *call)
|
||||
{
|
||||
afs_dataversion_t *store_version;
|
||||
struct afs_vnode *vnode = call->reply[0];
|
||||
const __be32 *bp;
|
||||
int ret;
|
||||
@ -1264,12 +1381,10 @@ static int afs_deliver_fs_store_status(struct afs_call *call)
|
||||
return ret;
|
||||
|
||||
/* unmarshall the reply once we've received all of it */
|
||||
store_version = NULL;
|
||||
if (call->operation_ID == FSSTOREDATA)
|
||||
store_version = &call->store_version;
|
||||
|
||||
bp = call->buffer;
|
||||
xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, store_version);
|
||||
if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode,
|
||||
&call->expected_version, NULL) < 0)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
|
||||
|
||||
_leave(" = 0 [done]");
|
||||
@ -1324,7 +1439,7 @@ static int afs_fs_setattr_size64(struct afs_fs_cursor *fc, struct iattr *attr)
|
||||
|
||||
call->key = fc->key;
|
||||
call->reply[0] = vnode;
|
||||
call->store_version = vnode->status.data_version + 1;
|
||||
call->expected_version = vnode->status.data_version + 1;
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = call->request;
|
||||
@ -1373,7 +1488,7 @@ static int afs_fs_setattr_size(struct afs_fs_cursor *fc, struct iattr *attr)
|
||||
|
||||
call->key = fc->key;
|
||||
call->reply[0] = vnode;
|
||||
call->store_version = vnode->status.data_version + 1;
|
||||
call->expected_version = vnode->status.data_version + 1;
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = call->request;
|
||||
@ -1418,6 +1533,7 @@ int afs_fs_setattr(struct afs_fs_cursor *fc, struct iattr *attr)
|
||||
|
||||
call->key = fc->key;
|
||||
call->reply[0] = vnode;
|
||||
call->expected_version = vnode->status.data_version;
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = call->request;
|
||||
@ -1471,7 +1587,7 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call)
|
||||
call->count = ntohl(call->tmp);
|
||||
_debug("volname length: %u", call->count);
|
||||
if (call->count >= AFSNAMEMAX)
|
||||
return -EBADMSG;
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
call->offset = 0;
|
||||
call->unmarshall++;
|
||||
|
||||
@ -1518,7 +1634,7 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call)
|
||||
call->count = ntohl(call->tmp);
|
||||
_debug("offline msg length: %u", call->count);
|
||||
if (call->count >= AFSNAMEMAX)
|
||||
return -EBADMSG;
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
call->offset = 0;
|
||||
call->unmarshall++;
|
||||
|
||||
@ -1565,7 +1681,7 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call)
|
||||
call->count = ntohl(call->tmp);
|
||||
_debug("motd length: %u", call->count);
|
||||
if (call->count >= AFSNAMEMAX)
|
||||
return -EBADMSG;
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
call->offset = 0;
|
||||
call->unmarshall++;
|
||||
|
||||
@ -1947,3 +2063,265 @@ int afs_fs_get_capabilities(struct afs_net *net,
|
||||
trace_afs_make_fs_call(call, NULL);
|
||||
return afs_make_call(ac, call, GFP_NOFS, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Deliver reply data to an FS.FetchStatus with no vnode.
|
||||
*/
|
||||
static int afs_deliver_fs_fetch_status(struct afs_call *call)
|
||||
{
|
||||
struct afs_file_status *status = call->reply[1];
|
||||
struct afs_callback *callback = call->reply[2];
|
||||
struct afs_volsync *volsync = call->reply[3];
|
||||
struct afs_vnode *vnode = call->reply[0];
|
||||
const __be32 *bp;
|
||||
int ret;
|
||||
|
||||
ret = afs_transfer_reply(call);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
_enter("{%x:%u}", vnode->fid.vid, vnode->fid.vnode);
|
||||
|
||||
/* unmarshall the reply once we've received all of it */
|
||||
bp = call->buffer;
|
||||
xdr_decode_AFSFetchStatus(call, &bp, status, vnode,
|
||||
&call->expected_version, NULL);
|
||||
callback[call->count].version = ntohl(bp[0]);
|
||||
callback[call->count].expiry = ntohl(bp[1]);
|
||||
callback[call->count].type = ntohl(bp[2]);
|
||||
if (vnode)
|
||||
xdr_decode_AFSCallBack(call, vnode, &bp);
|
||||
else
|
||||
bp += 3;
|
||||
if (volsync)
|
||||
xdr_decode_AFSVolSync(&bp, volsync);
|
||||
|
||||
_leave(" = 0 [done]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* FS.FetchStatus operation type
|
||||
*/
|
||||
static const struct afs_call_type afs_RXFSFetchStatus = {
|
||||
.name = "FS.FetchStatus",
|
||||
.op = afs_FS_FetchStatus,
|
||||
.deliver = afs_deliver_fs_fetch_status,
|
||||
.destructor = afs_flat_call_destructor,
|
||||
};
|
||||
|
||||
/*
|
||||
* Fetch the status information for a fid without needing a vnode handle.
|
||||
*/
|
||||
int afs_fs_fetch_status(struct afs_fs_cursor *fc,
|
||||
struct afs_net *net,
|
||||
struct afs_fid *fid,
|
||||
struct afs_file_status *status,
|
||||
struct afs_callback *callback,
|
||||
struct afs_volsync *volsync)
|
||||
{
|
||||
struct afs_call *call;
|
||||
__be32 *bp;
|
||||
|
||||
_enter(",%x,{%x:%u},,",
|
||||
key_serial(fc->key), fid->vid, fid->vnode);
|
||||
|
||||
call = afs_alloc_flat_call(net, &afs_RXFSFetchStatus, 16, (21 + 3 + 6) * 4);
|
||||
if (!call) {
|
||||
fc->ac.error = -ENOMEM;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
call->key = fc->key;
|
||||
call->reply[0] = NULL; /* vnode for fid[0] */
|
||||
call->reply[1] = status;
|
||||
call->reply[2] = callback;
|
||||
call->reply[3] = volsync;
|
||||
call->expected_version = 1; /* vnode->status.data_version */
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = call->request;
|
||||
bp[0] = htonl(FSFETCHSTATUS);
|
||||
bp[1] = htonl(fid->vid);
|
||||
bp[2] = htonl(fid->vnode);
|
||||
bp[3] = htonl(fid->unique);
|
||||
|
||||
call->cb_break = fc->cb_break;
|
||||
afs_use_fs_server(call, fc->cbi);
|
||||
trace_afs_make_fs_call(call, fid);
|
||||
return afs_make_call(&fc->ac, call, GFP_NOFS, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* Deliver reply data to an FS.InlineBulkStatus call
|
||||
*/
|
||||
static int afs_deliver_fs_inline_bulk_status(struct afs_call *call)
|
||||
{
|
||||
struct afs_file_status *statuses;
|
||||
struct afs_callback *callbacks;
|
||||
struct afs_vnode *vnode = call->reply[0];
|
||||
const __be32 *bp;
|
||||
u32 tmp;
|
||||
int ret;
|
||||
|
||||
_enter("{%u}", call->unmarshall);
|
||||
|
||||
switch (call->unmarshall) {
|
||||
case 0:
|
||||
call->offset = 0;
|
||||
call->unmarshall++;
|
||||
|
||||
/* Extract the file status count and array in two steps */
|
||||
case 1:
|
||||
_debug("extract status count");
|
||||
ret = afs_extract_data(call, &call->tmp, 4, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tmp = ntohl(call->tmp);
|
||||
_debug("status count: %u/%u", tmp, call->count2);
|
||||
if (tmp != call->count2)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
|
||||
call->count = 0;
|
||||
call->unmarshall++;
|
||||
more_counts:
|
||||
call->offset = 0;
|
||||
|
||||
case 2:
|
||||
_debug("extract status array %u", call->count);
|
||||
ret = afs_extract_data(call, call->buffer, 21 * 4, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
bp = call->buffer;
|
||||
statuses = call->reply[1];
|
||||
if (xdr_decode_AFSFetchStatus(call, &bp, &statuses[call->count],
|
||||
call->count == 0 ? vnode : NULL,
|
||||
NULL, NULL) < 0)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
|
||||
call->count++;
|
||||
if (call->count < call->count2)
|
||||
goto more_counts;
|
||||
|
||||
call->count = 0;
|
||||
call->unmarshall++;
|
||||
call->offset = 0;
|
||||
|
||||
/* Extract the callback count and array in two steps */
|
||||
case 3:
|
||||
_debug("extract CB count");
|
||||
ret = afs_extract_data(call, &call->tmp, 4, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
tmp = ntohl(call->tmp);
|
||||
_debug("CB count: %u", tmp);
|
||||
if (tmp != call->count2)
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
call->count = 0;
|
||||
call->unmarshall++;
|
||||
more_cbs:
|
||||
call->offset = 0;
|
||||
|
||||
case 4:
|
||||
_debug("extract CB array");
|
||||
ret = afs_extract_data(call, call->buffer, 3 * 4, true);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
_debug("unmarshall CB array");
|
||||
bp = call->buffer;
|
||||
callbacks = call->reply[2];
|
||||
callbacks[call->count].version = ntohl(bp[0]);
|
||||
callbacks[call->count].expiry = ntohl(bp[1]);
|
||||
callbacks[call->count].type = ntohl(bp[2]);
|
||||
statuses = call->reply[1];
|
||||
if (call->count == 0 && vnode && statuses[0].abort_code == 0)
|
||||
xdr_decode_AFSCallBack(call, vnode, &bp);
|
||||
call->count++;
|
||||
if (call->count < call->count2)
|
||||
goto more_cbs;
|
||||
|
||||
call->offset = 0;
|
||||
call->unmarshall++;
|
||||
|
||||
case 5:
|
||||
ret = afs_extract_data(call, call->buffer, 6 * 4, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
bp = call->buffer;
|
||||
if (call->reply[3])
|
||||
xdr_decode_AFSVolSync(&bp, call->reply[3]);
|
||||
|
||||
call->offset = 0;
|
||||
call->unmarshall++;
|
||||
|
||||
case 6:
|
||||
break;
|
||||
}
|
||||
|
||||
_leave(" = 0 [done]");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* FS.InlineBulkStatus operation type
|
||||
*/
|
||||
static const struct afs_call_type afs_RXFSInlineBulkStatus = {
|
||||
.name = "FS.InlineBulkStatus",
|
||||
.op = afs_FS_InlineBulkStatus,
|
||||
.deliver = afs_deliver_fs_inline_bulk_status,
|
||||
.destructor = afs_flat_call_destructor,
|
||||
};
|
||||
|
||||
/*
|
||||
* Fetch the status information for up to 50 files
|
||||
*/
|
||||
int afs_fs_inline_bulk_status(struct afs_fs_cursor *fc,
|
||||
struct afs_net *net,
|
||||
struct afs_fid *fids,
|
||||
struct afs_file_status *statuses,
|
||||
struct afs_callback *callbacks,
|
||||
unsigned int nr_fids,
|
||||
struct afs_volsync *volsync)
|
||||
{
|
||||
struct afs_call *call;
|
||||
__be32 *bp;
|
||||
int i;
|
||||
|
||||
_enter(",%x,{%x:%u},%u",
|
||||
key_serial(fc->key), fids[0].vid, fids[1].vnode, nr_fids);
|
||||
|
||||
call = afs_alloc_flat_call(net, &afs_RXFSInlineBulkStatus,
|
||||
(2 + nr_fids * 3) * 4,
|
||||
21 * 4);
|
||||
if (!call) {
|
||||
fc->ac.error = -ENOMEM;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
call->key = fc->key;
|
||||
call->reply[0] = NULL; /* vnode for fid[0] */
|
||||
call->reply[1] = statuses;
|
||||
call->reply[2] = callbacks;
|
||||
call->reply[3] = volsync;
|
||||
call->count2 = nr_fids;
|
||||
|
||||
/* marshall the parameters */
|
||||
bp = call->request;
|
||||
*bp++ = htonl(FSINLINEBULKSTATUS);
|
||||
*bp++ = htonl(nr_fids);
|
||||
for (i = 0; i < nr_fids; i++) {
|
||||
*bp++ = htonl(fids[i].vid);
|
||||
*bp++ = htonl(fids[i].vnode);
|
||||
*bp++ = htonl(fids[i].unique);
|
||||
}
|
||||
|
||||
call->cb_break = fc->cb_break;
|
||||
afs_use_fs_server(call, fc->cbi);
|
||||
trace_afs_make_fs_call(call, &fids[0]);
|
||||
return afs_make_call(&fc->ac, call, GFP_NOFS, false);
|
||||
}
|
||||
|
@ -30,12 +30,11 @@ static const struct inode_operations afs_symlink_inode_operations = {
|
||||
};
|
||||
|
||||
/*
|
||||
* map the AFS file status to the inode member variables
|
||||
* Initialise an inode from the vnode status.
|
||||
*/
|
||||
static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
|
||||
static int afs_inode_init_from_status(struct afs_vnode *vnode, struct key *key)
|
||||
{
|
||||
struct inode *inode = AFS_VNODE_TO_I(vnode);
|
||||
bool changed;
|
||||
|
||||
_debug("FS: ft=%d lk=%d sz=%llu ver=%Lu mod=%hu",
|
||||
vnode->status.type,
|
||||
@ -46,16 +45,21 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
|
||||
|
||||
read_seqlock_excl(&vnode->cb_lock);
|
||||
|
||||
afs_update_inode_from_status(vnode, &vnode->status, NULL,
|
||||
AFS_VNODE_NOT_YET_SET);
|
||||
|
||||
switch (vnode->status.type) {
|
||||
case AFS_FTYPE_FILE:
|
||||
inode->i_mode = S_IFREG | vnode->status.mode;
|
||||
inode->i_op = &afs_file_inode_operations;
|
||||
inode->i_fop = &afs_file_operations;
|
||||
inode->i_mapping->a_ops = &afs_fs_aops;
|
||||
break;
|
||||
case AFS_FTYPE_DIR:
|
||||
inode->i_mode = S_IFDIR | vnode->status.mode;
|
||||
inode->i_op = &afs_dir_inode_operations;
|
||||
inode->i_fop = &afs_dir_file_operations;
|
||||
inode->i_mapping->a_ops = &afs_dir_aops;
|
||||
break;
|
||||
case AFS_FTYPE_SYMLINK:
|
||||
/* Symlinks with a mode of 0644 are actually mountpoints. */
|
||||
@ -67,45 +71,31 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
|
||||
inode->i_mode = S_IFDIR | 0555;
|
||||
inode->i_op = &afs_mntpt_inode_operations;
|
||||
inode->i_fop = &afs_mntpt_file_operations;
|
||||
inode->i_mapping->a_ops = &afs_fs_aops;
|
||||
} else {
|
||||
inode->i_mode = S_IFLNK | vnode->status.mode;
|
||||
inode->i_op = &afs_symlink_inode_operations;
|
||||
inode->i_mapping->a_ops = &afs_fs_aops;
|
||||
}
|
||||
inode_nohighmem(inode);
|
||||
break;
|
||||
default:
|
||||
printk("kAFS: AFS vnode with undefined type\n");
|
||||
read_sequnlock_excl(&vnode->cb_lock);
|
||||
return -EBADMSG;
|
||||
return afs_protocol_error(NULL, -EBADMSG);
|
||||
}
|
||||
|
||||
changed = (vnode->status.size != inode->i_size);
|
||||
|
||||
set_nlink(inode, vnode->status.nlink);
|
||||
inode->i_uid = vnode->status.owner;
|
||||
inode->i_gid = vnode->status.group;
|
||||
inode->i_size = vnode->status.size;
|
||||
inode->i_ctime.tv_sec = vnode->status.mtime_client;
|
||||
inode->i_ctime.tv_nsec = 0;
|
||||
inode->i_atime = inode->i_mtime = inode->i_ctime;
|
||||
inode->i_blocks = 0;
|
||||
inode->i_generation = vnode->fid.unique;
|
||||
inode_set_iversion_raw(inode, vnode->status.data_version);
|
||||
inode->i_mapping->a_ops = &afs_fs_aops;
|
||||
vnode->invalid_before = vnode->status.data_version;
|
||||
|
||||
read_sequnlock_excl(&vnode->cb_lock);
|
||||
|
||||
#ifdef CONFIG_AFS_FSCACHE
|
||||
if (changed)
|
||||
fscache_attr_changed(vnode->cache);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch file status from the volume.
|
||||
*/
|
||||
int afs_fetch_status(struct afs_vnode *vnode, struct key *key)
|
||||
int afs_fetch_status(struct afs_vnode *vnode, struct key *key, bool new_inode)
|
||||
{
|
||||
struct afs_fs_cursor fc;
|
||||
int ret;
|
||||
@ -119,7 +109,7 @@ int afs_fetch_status(struct afs_vnode *vnode, struct key *key)
|
||||
if (afs_begin_vnode_operation(&fc, vnode, key)) {
|
||||
while (afs_select_fileserver(&fc)) {
|
||||
fc.cb_break = vnode->cb_break + vnode->cb_s_break;
|
||||
afs_fs_fetch_file_status(&fc, NULL);
|
||||
afs_fs_fetch_file_status(&fc, NULL, new_inode);
|
||||
}
|
||||
|
||||
afs_check_for_remote_deletion(&fc, fc.vnode);
|
||||
@ -255,6 +245,11 @@ static void afs_get_inode_cache(struct afs_vnode *vnode)
|
||||
} __packed key;
|
||||
struct afs_vnode_cache_aux aux;
|
||||
|
||||
if (vnode->status.type == AFS_FTYPE_DIR) {
|
||||
vnode->cache = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
key.vnode_id = vnode->fid.vnode;
|
||||
key.unique = vnode->fid.unique;
|
||||
key.vnode_id_ext[0] = 0;
|
||||
@ -307,7 +302,7 @@ struct inode *afs_iget(struct super_block *sb, struct key *key,
|
||||
|
||||
if (!status) {
|
||||
/* it's a remotely extant inode */
|
||||
ret = afs_fetch_status(vnode, key);
|
||||
ret = afs_fetch_status(vnode, key, true);
|
||||
if (ret < 0)
|
||||
goto bad_inode;
|
||||
} else {
|
||||
@ -331,15 +326,12 @@ struct inode *afs_iget(struct super_block *sb, struct key *key,
|
||||
vnode->cb_expires_at += ktime_get_real_seconds();
|
||||
}
|
||||
|
||||
/* set up caching before mapping the status, as map-status reads the
|
||||
* first page of symlinks to see if they're really mountpoints */
|
||||
inode->i_size = vnode->status.size;
|
||||
afs_get_inode_cache(vnode);
|
||||
|
||||
ret = afs_inode_map_status(vnode, key);
|
||||
ret = afs_inode_init_from_status(vnode, key);
|
||||
if (ret < 0)
|
||||
goto bad_inode;
|
||||
|
||||
afs_get_inode_cache(vnode);
|
||||
|
||||
/* success */
|
||||
clear_bit(AFS_VNODE_UNSET, &vnode->flags);
|
||||
inode->i_flags |= S_NOATIME;
|
||||
@ -349,10 +341,6 @@ struct inode *afs_iget(struct super_block *sb, struct key *key,
|
||||
|
||||
/* failure */
|
||||
bad_inode:
|
||||
#ifdef CONFIG_AFS_FSCACHE
|
||||
fscache_relinquish_cookie(vnode->cache, NULL, ret == -ENOENT);
|
||||
vnode->cache = NULL;
|
||||
#endif
|
||||
iget_failed(inode);
|
||||
_leave(" = %d [bad]", ret);
|
||||
return ERR_PTR(ret);
|
||||
@ -407,8 +395,11 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
|
||||
if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
|
||||
if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break) {
|
||||
vnode->cb_s_break = vnode->cb_interest->server->cb_s_break;
|
||||
} else if (!test_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags) &&
|
||||
!test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) &&
|
||||
} else if (vnode->status.type == AFS_FTYPE_DIR &&
|
||||
test_bit(AFS_VNODE_DIR_VALID, &vnode->flags) &&
|
||||
vnode->cb_expires_at - 10 > now) {
|
||||
valid = true;
|
||||
} else if (!test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) &&
|
||||
vnode->cb_expires_at - 10 > now) {
|
||||
valid = true;
|
||||
}
|
||||
@ -432,7 +423,7 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
|
||||
* access */
|
||||
if (!test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
|
||||
_debug("not promised");
|
||||
ret = afs_fetch_status(vnode, key);
|
||||
ret = afs_fetch_status(vnode, key, false);
|
||||
if (ret < 0) {
|
||||
if (ret == -ENOENT) {
|
||||
set_bit(AFS_VNODE_DELETED, &vnode->flags);
|
||||
@ -453,8 +444,6 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
|
||||
* different */
|
||||
if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags))
|
||||
afs_zap_data(vnode);
|
||||
|
||||
clear_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags);
|
||||
mutex_unlock(&vnode->validate_lock);
|
||||
valid:
|
||||
_leave(" = 0");
|
||||
@ -544,7 +533,7 @@ void afs_evict_inode(struct inode *inode)
|
||||
}
|
||||
#endif
|
||||
|
||||
afs_put_permits(vnode->permit_cache);
|
||||
afs_put_permits(rcu_access_pointer(vnode->permit_cache));
|
||||
_leave("");
|
||||
}
|
||||
|
||||
|
@ -122,7 +122,8 @@ struct afs_call {
|
||||
u32 operation_ID; /* operation ID for an incoming call */
|
||||
u32 count; /* count for use in unmarshalling */
|
||||
__be32 tmp; /* place to extract temporary data */
|
||||
afs_dataversion_t store_version; /* updated version expected from store */
|
||||
afs_dataversion_t expected_version; /* Updated version expected from store */
|
||||
afs_dataversion_t expected_version_2; /* 2nd updated version expected from store */
|
||||
};
|
||||
|
||||
struct afs_call_type {
|
||||
@ -173,11 +174,14 @@ struct afs_read {
|
||||
loff_t len; /* How much we're asking for */
|
||||
loff_t actual_len; /* How much we're actually getting */
|
||||
loff_t remain; /* Amount remaining */
|
||||
atomic_t usage;
|
||||
loff_t file_size; /* File size returned by server */
|
||||
afs_dataversion_t data_version; /* Version number returned by server */
|
||||
refcount_t usage;
|
||||
unsigned int index; /* Which page we're reading into */
|
||||
unsigned int nr_pages;
|
||||
void (*page_done)(struct afs_call *, struct afs_read *);
|
||||
struct page *pages[];
|
||||
struct page **pages;
|
||||
struct page *array[];
|
||||
};
|
||||
|
||||
/*
|
||||
@ -198,6 +202,18 @@ static inline struct afs_super_info *AFS_FS_S(struct super_block *sb)
|
||||
|
||||
extern struct file_system_type afs_fs_type;
|
||||
|
||||
/*
|
||||
* Set of substitutes for @sys.
|
||||
*/
|
||||
struct afs_sysnames {
|
||||
#define AFS_NR_SYSNAME 16
|
||||
char *subs[AFS_NR_SYSNAME];
|
||||
refcount_t usage;
|
||||
unsigned short nr;
|
||||
short error;
|
||||
char blank[1];
|
||||
};
|
||||
|
||||
/*
|
||||
* AFS network namespace record.
|
||||
*/
|
||||
@ -245,9 +261,25 @@ struct afs_net {
|
||||
struct mutex lock_manager_mutex;
|
||||
|
||||
/* Misc */
|
||||
struct proc_dir_entry *proc_afs; /* /proc/net/afs directory */
|
||||
struct proc_dir_entry *proc_afs; /* /proc/net/afs directory */
|
||||
struct afs_sysnames *sysnames;
|
||||
rwlock_t sysnames_lock;
|
||||
|
||||
/* Statistics counters */
|
||||
atomic_t n_lookup; /* Number of lookups done */
|
||||
atomic_t n_reval; /* Number of dentries needing revalidation */
|
||||
atomic_t n_inval; /* Number of invalidations by the server */
|
||||
atomic_t n_relpg; /* Number of invalidations by releasepage */
|
||||
atomic_t n_read_dir; /* Number of directory pages read */
|
||||
atomic_t n_dir_cr; /* Number of directory entry creation edits */
|
||||
atomic_t n_dir_rm; /* Number of directory entry removal edits */
|
||||
atomic_t n_stores; /* Number of store ops */
|
||||
atomic_long_t n_store_bytes; /* Number of bytes stored */
|
||||
atomic_long_t n_fetch_bytes; /* Number of bytes fetched */
|
||||
atomic_t n_fetches; /* Number of data fetch ops */
|
||||
};
|
||||
|
||||
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 {
|
||||
@ -363,6 +395,7 @@ struct afs_server {
|
||||
#define AFS_SERVER_FL_UPDATING 4
|
||||
#define AFS_SERVER_FL_PROBED 5 /* The fileserver has been probed */
|
||||
#define AFS_SERVER_FL_PROBING 6 /* Fileserver is being probed */
|
||||
#define AFS_SERVER_FL_NO_IBULK 7 /* Fileserver doesn't support FS.InlineBulkStatus */
|
||||
atomic_t usage;
|
||||
u32 addr_version; /* Address list version */
|
||||
|
||||
@ -455,10 +488,11 @@ struct afs_vnode {
|
||||
struct afs_volume *volume; /* volume on which vnode resides */
|
||||
struct afs_fid fid; /* the file identifier for this inode */
|
||||
struct afs_file_status status; /* AFS status info for this file */
|
||||
afs_dataversion_t invalid_before; /* Child dentries are invalid before this */
|
||||
#ifdef CONFIG_AFS_FSCACHE
|
||||
struct fscache_cookie *cache; /* caching cookie */
|
||||
#endif
|
||||
struct afs_permits *permit_cache; /* cache of permits so far obtained */
|
||||
struct afs_permits __rcu *permit_cache; /* cache of permits so far obtained */
|
||||
struct mutex io_lock; /* Lock for serialising I/O on this mutex */
|
||||
struct mutex validate_lock; /* lock for validating this vnode */
|
||||
spinlock_t wb_lock; /* lock for wb_keys */
|
||||
@ -466,12 +500,13 @@ struct afs_vnode {
|
||||
unsigned long flags;
|
||||
#define AFS_VNODE_CB_PROMISED 0 /* Set if vnode has a callback promise */
|
||||
#define AFS_VNODE_UNSET 1 /* set if vnode attributes not yet set */
|
||||
#define AFS_VNODE_DIR_MODIFIED 2 /* set if dir vnode's data modified */
|
||||
#define AFS_VNODE_DIR_VALID 2 /* Set if dir contents are valid */
|
||||
#define AFS_VNODE_ZAP_DATA 3 /* set if vnode's data should be invalidated */
|
||||
#define AFS_VNODE_DELETED 4 /* set if vnode deleted on server */
|
||||
#define AFS_VNODE_MOUNTPOINT 5 /* set if vnode is a mountpoint symlink */
|
||||
#define AFS_VNODE_AUTOCELL 6 /* set if Vnode is an auto mount point */
|
||||
#define AFS_VNODE_PSEUDODIR 7 /* set if Vnode is a pseudo directory */
|
||||
#define AFS_VNODE_NEW_CONTENT 8 /* Set if file has new content (create/trunc-0) */
|
||||
|
||||
struct list_head wb_keys; /* List of keys available for writeback */
|
||||
struct list_head pending_locks; /* locks waiting to be granted */
|
||||
@ -611,7 +646,7 @@ extern struct fscache_cookie_def afs_vnode_cache_index_def;
|
||||
*/
|
||||
extern void afs_init_callback_state(struct afs_server *);
|
||||
extern void afs_break_callback(struct afs_vnode *);
|
||||
extern void afs_break_callbacks(struct afs_server *, size_t,struct afs_callback[]);
|
||||
extern void afs_break_callbacks(struct afs_server *, size_t, struct afs_callback_break*);
|
||||
|
||||
extern int afs_register_server_cb_interest(struct afs_vnode *, struct afs_server_entry *);
|
||||
extern void afs_put_cb_interest(struct afs_net *, struct afs_cb_interest *);
|
||||
@ -646,11 +681,26 @@ extern bool afs_cm_incoming_call(struct afs_call *);
|
||||
*/
|
||||
extern const struct file_operations afs_dir_file_operations;
|
||||
extern const struct inode_operations afs_dir_inode_operations;
|
||||
extern const struct file_operations afs_dynroot_file_operations;
|
||||
extern const struct inode_operations afs_dynroot_inode_operations;
|
||||
extern const struct address_space_operations afs_dir_aops;
|
||||
extern const struct dentry_operations afs_fs_dentry_operations;
|
||||
|
||||
extern bool afs_dir_check_page(struct inode *, struct page *);
|
||||
extern void afs_d_release(struct dentry *);
|
||||
|
||||
/*
|
||||
* dir_edit.c
|
||||
*/
|
||||
extern void afs_edit_dir_add(struct afs_vnode *, struct qstr *, struct afs_fid *,
|
||||
enum afs_edit_dir_reason);
|
||||
extern void afs_edit_dir_remove(struct afs_vnode *, struct qstr *, enum afs_edit_dir_reason);
|
||||
|
||||
/*
|
||||
* dynroot.c
|
||||
*/
|
||||
extern const struct file_operations afs_dynroot_file_operations;
|
||||
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 *);
|
||||
|
||||
/*
|
||||
* file.c
|
||||
@ -680,17 +730,23 @@ extern int afs_flock(struct file *, int, struct file_lock *);
|
||||
/*
|
||||
* fsclient.c
|
||||
*/
|
||||
extern int afs_fs_fetch_file_status(struct afs_fs_cursor *, struct afs_volsync *);
|
||||
#define AFS_VNODE_NOT_YET_SET 0x01
|
||||
#define AFS_VNODE_META_CHANGED 0x02
|
||||
#define AFS_VNODE_DATA_CHANGED 0x04
|
||||
extern void afs_update_inode_from_status(struct afs_vnode *, struct afs_file_status *,
|
||||
const afs_dataversion_t *, u8);
|
||||
|
||||
extern int afs_fs_fetch_file_status(struct afs_fs_cursor *, struct afs_volsync *, bool);
|
||||
extern int afs_fs_give_up_callbacks(struct afs_net *, struct afs_server *);
|
||||
extern int afs_fs_fetch_data(struct afs_fs_cursor *, struct afs_read *);
|
||||
extern int afs_fs_create(struct afs_fs_cursor *, const char *, umode_t,
|
||||
extern int afs_fs_create(struct afs_fs_cursor *, const char *, umode_t, u64,
|
||||
struct afs_fid *, struct afs_file_status *, struct afs_callback *);
|
||||
extern int afs_fs_remove(struct afs_fs_cursor *, const char *, bool);
|
||||
extern int afs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *);
|
||||
extern int afs_fs_symlink(struct afs_fs_cursor *, const char *, const char *,
|
||||
extern int afs_fs_remove(struct afs_fs_cursor *, const char *, bool, u64);
|
||||
extern int afs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *, u64);
|
||||
extern int afs_fs_symlink(struct afs_fs_cursor *, const char *, const char *, u64,
|
||||
struct afs_fid *, struct afs_file_status *);
|
||||
extern int afs_fs_rename(struct afs_fs_cursor *, const char *,
|
||||
struct afs_vnode *, const char *);
|
||||
struct afs_vnode *, const char *, u64, u64);
|
||||
extern int afs_fs_store_data(struct afs_fs_cursor *, struct address_space *,
|
||||
pgoff_t, pgoff_t, unsigned, unsigned);
|
||||
extern int afs_fs_setattr(struct afs_fs_cursor *, struct iattr *);
|
||||
@ -702,11 +758,18 @@ extern int afs_fs_give_up_all_callbacks(struct afs_net *, struct afs_server *,
|
||||
struct afs_addr_cursor *, struct key *);
|
||||
extern int afs_fs_get_capabilities(struct afs_net *, struct afs_server *,
|
||||
struct afs_addr_cursor *, struct key *);
|
||||
extern int afs_fs_inline_bulk_status(struct afs_fs_cursor *, struct afs_net *,
|
||||
struct afs_fid *, struct afs_file_status *,
|
||||
struct afs_callback *, unsigned int,
|
||||
struct afs_volsync *);
|
||||
extern int afs_fs_fetch_status(struct afs_fs_cursor *, struct afs_net *,
|
||||
struct afs_fid *, struct afs_file_status *,
|
||||
struct afs_callback *, struct afs_volsync *);
|
||||
|
||||
/*
|
||||
* inode.c
|
||||
*/
|
||||
extern int afs_fetch_status(struct afs_vnode *, struct key *);
|
||||
extern int afs_fetch_status(struct afs_vnode *, struct key *, bool);
|
||||
extern int afs_iget5_test(struct inode *, void *);
|
||||
extern struct inode *afs_iget_pseudo_dir(struct super_block *, bool);
|
||||
extern struct inode *afs_iget(struct super_block *, struct key *,
|
||||
@ -754,6 +817,13 @@ static inline void afs_put_net(struct afs_net *net)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void __afs_stat(atomic_t *s)
|
||||
{
|
||||
atomic_inc(s);
|
||||
}
|
||||
|
||||
#define afs_stat_v(vnode, n) __afs_stat(&afs_v2net(vnode)->n)
|
||||
|
||||
/*
|
||||
* misc.c
|
||||
*/
|
||||
@ -781,6 +851,7 @@ 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 void afs_put_sysnames(struct afs_sysnames *);
|
||||
|
||||
/*
|
||||
* rotate.c
|
||||
@ -809,6 +880,7 @@ extern void afs_flat_call_destructor(struct afs_call *);
|
||||
extern void afs_send_empty_reply(struct afs_call *);
|
||||
extern void afs_send_simple_reply(struct afs_call *, const void *, size_t);
|
||||
extern int afs_extract_data(struct afs_call *, void *, size_t, bool);
|
||||
extern int afs_protocol_error(struct afs_call *, int);
|
||||
|
||||
static inline int afs_transfer_reply(struct afs_call *call)
|
||||
{
|
||||
@ -955,7 +1027,6 @@ extern int afs_writepage(struct page *, struct writeback_control *);
|
||||
extern int afs_writepages(struct address_space *, struct writeback_control *);
|
||||
extern void afs_pages_written_back(struct afs_vnode *, struct afs_call *);
|
||||
extern ssize_t afs_file_write(struct kiocb *, struct iov_iter *);
|
||||
extern int afs_flush(struct file *, fl_owner_t);
|
||||
extern int afs_fsync(struct file *, loff_t, loff_t, int);
|
||||
extern int afs_page_mkwrite(struct vm_fault *);
|
||||
extern void afs_prune_wb_keys(struct afs_vnode *);
|
||||
|
@ -34,11 +34,42 @@ MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list");
|
||||
struct workqueue_struct *afs_wq;
|
||||
struct afs_net __afs_net;
|
||||
|
||||
#if defined(CONFIG_ALPHA)
|
||||
const char afs_init_sysname[] = "alpha_linux26";
|
||||
#elif defined(CONFIG_X86_64)
|
||||
const char afs_init_sysname[] = "amd64_linux26";
|
||||
#elif defined(CONFIG_ARM)
|
||||
const char afs_init_sysname[] = "arm_linux26";
|
||||
#elif defined(CONFIG_ARM64)
|
||||
const char afs_init_sysname[] = "aarch64_linux26";
|
||||
#elif defined(CONFIG_X86_32)
|
||||
const char afs_init_sysname[] = "i386_linux26";
|
||||
#elif defined(CONFIG_IA64)
|
||||
const char afs_init_sysname[] = "ia64_linux26";
|
||||
#elif defined(CONFIG_PPC64)
|
||||
const char afs_init_sysname[] = "ppc64_linux26";
|
||||
#elif defined(CONFIG_PPC32)
|
||||
const char afs_init_sysname[] = "ppc_linux26";
|
||||
#elif defined(CONFIG_S390)
|
||||
#ifdef CONFIG_64BIT
|
||||
const char afs_init_sysname[] = "s390x_linux26";
|
||||
#else
|
||||
const char afs_init_sysname[] = "s390_linux26";
|
||||
#endif
|
||||
#elif defined(CONFIG_SPARC64)
|
||||
const char afs_init_sysname[] = "sparc64_linux26";
|
||||
#elif defined(CONFIG_SPARC32)
|
||||
const char afs_init_sysname[] = "sparc_linux26";
|
||||
#else
|
||||
const char afs_init_sysname[] = "unknown_linux26";
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialise an AFS network namespace record.
|
||||
*/
|
||||
static int __net_init afs_net_init(struct afs_net *net)
|
||||
{
|
||||
struct afs_sysnames *sysnames;
|
||||
int ret;
|
||||
|
||||
net->live = true;
|
||||
@ -67,6 +98,16 @@ static int __net_init afs_net_init(struct afs_net *net)
|
||||
INIT_WORK(&net->fs_manager, afs_manage_servers);
|
||||
timer_setup(&net->fs_timer, afs_servers_timer, 0);
|
||||
|
||||
ret = -ENOMEM;
|
||||
sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
|
||||
if (!sysnames)
|
||||
goto error_sysnames;
|
||||
sysnames->subs[0] = (char *)&afs_init_sysname;
|
||||
sysnames->nr = 1;
|
||||
refcount_set(&sysnames->usage, 1);
|
||||
net->sysnames = sysnames;
|
||||
rwlock_init(&net->sysnames_lock);
|
||||
|
||||
/* Register the /proc stuff */
|
||||
ret = afs_proc_init(net);
|
||||
if (ret < 0)
|
||||
@ -92,6 +133,8 @@ error_cell_init:
|
||||
net->live = false;
|
||||
afs_proc_cleanup(net);
|
||||
error_proc:
|
||||
afs_put_sysnames(net->sysnames);
|
||||
error_sysnames:
|
||||
net->live = false;
|
||||
return ret;
|
||||
}
|
||||
@ -106,6 +149,7 @@ static void __net_exit afs_net_exit(struct afs_net *net)
|
||||
afs_purge_servers(net);
|
||||
afs_close_socket(net);
|
||||
afs_proc_cleanup(net);
|
||||
afs_put_sysnames(net->sysnames);
|
||||
}
|
||||
|
||||
/*
|
||||
|
326
fs/afs/proc.c
326
fs/afs/proc.c
@ -126,6 +126,34 @@ static const struct file_operations afs_proc_servers_fops = {
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
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 const struct file_operations afs_proc_stats_fops;
|
||||
|
||||
/*
|
||||
* initialise the /proc/fs/afs/ directory
|
||||
*/
|
||||
@ -139,7 +167,9 @@ int afs_proc_init(struct afs_net *net)
|
||||
|
||||
if (!proc_create("cells", 0644, net->proc_afs, &afs_proc_cells_fops) ||
|
||||
!proc_create("rootcell", 0644, net->proc_afs, &afs_proc_rootcell_fops) ||
|
||||
!proc_create("servers", 0644, net->proc_afs, &afs_proc_servers_fops))
|
||||
!proc_create("servers", 0644, net->proc_afs, &afs_proc_servers_fops) ||
|
||||
!proc_create("stats", 0644, net->proc_afs, &afs_proc_stats_fops) ||
|
||||
!proc_create("sysname", 0644, net->proc_afs, &afs_proc_sysname_fops))
|
||||
goto error_tree;
|
||||
|
||||
_leave(" = 0");
|
||||
@ -183,6 +213,7 @@ static int afs_proc_cells_open(struct inode *inode, struct file *file)
|
||||
* first item
|
||||
*/
|
||||
static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
|
||||
__acquires(rcu)
|
||||
{
|
||||
struct afs_net *net = afs_seq2net(m);
|
||||
|
||||
@ -204,6 +235,7 @@ static void *afs_proc_cells_next(struct seq_file *m, void *v, loff_t *pos)
|
||||
* clean up after reading from the cells list
|
||||
*/
|
||||
static void afs_proc_cells_stop(struct seq_file *m, void *v)
|
||||
__releases(rcu)
|
||||
{
|
||||
rcu_read_unlock();
|
||||
}
|
||||
@ -282,7 +314,8 @@ static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
|
||||
goto done;
|
||||
}
|
||||
|
||||
set_bit(AFS_CELL_FL_NO_GC, &cell->flags);
|
||||
if (test_and_set_bit(AFS_CELL_FL_NO_GC, &cell->flags))
|
||||
afs_put_cell(net, cell);
|
||||
printk("kAFS: Added new cell '%s'\n", name);
|
||||
} else {
|
||||
goto inval;
|
||||
@ -304,7 +337,40 @@ inval:
|
||||
static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf,
|
||||
size_t size, loff_t *_pos)
|
||||
{
|
||||
return 0;
|
||||
struct afs_cell *cell;
|
||||
struct afs_net *net = afs_proc2net(file);
|
||||
unsigned int seq = 0;
|
||||
char name[AFS_MAXCELLNAME + 1];
|
||||
int len;
|
||||
|
||||
if (*_pos > 0)
|
||||
return 0;
|
||||
if (!net->ws_cell)
|
||||
return 0;
|
||||
|
||||
rcu_read_lock();
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -327,6 +393,12 @@ static ssize_t afs_proc_rootcell_write(struct file *file,
|
||||
if (IS_ERR(kbuf))
|
||||
return PTR_ERR(kbuf);
|
||||
|
||||
ret = -EINVAL;
|
||||
if (kbuf[0] == '.')
|
||||
goto out;
|
||||
if (memchr(kbuf, '/', size))
|
||||
goto out;
|
||||
|
||||
/* trim to first NL */
|
||||
s = memchr(kbuf, '\n', size);
|
||||
if (s)
|
||||
@ -339,6 +411,7 @@ static ssize_t afs_proc_rootcell_write(struct file *file,
|
||||
if (ret >= 0)
|
||||
ret = size; /* consume everything, always */
|
||||
|
||||
out:
|
||||
kfree(kbuf);
|
||||
_leave(" = %d", ret);
|
||||
return ret;
|
||||
@ -413,6 +486,7 @@ static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file)
|
||||
* first item
|
||||
*/
|
||||
static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
|
||||
__acquires(cell->proc_lock)
|
||||
{
|
||||
struct afs_cell *cell = m->private;
|
||||
|
||||
@ -438,6 +512,7 @@ static void *afs_proc_cell_volumes_next(struct seq_file *p, 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)
|
||||
{
|
||||
struct afs_cell *cell = p->private;
|
||||
|
||||
@ -500,6 +575,7 @@ static int afs_proc_cell_vlservers_open(struct inode *inode, struct file *file)
|
||||
* first item
|
||||
*/
|
||||
static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
|
||||
__acquires(rcu)
|
||||
{
|
||||
struct afs_addr_list *alist;
|
||||
struct afs_cell *cell = m->private;
|
||||
@ -544,6 +620,7 @@ static void *afs_proc_cell_vlservers_next(struct seq_file *p, 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)
|
||||
{
|
||||
rcu_read_unlock();
|
||||
}
|
||||
@ -580,6 +657,7 @@ static int afs_proc_servers_open(struct inode *inode, struct file *file)
|
||||
* first item.
|
||||
*/
|
||||
static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
|
||||
__acquires(rcu)
|
||||
{
|
||||
struct afs_net *net = afs_seq2net(m);
|
||||
|
||||
@ -601,6 +679,7 @@ static void *afs_proc_servers_next(struct seq_file *m, void *v, loff_t *_pos)
|
||||
* clean up after reading from the cells list
|
||||
*/
|
||||
static void afs_proc_servers_stop(struct seq_file *p, void *v)
|
||||
__releases(rcu)
|
||||
{
|
||||
rcu_read_unlock();
|
||||
}
|
||||
@ -626,3 +705,244 @@ static int afs_proc_servers_show(struct seq_file *m, void *v)
|
||||
&alist->addrs[alist->index].transport);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void afs_put_sysnames(struct afs_sysnames *sysnames)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (sysnames && refcount_dec_and_test(&sysnames->usage)) {
|
||||
for (i = 0; i < sysnames->nr; i++)
|
||||
if (sysnames->subs[i] != afs_init_sysname &&
|
||||
sysnames->subs[i] != sysnames->blank)
|
||||
kfree(sysnames->subs[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 seq_file *m;
|
||||
int ret;
|
||||
|
||||
ret = seq_open(file, &afs_proc_sysname_ops);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (file->f_mode & FMODE_WRITE) {
|
||||
sysnames = kzalloc(sizeof(*sysnames), GFP_KERNEL);
|
||||
if (!sysnames) {
|
||||
seq_release(inode, file);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
refcount_set(&sysnames->usage, 1);
|
||||
m = file->private_data;
|
||||
m->private = sysnames;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle writes to /proc/fs/afs/sysname to set the @sys substitution.
|
||||
*/
|
||||
static ssize_t afs_proc_sysname_write(struct file *file,
|
||||
const char __user *buf,
|
||||
size_t size, loff_t *_pos)
|
||||
{
|
||||
struct afs_sysnames *sysnames;
|
||||
struct seq_file *m = file->private_data;
|
||||
char *kbuf = NULL, *s, *p, *sub;
|
||||
int ret, len;
|
||||
|
||||
sysnames = m->private;
|
||||
if (!sysnames)
|
||||
return -EINVAL;
|
||||
if (sysnames->error)
|
||||
return sysnames->error;
|
||||
|
||||
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;
|
||||
while ((s = strsep(&p, " \t\n"))) {
|
||||
len = strlen(s);
|
||||
if (len == 0)
|
||||
continue;
|
||||
ret = -ENAMETOOLONG;
|
||||
if (len >= AFSNAMEMAX)
|
||||
goto error;
|
||||
|
||||
if (len >= 4 &&
|
||||
s[len - 4] == '@' &&
|
||||
s[len - 3] == 's' &&
|
||||
s[len - 2] == 'y' &&
|
||||
s[len - 1] == 's')
|
||||
/* Protect against recursion */
|
||||
goto invalid;
|
||||
|
||||
if (s[0] == '.' &&
|
||||
(len < 2 || (len == 2 && s[1] == '.')))
|
||||
goto invalid;
|
||||
|
||||
if (memchr(s, '/', len))
|
||||
goto invalid;
|
||||
|
||||
ret = -EFBIG;
|
||||
if (sysnames->nr >= AFS_NR_SYSNAME)
|
||||
goto out;
|
||||
|
||||
if (strcmp(s, afs_init_sysname) == 0) {
|
||||
sub = (char *)afs_init_sysname;
|
||||
} else {
|
||||
ret = -ENOMEM;
|
||||
sub = kmemdup(s, len + 1, GFP_KERNEL);
|
||||
if (!sub)
|
||||
goto out;
|
||||
}
|
||||
|
||||
sysnames->subs[sysnames->nr] = sub;
|
||||
sysnames->nr++;
|
||||
}
|
||||
|
||||
ret = size; /* consume everything, always */
|
||||
out:
|
||||
inode_unlock(file_inode(file));
|
||||
kfree(kbuf);
|
||||
return ret;
|
||||
|
||||
invalid:
|
||||
ret = -EINVAL;
|
||||
error:
|
||||
sysnames->error = ret;
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int afs_proc_sysname_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct afs_sysnames *sysnames, *kill = NULL;
|
||||
struct seq_file *m = file->private_data;
|
||||
struct afs_net *net = afs_seq2net(m);
|
||||
|
||||
sysnames = m->private;
|
||||
if (sysnames) {
|
||||
if (!sysnames->error) {
|
||||
kill = sysnames;
|
||||
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);
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Display general per-net namespace statistics
|
||||
*/
|
||||
static int afs_proc_stats_show(struct seq_file *m, void *v)
|
||||
{
|
||||
struct afs_net *net = afs_seq2net(m);
|
||||
|
||||
seq_puts(m, "kAFS statistics\n");
|
||||
|
||||
seq_printf(m, "dir-mgmt: look=%u reval=%u inval=%u relpg=%u\n",
|
||||
atomic_read(&net->n_lookup),
|
||||
atomic_read(&net->n_reval),
|
||||
atomic_read(&net->n_inval),
|
||||
atomic_read(&net->n_relpg));
|
||||
|
||||
seq_printf(m, "dir-data: rdpg=%u\n",
|
||||
atomic_read(&net->n_read_dir));
|
||||
|
||||
seq_printf(m, "dir-edit: cr=%u rm=%u\n",
|
||||
atomic_read(&net->n_dir_cr),
|
||||
atomic_read(&net->n_dir_rm));
|
||||
|
||||
seq_printf(m, "file-rd : n=%u nb=%lu\n",
|
||||
atomic_read(&net->n_fetches),
|
||||
atomic_long_read(&net->n_fetch_bytes));
|
||||
seq_printf(m, "file-wr : n=%u nb=%lu\n",
|
||||
atomic_read(&net->n_stores),
|
||||
atomic_long_read(&net->n_store_bytes));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Open "/proc/fs/afs/stats" to allow reading of the stat counters.
|
||||
*/
|
||||
static int afs_proc_stats_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, afs_proc_stats_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations afs_proc_stats_fops = {
|
||||
.open = afs_proc_stats_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
@ -21,7 +21,7 @@
|
||||
/*
|
||||
* Initialise a filesystem server cursor for iterating over FS servers.
|
||||
*/
|
||||
void afs_init_fs_cursor(struct afs_fs_cursor *fc, struct afs_vnode *vnode)
|
||||
static void afs_init_fs_cursor(struct afs_fs_cursor *fc, struct afs_vnode *vnode)
|
||||
{
|
||||
memset(fc, 0, sizeof(*fc));
|
||||
}
|
||||
|
@ -926,3 +926,12 @@ int afs_extract_data(struct afs_call *call, void *buf, size_t count,
|
||||
afs_set_call_complete(call, ret, remote_abort);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Log protocol error production.
|
||||
*/
|
||||
noinline int afs_protocol_error(struct afs_call *call, int error)
|
||||
{
|
||||
trace_afs_protocol_error(call, error, __builtin_return_address(0));
|
||||
return error;
|
||||
}
|
||||
|
@ -178,18 +178,14 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
|
||||
}
|
||||
}
|
||||
|
||||
if (cb_break != (vnode->cb_break + vnode->cb_interest->server->cb_s_break)) {
|
||||
rcu_read_unlock();
|
||||
if (cb_break != (vnode->cb_break + vnode->cb_interest->server->cb_s_break))
|
||||
goto someone_else_changed_it;
|
||||
}
|
||||
|
||||
/* We need a ref on any permits list we want to copy as we'll have to
|
||||
* drop the lock to do memory allocation.
|
||||
*/
|
||||
if (permits && !refcount_inc_not_zero(&permits->usage)) {
|
||||
rcu_read_unlock();
|
||||
if (permits && !refcount_inc_not_zero(&permits->usage))
|
||||
goto someone_else_changed_it;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
@ -278,6 +274,7 @@ someone_else_changed_it:
|
||||
/* Someone else changed the cache under us - don't recheck at this
|
||||
* time.
|
||||
*/
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -296,8 +293,6 @@ int afs_check_permit(struct afs_vnode *vnode, struct key *key,
|
||||
_enter("{%x:%u},%x",
|
||||
vnode->fid.vid, vnode->fid.vnode, key_serial(key));
|
||||
|
||||
permits = vnode->permit_cache;
|
||||
|
||||
/* check the permits to see if we've got one yet */
|
||||
if (key == vnode->volume->cell->anonymous_key) {
|
||||
_debug("anon");
|
||||
@ -327,7 +322,7 @@ int afs_check_permit(struct afs_vnode *vnode, struct key *key,
|
||||
*/
|
||||
_debug("no valid permit");
|
||||
|
||||
ret = afs_fetch_status(vnode, key);
|
||||
ret = afs_fetch_status(vnode, key, false);
|
||||
if (ret < 0) {
|
||||
*_access = 0;
|
||||
_leave(" = %d", ret);
|
||||
|
@ -59,7 +59,8 @@ struct afs_server *afs_find_server(struct afs_net *net,
|
||||
alist = rcu_dereference(server->addresses);
|
||||
for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) {
|
||||
b = &alist->addrs[i].transport.sin6;
|
||||
diff = (u16)a->sin6_port - (u16)b->sin6_port;
|
||||
diff = ((u16 __force)a->sin6_port -
|
||||
(u16 __force)b->sin6_port);
|
||||
if (diff == 0)
|
||||
diff = memcmp(&a->sin6_addr,
|
||||
&b->sin6_addr,
|
||||
@ -79,10 +80,11 @@ struct afs_server *afs_find_server(struct afs_net *net,
|
||||
alist = rcu_dereference(server->addresses);
|
||||
for (i = 0; i < alist->nr_ipv4; i++) {
|
||||
b = &alist->addrs[i].transport.sin6;
|
||||
diff = (u16)a->sin6_port - (u16)b->sin6_port;
|
||||
diff = ((u16 __force)a->sin6_port -
|
||||
(u16 __force)b->sin6_port);
|
||||
if (diff == 0)
|
||||
diff = ((u32)a->sin6_addr.s6_addr32[3] -
|
||||
(u32)b->sin6_addr.s6_addr32[3]);
|
||||
diff = ((u32 __force)a->sin6_addr.s6_addr32[3] -
|
||||
(u32 __force)b->sin6_addr.s6_addr32[3]);
|
||||
if (diff == 0)
|
||||
goto found;
|
||||
if (diff < 0) {
|
||||
@ -381,7 +383,7 @@ static void afs_server_rcu(struct rcu_head *rcu)
|
||||
{
|
||||
struct afs_server *server = container_of(rcu, struct afs_server, rcu);
|
||||
|
||||
afs_put_addrlist(server->addresses);
|
||||
afs_put_addrlist(rcu_access_pointer(server->addresses));
|
||||
kfree(server);
|
||||
}
|
||||
|
||||
@ -390,7 +392,7 @@ static void afs_server_rcu(struct rcu_head *rcu)
|
||||
*/
|
||||
static void afs_destroy_server(struct afs_net *net, struct afs_server *server)
|
||||
{
|
||||
struct afs_addr_list *alist = server->addresses;
|
||||
struct afs_addr_list *alist = rcu_access_pointer(server->addresses);
|
||||
struct afs_addr_cursor ac = {
|
||||
.alist = alist,
|
||||
.addr = &alist->addrs[0],
|
||||
|
@ -154,7 +154,7 @@ static int afs_show_devname(struct seq_file *m, struct dentry *root)
|
||||
seq_puts(m, "none");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
switch (volume->type) {
|
||||
case AFSVL_RWVOL:
|
||||
break;
|
||||
@ -269,7 +269,7 @@ static int afs_parse_device_name(struct afs_mount_params *params,
|
||||
int cellnamesz;
|
||||
|
||||
_enter(",%s", name);
|
||||
|
||||
|
||||
if (!name) {
|
||||
printk(KERN_ERR "kAFS: no volume name specified\n");
|
||||
return -EINVAL;
|
||||
@ -418,7 +418,10 @@ static int afs_fill_super(struct super_block *sb,
|
||||
if (!sb->s_root)
|
||||
goto error;
|
||||
|
||||
sb->s_d_op = &afs_fs_dentry_operations;
|
||||
if (params->dyn_root)
|
||||
sb->s_d_op = &afs_dynroot_dentry_operations;
|
||||
else
|
||||
sb->s_d_op = &afs_fs_dentry_operations;
|
||||
|
||||
_leave(" = 0");
|
||||
return 0;
|
||||
@ -676,7 +679,7 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf)
|
||||
buf->f_bfree = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
key = afs_request_key(vnode->volume->cell);
|
||||
if (IS_ERR(key))
|
||||
return PTR_ERR(key);
|
||||
|
@ -303,7 +303,7 @@ struct afs_addr_list *afs_vl_get_addrs_u(struct afs_net *net,
|
||||
r->uuid.clock_seq_hi_and_reserved = htonl(u->clock_seq_hi_and_reserved);
|
||||
r->uuid.clock_seq_low = htonl(u->clock_seq_low);
|
||||
for (i = 0; i < 6; i++)
|
||||
r->uuid.node[i] = ntohl(u->node[i]);
|
||||
r->uuid.node[i] = htonl(u->node[i]);
|
||||
|
||||
trace_afs_make_vl_call(call);
|
||||
return (struct afs_addr_list *)afs_make_call(ac, call, GFP_KERNEL, false);
|
||||
@ -450,7 +450,7 @@ again:
|
||||
call->count2 = ntohl(*bp); /* Type or next count */
|
||||
|
||||
if (call->count > YFS_MAXENDPOINTS)
|
||||
return -EBADMSG;
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
|
||||
alist = afs_alloc_addrlist(call->count, FS_SERVICE, AFS_FS_PORT);
|
||||
if (!alist)
|
||||
@ -474,7 +474,7 @@ again:
|
||||
size = sizeof(__be32) * (1 + 4 + 1);
|
||||
break;
|
||||
default:
|
||||
return -EBADMSG;
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
}
|
||||
|
||||
size += sizeof(__be32);
|
||||
@ -487,24 +487,24 @@ again:
|
||||
switch (call->count2) {
|
||||
case YFS_ENDPOINT_IPV4:
|
||||
if (ntohl(bp[0]) != sizeof(__be32) * 2)
|
||||
return -EBADMSG;
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
afs_merge_fs_addr4(alist, bp[1], ntohl(bp[2]));
|
||||
bp += 3;
|
||||
break;
|
||||
case YFS_ENDPOINT_IPV6:
|
||||
if (ntohl(bp[0]) != sizeof(__be32) * 5)
|
||||
return -EBADMSG;
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
afs_merge_fs_addr6(alist, bp + 1, ntohl(bp[5]));
|
||||
bp += 6;
|
||||
break;
|
||||
default:
|
||||
return -EBADMSG;
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
}
|
||||
|
||||
/* Got either the type of the next entry or the count of
|
||||
* volEndpoints if no more fsEndpoints.
|
||||
*/
|
||||
call->count2 = htonl(*bp++);
|
||||
call->count2 = ntohl(*bp++);
|
||||
|
||||
call->offset = 0;
|
||||
call->count--;
|
||||
@ -517,7 +517,7 @@ again:
|
||||
if (!call->count)
|
||||
goto end;
|
||||
if (call->count > YFS_MAXENDPOINTS)
|
||||
return -EBADMSG;
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
|
||||
call->unmarshall = 3;
|
||||
|
||||
@ -531,7 +531,7 @@ again:
|
||||
return ret;
|
||||
|
||||
bp = call->buffer;
|
||||
call->count2 = htonl(*bp++);
|
||||
call->count2 = ntohl(*bp++);
|
||||
call->offset = 0;
|
||||
call->unmarshall = 4;
|
||||
|
||||
@ -545,7 +545,7 @@ again:
|
||||
size = sizeof(__be32) * (1 + 4 + 1);
|
||||
break;
|
||||
default:
|
||||
return -EBADMSG;
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
}
|
||||
|
||||
if (call->count > 1)
|
||||
@ -558,16 +558,16 @@ again:
|
||||
switch (call->count2) {
|
||||
case YFS_ENDPOINT_IPV4:
|
||||
if (ntohl(bp[0]) != sizeof(__be32) * 2)
|
||||
return -EBADMSG;
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
bp += 3;
|
||||
break;
|
||||
case YFS_ENDPOINT_IPV6:
|
||||
if (ntohl(bp[0]) != sizeof(__be32) * 5)
|
||||
return -EBADMSG;
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
bp += 6;
|
||||
break;
|
||||
default:
|
||||
return -EBADMSG;
|
||||
return afs_protocol_error(call, -EBADMSG);
|
||||
}
|
||||
|
||||
/* Got either the type of the next entry or the count of
|
||||
@ -576,7 +576,7 @@ again:
|
||||
call->offset = 0;
|
||||
call->count--;
|
||||
if (call->count > 0) {
|
||||
call->count2 = htonl(*bp++);
|
||||
call->count2 = ntohl(*bp++);
|
||||
goto again;
|
||||
}
|
||||
|
||||
|
@ -42,10 +42,11 @@ static int afs_fill_page(struct afs_vnode *vnode, struct key *key,
|
||||
if (!req)
|
||||
return -ENOMEM;
|
||||
|
||||
atomic_set(&req->usage, 1);
|
||||
refcount_set(&req->usage, 1);
|
||||
req->pos = pos;
|
||||
req->len = len;
|
||||
req->nr_pages = 1;
|
||||
req->pages = req->array;
|
||||
req->pages[0] = page;
|
||||
get_page(page);
|
||||
|
||||
@ -124,7 +125,12 @@ try_again:
|
||||
page->index, priv);
|
||||
goto flush_conflicting_write;
|
||||
}
|
||||
if (to < f || from > t)
|
||||
/* If the file is being filled locally, allow inter-write
|
||||
* spaces to be merged into writes. If it's not, only write
|
||||
* back what the user gives us.
|
||||
*/
|
||||
if (!test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags) &&
|
||||
(to < f || from > t))
|
||||
goto flush_conflicting_write;
|
||||
if (from < f)
|
||||
f = from;
|
||||
@ -355,6 +361,12 @@ found_key:
|
||||
}
|
||||
|
||||
switch (ret) {
|
||||
case 0:
|
||||
afs_stat_v(vnode, n_stores);
|
||||
atomic_long_add((last * PAGE_SIZE + to) -
|
||||
(first * PAGE_SIZE + offset),
|
||||
&afs_v2net(vnode)->n_store_bytes);
|
||||
break;
|
||||
case -EACCES:
|
||||
case -EPERM:
|
||||
case -ENOKEY:
|
||||
@ -412,7 +424,8 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
|
||||
trace_afs_page_dirty(vnode, tracepoint_string("WARN"),
|
||||
primary_page->index, priv);
|
||||
|
||||
if (start >= final_page || to < PAGE_SIZE)
|
||||
if (start >= final_page ||
|
||||
(to < PAGE_SIZE && !test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags)))
|
||||
goto no_more;
|
||||
|
||||
start++;
|
||||
@ -433,9 +446,10 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
|
||||
}
|
||||
|
||||
for (loop = 0; loop < n; loop++) {
|
||||
if (to != PAGE_SIZE)
|
||||
break;
|
||||
page = pages[loop];
|
||||
if (to != PAGE_SIZE &&
|
||||
!test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags))
|
||||
break;
|
||||
if (page->index > final_page)
|
||||
break;
|
||||
if (!trylock_page(page))
|
||||
@ -448,7 +462,8 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
|
||||
priv = page_private(page);
|
||||
f = priv & AFS_PRIV_MAX;
|
||||
t = priv >> AFS_PRIV_SHIFT;
|
||||
if (f != 0) {
|
||||
if (f != 0 &&
|
||||
!test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags)) {
|
||||
unlock_page(page);
|
||||
break;
|
||||
}
|
||||
@ -734,20 +749,6 @@ int afs_fsync(struct file *file, loff_t start, loff_t end, int datasync)
|
||||
return file_write_and_wait_range(file, start, end);
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush out all outstanding writes on a file opened for writing when it is
|
||||
* closed.
|
||||
*/
|
||||
int afs_flush(struct file *file, fl_owner_t id)
|
||||
{
|
||||
_enter("");
|
||||
|
||||
if ((file->f_mode & FMODE_WRITE) == 0)
|
||||
return 0;
|
||||
|
||||
return vfs_fsync(file, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* notification that a previously read-only page is about to become writable
|
||||
* - if it returns an error, the caller will deliver a bus error signal
|
||||
|
103
fs/afs/xdr_fs.h
Normal file
103
fs/afs/xdr_fs.h
Normal file
@ -0,0 +1,103 @@
|
||||
/* AFS fileserver XDR types
|
||||
*
|
||||
* Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
|
||||
* Written by David Howells (dhowells@redhat.com)
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public Licence
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the Licence, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef XDR_FS_H
|
||||
#define XDR_FS_H
|
||||
|
||||
struct afs_xdr_AFSFetchStatus {
|
||||
__be32 if_version;
|
||||
#define AFS_FSTATUS_VERSION 1
|
||||
__be32 type;
|
||||
__be32 nlink;
|
||||
__be32 size_lo;
|
||||
__be32 data_version_lo;
|
||||
__be32 author;
|
||||
__be32 owner;
|
||||
__be32 caller_access;
|
||||
__be32 anon_access;
|
||||
__be32 mode;
|
||||
__be32 parent_vnode;
|
||||
__be32 parent_unique;
|
||||
__be32 seg_size;
|
||||
__be32 mtime_client;
|
||||
__be32 mtime_server;
|
||||
__be32 group;
|
||||
__be32 sync_counter;
|
||||
__be32 data_version_hi;
|
||||
__be32 lock_count;
|
||||
__be32 size_hi;
|
||||
__be32 abort_code;
|
||||
} __packed;
|
||||
|
||||
#define AFS_DIR_HASHTBL_SIZE 128
|
||||
#define AFS_DIR_DIRENT_SIZE 32
|
||||
#define AFS_DIR_SLOTS_PER_BLOCK 64
|
||||
#define AFS_DIR_BLOCK_SIZE 2048
|
||||
#define AFS_DIR_BLOCKS_PER_PAGE (PAGE_SIZE / AFS_DIR_BLOCK_SIZE)
|
||||
#define AFS_DIR_MAX_SLOTS 65536
|
||||
#define AFS_DIR_BLOCKS_WITH_CTR 128
|
||||
#define AFS_DIR_MAX_BLOCKS 1023
|
||||
#define AFS_DIR_RESV_BLOCKS 1
|
||||
#define AFS_DIR_RESV_BLOCKS0 13
|
||||
|
||||
/*
|
||||
* Directory entry structure.
|
||||
*/
|
||||
union afs_xdr_dirent {
|
||||
struct {
|
||||
u8 valid;
|
||||
u8 unused[1];
|
||||
__be16 hash_next;
|
||||
__be32 vnode;
|
||||
__be32 unique;
|
||||
u8 name[16];
|
||||
u8 overflow[4]; /* if any char of the name (inc
|
||||
* NUL) reaches here, consume
|
||||
* the next dirent too */
|
||||
} u;
|
||||
u8 extended_name[32];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Directory block header (one at the beginning of every 2048-byte block).
|
||||
*/
|
||||
struct afs_xdr_dir_hdr {
|
||||
__be16 npages;
|
||||
__be16 magic;
|
||||
#define AFS_DIR_MAGIC htons(1234)
|
||||
u8 reserved;
|
||||
u8 bitmap[8];
|
||||
u8 pad[19];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Directory block layout
|
||||
*/
|
||||
union afs_xdr_dir_block {
|
||||
struct afs_xdr_dir_hdr hdr;
|
||||
|
||||
struct {
|
||||
struct afs_xdr_dir_hdr hdr;
|
||||
u8 alloc_ctrs[AFS_DIR_MAX_BLOCKS];
|
||||
__be16 hashtable[AFS_DIR_HASHTBL_SIZE];
|
||||
} meta;
|
||||
|
||||
union afs_xdr_dirent dirents[AFS_DIR_SLOTS_PER_BLOCK];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Directory layout on a linux VM page.
|
||||
*/
|
||||
struct afs_xdr_dir_page {
|
||||
union afs_xdr_dir_block blocks[AFS_DIR_BLOCKS_PER_PAGE];
|
||||
};
|
||||
|
||||
#endif /* XDR_FS_H */
|
@ -1667,7 +1667,7 @@ typedef int (*filldir_t)(struct dir_context *, const char *, int, loff_t, u64,
|
||||
unsigned);
|
||||
|
||||
struct dir_context {
|
||||
const filldir_t actor;
|
||||
filldir_t actor;
|
||||
loff_t pos;
|
||||
};
|
||||
|
||||
|
@ -49,6 +49,7 @@ enum afs_fs_operation {
|
||||
afs_FS_ExtendLock = 157, /* AFS Extend a file lock */
|
||||
afs_FS_ReleaseLock = 158, /* AFS Release a file lock */
|
||||
afs_FS_Lookup = 161, /* AFS lookup file in directory */
|
||||
afs_FS_InlineBulkStatus = 65536, /* AFS Fetch multiple file statuses with errors */
|
||||
afs_FS_FetchData64 = 65537, /* AFS Fetch file data */
|
||||
afs_FS_StoreData64 = 65538, /* AFS Store file data */
|
||||
afs_FS_GiveUpAllCallBacks = 65539, /* AFS Give up all our callbacks on a server */
|
||||
@ -62,6 +63,27 @@ enum afs_vl_operation {
|
||||
afs_VL_GetCapabilities = 65537, /* AFS Get VL server capabilities */
|
||||
};
|
||||
|
||||
enum afs_edit_dir_op {
|
||||
afs_edit_dir_create,
|
||||
afs_edit_dir_create_error,
|
||||
afs_edit_dir_create_inval,
|
||||
afs_edit_dir_create_nospc,
|
||||
afs_edit_dir_delete,
|
||||
afs_edit_dir_delete_error,
|
||||
afs_edit_dir_delete_inval,
|
||||
afs_edit_dir_delete_noent,
|
||||
};
|
||||
|
||||
enum afs_edit_dir_reason {
|
||||
afs_edit_dir_for_create,
|
||||
afs_edit_dir_for_link,
|
||||
afs_edit_dir_for_mkdir,
|
||||
afs_edit_dir_for_rename,
|
||||
afs_edit_dir_for_rmdir,
|
||||
afs_edit_dir_for_symlink,
|
||||
afs_edit_dir_for_unlink,
|
||||
};
|
||||
|
||||
#endif /* end __AFS_DECLARE_TRACE_ENUMS_ONCE_ONLY */
|
||||
|
||||
/*
|
||||
@ -93,6 +115,7 @@ enum afs_vl_operation {
|
||||
EM(afs_FS_ExtendLock, "FS.ExtendLock") \
|
||||
EM(afs_FS_ReleaseLock, "FS.ReleaseLock") \
|
||||
EM(afs_FS_Lookup, "FS.Lookup") \
|
||||
EM(afs_FS_InlineBulkStatus, "FS.InlineBulkStatus") \
|
||||
EM(afs_FS_FetchData64, "FS.FetchData64") \
|
||||
EM(afs_FS_StoreData64, "FS.StoreData64") \
|
||||
EM(afs_FS_GiveUpAllCallBacks, "FS.GiveUpAllCallBacks") \
|
||||
@ -104,6 +127,25 @@ enum afs_vl_operation {
|
||||
EM(afs_YFSVL_GetEndpoints, "YFSVL.GetEndpoints") \
|
||||
E_(afs_VL_GetCapabilities, "VL.GetCapabilities")
|
||||
|
||||
#define afs_edit_dir_ops \
|
||||
EM(afs_edit_dir_create, "create") \
|
||||
EM(afs_edit_dir_create_error, "c_fail") \
|
||||
EM(afs_edit_dir_create_inval, "c_invl") \
|
||||
EM(afs_edit_dir_create_nospc, "c_nspc") \
|
||||
EM(afs_edit_dir_delete, "delete") \
|
||||
EM(afs_edit_dir_delete_error, "d_err ") \
|
||||
EM(afs_edit_dir_delete_inval, "d_invl") \
|
||||
E_(afs_edit_dir_delete_noent, "d_nent")
|
||||
|
||||
#define afs_edit_dir_reasons \
|
||||
EM(afs_edit_dir_for_create, "Create") \
|
||||
EM(afs_edit_dir_for_link, "Link ") \
|
||||
EM(afs_edit_dir_for_mkdir, "MkDir ") \
|
||||
EM(afs_edit_dir_for_rename, "Rename") \
|
||||
EM(afs_edit_dir_for_rmdir, "RmDir ") \
|
||||
EM(afs_edit_dir_for_symlink, "Symlnk") \
|
||||
E_(afs_edit_dir_for_unlink, "Unlink")
|
||||
|
||||
|
||||
/*
|
||||
* Export enum symbols via userspace.
|
||||
@ -116,6 +158,8 @@ enum afs_vl_operation {
|
||||
afs_call_traces;
|
||||
afs_fs_operations;
|
||||
afs_vl_operations;
|
||||
afs_edit_dir_ops;
|
||||
afs_edit_dir_reasons;
|
||||
|
||||
/*
|
||||
* Now redefine the EM() and E_() macros to map the enums to the strings that
|
||||
@ -462,6 +506,75 @@ TRACE_EVENT(afs_call_state,
|
||||
__entry->ret, __entry->abort)
|
||||
);
|
||||
|
||||
TRACE_EVENT(afs_edit_dir,
|
||||
TP_PROTO(struct afs_vnode *dvnode,
|
||||
enum afs_edit_dir_reason why,
|
||||
enum afs_edit_dir_op op,
|
||||
unsigned int block,
|
||||
unsigned int slot,
|
||||
unsigned int f_vnode,
|
||||
unsigned int f_unique,
|
||||
const char *name),
|
||||
|
||||
TP_ARGS(dvnode, why, op, block, slot, f_vnode, f_unique, name),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, vnode )
|
||||
__field(unsigned int, unique )
|
||||
__field(enum afs_edit_dir_reason, why )
|
||||
__field(enum afs_edit_dir_op, op )
|
||||
__field(unsigned int, block )
|
||||
__field(unsigned short, slot )
|
||||
__field(unsigned int, f_vnode )
|
||||
__field(unsigned int, f_unique )
|
||||
__array(char, name, 18 )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
int __len = strlen(name);
|
||||
__len = min(__len, 17);
|
||||
__entry->vnode = dvnode->fid.vnode;
|
||||
__entry->unique = dvnode->fid.unique;
|
||||
__entry->why = why;
|
||||
__entry->op = op;
|
||||
__entry->block = block;
|
||||
__entry->slot = slot;
|
||||
__entry->f_vnode = f_vnode;
|
||||
__entry->f_unique = f_unique;
|
||||
memcpy(__entry->name, name, __len);
|
||||
__entry->name[__len] = 0;
|
||||
),
|
||||
|
||||
TP_printk("d=%x:%x %s %s %u[%u] f=%x:%x %s",
|
||||
__entry->vnode, __entry->unique,
|
||||
__print_symbolic(__entry->why, afs_edit_dir_reasons),
|
||||
__print_symbolic(__entry->op, afs_edit_dir_ops),
|
||||
__entry->block, __entry->slot,
|
||||
__entry->f_vnode, __entry->f_unique,
|
||||
__entry->name)
|
||||
);
|
||||
|
||||
TRACE_EVENT(afs_protocol_error,
|
||||
TP_PROTO(struct afs_call *call, int error, const void *where),
|
||||
|
||||
TP_ARGS(call, error, where),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(unsigned int, call )
|
||||
__field(int, error )
|
||||
__field(const void *, where )
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->call = call ? call->debug_id : 0;
|
||||
__entry->error = error;
|
||||
__entry->where = where;
|
||||
),
|
||||
|
||||
TP_printk("c=%08x r=%d sp=%pSR",
|
||||
__entry->call, __entry->error, __entry->where)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_AFS_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
|
Loading…
Reference in New Issue
Block a user