Commit 19e8a2f8 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 5d136594 5a813276
...@@ -11,7 +11,7 @@ Contents: ...@@ -11,7 +11,7 @@ Contents:
- Proc filesystem. - Proc filesystem.
- The cell database. - The cell database.
- Security. - Security.
- Examples. - The @sys substitution.
======== ========
...@@ -230,3 +230,29 @@ If a file is opened with a particular key and then the file descriptor is ...@@ -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 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 socket), then the operations on the file will be made with key that was used to
open the file. 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 := \ ...@@ -12,6 +12,8 @@ kafs-objs := \
cell.o \ cell.o \
cmservice.o \ cmservice.o \
dir.o \ dir.o \
dir_edit.o \
dynroot.o \
file.o \ file.o \
flock.o \ flock.o \
fsclient.o \ fsclient.o \
......
...@@ -243,9 +243,9 @@ void afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port) ...@@ -243,9 +243,9 @@ void afs_merge_fs_addr4(struct afs_addr_list *alist, __be32 xdr, u16 port)
xport == a->sin6_port) xport == a->sin6_port)
return; return;
if (xdr == a->sin6_addr.s6_addr32[3] && if (xdr == a->sin6_addr.s6_addr32[3] &&
xport < a->sin6_port) (u16 __force)xport < (u16 __force)a->sin6_port)
break; break;
if (xdr < a->sin6_addr.s6_addr32[3]) if ((u32 __force)xdr < (u32 __force)a->sin6_addr.s6_addr32[3])
break; break;
} }
...@@ -280,7 +280,7 @@ void afs_merge_fs_addr6(struct afs_addr_list *alist, __be32 *xdr, u16 port) ...@@ -280,7 +280,7 @@ void afs_merge_fs_addr6(struct afs_addr_list *alist, __be32 *xdr, u16 port)
xport == a->sin6_port) xport == a->sin6_port)
return; return;
if (diff == 0 && if (diff == 0 &&
xport < a->sin6_port) (u16 __force)xport < (u16 __force)a->sin6_port)
break; break;
if (diff < 0) if (diff < 0)
break; break;
......
...@@ -67,10 +67,14 @@ typedef enum { ...@@ -67,10 +67,14 @@ typedef enum {
} afs_callback_type_t; } afs_callback_type_t;
struct afs_callback { struct afs_callback {
struct afs_fid fid; /* file identifier */ unsigned version; /* Callback version */
unsigned version; /* callback version */ unsigned expiry; /* Time at which expires */
unsigned expiry; /* time at which expires */ afs_callback_type_t type; /* Type of callback */
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 */ #define AFSCBMAX 50 /* maximum callbacks transferred per bulk op */
...@@ -123,21 +127,20 @@ typedef u32 afs_access_t; ...@@ -123,21 +127,20 @@ typedef u32 afs_access_t;
* AFS file status information * AFS file status information
*/ */
struct afs_file_status { struct afs_file_status {
unsigned if_version; /* interface version */ u64 size; /* file size */
#define AFS_FSTATUS_VERSION 1 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 */ afs_file_type_t type; /* file type */
unsigned nlink; /* link count */ unsigned nlink; /* link count */
u64 size; /* file size */
afs_dataversion_t data_version; /* current data version */
u32 author; /* author ID */ u32 author; /* author ID */
kuid_t owner; /* owner ID */ u32 owner; /* owner ID */
kgid_t group; /* group ID */ u32 group; /* group ID */
afs_access_t caller_access; /* access rights for authenticated caller */ afs_access_t caller_access; /* access rights for authenticated caller */
afs_access_t anon_access; /* access rights for unauthenticated caller */ afs_access_t anon_access; /* access rights for unauthenticated caller */
umode_t mode; /* UNIX mode */ 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 */ s32 lock_count; /* file lock count (0=UNLK -1=WRLCK +ve=#RDLCK */
}; };
......
...@@ -31,10 +31,12 @@ enum AFS_FS_Operations { ...@@ -31,10 +31,12 @@ enum AFS_FS_Operations {
FSGETVOLUMEINFO = 148, /* AFS Get information about a volume */ FSGETVOLUMEINFO = 148, /* AFS Get information about a volume */
FSGETVOLUMESTATUS = 149, /* AFS Get volume status information */ FSGETVOLUMESTATUS = 149, /* AFS Get volume status information */
FSGETROOTVOLUME = 151, /* AFS Get root volume name */ FSGETROOTVOLUME = 151, /* AFS Get root volume name */
FSBULKSTATUS = 155, /* AFS Fetch multiple file statuses */
FSSETLOCK = 156, /* AFS Request a file lock */ FSSETLOCK = 156, /* AFS Request a file lock */
FSEXTENDLOCK = 157, /* AFS Extend a file lock */ FSEXTENDLOCK = 157, /* AFS Extend a file lock */
FSRELEASELOCK = 158, /* AFS Release a file lock */ FSRELEASELOCK = 158, /* AFS Release a file lock */
FSLOOKUP = 161, /* AFS lookup file in directory */ FSLOOKUP = 161, /* AFS lookup file in directory */
FSINLINEBULKSTATUS = 65536, /* AFS Fetch multiple file statuses with inline errors */
FSFETCHDATA64 = 65537, /* AFS Fetch file data */ FSFETCHDATA64 = 65537, /* AFS Fetch file data */
FSSTOREDATA64 = 65538, /* AFS Store file data */ FSSTOREDATA64 = 65538, /* AFS Store file data */
FSGIVEUPALLCALLBACKS = 65539, /* AFS Give up all outstanding callbacks on a server */ FSGIVEUPALLCALLBACKS = 65539, /* AFS Give up all outstanding callbacks on a server */
......
...@@ -96,26 +96,6 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode, ...@@ -96,26 +96,6 @@ int afs_register_server_cb_interest(struct afs_vnode *vnode,
return 0; 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. * Remove an interest on a server.
*/ */
...@@ -150,6 +130,7 @@ void afs_break_callback(struct afs_vnode *vnode) ...@@ -150,6 +130,7 @@ void afs_break_callback(struct afs_vnode *vnode)
write_seqlock(&vnode->cb_lock); write_seqlock(&vnode->cb_lock);
clear_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { if (test_and_clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
vnode->cb_break++; vnode->cb_break++;
afs_clear_permits(vnode); afs_clear_permits(vnode);
...@@ -207,7 +188,7 @@ static void afs_break_one_callback(struct afs_server *server, ...@@ -207,7 +188,7 @@ static void afs_break_one_callback(struct afs_server *server,
* allow the fileserver to break callback promises * allow the fileserver to break callback promises
*/ */
void afs_break_callbacks(struct afs_server *server, size_t count, 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); _enter("%p,%zu,", server, count);
...@@ -219,9 +200,9 @@ void afs_break_callbacks(struct afs_server *server, size_t count, ...@@ -219,9 +200,9 @@ void afs_break_callbacks(struct afs_server *server, size_t count,
callbacks->fid.vid, callbacks->fid.vid,
callbacks->fid.vnode, callbacks->fid.vnode,
callbacks->fid.unique, callbacks->fid.unique,
callbacks->version, callbacks->cb.version,
callbacks->expiry, callbacks->cb.expiry,
callbacks->type callbacks->cb.type
); );
afs_break_one_callback(server, &callbacks->fid); afs_break_one_callback(server, &callbacks->fid);
} }
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
#include <keys/rxrpc-type.h> #include <keys/rxrpc-type.h>
#include "internal.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 *); static void afs_manage_cell(struct work_struct *);
...@@ -75,7 +75,7 @@ struct afs_cell *afs_lookup_cell_rcu(struct afs_net *net, ...@@ -75,7 +75,7 @@ struct afs_cell *afs_lookup_cell_rcu(struct afs_net *net,
cell = rcu_dereference_raw(net->ws_cell); cell = rcu_dereference_raw(net->ws_cell);
if (cell) { if (cell) {
afs_get_cell(cell); afs_get_cell(cell);
continue; break;
} }
ret = -EDESTADDRREQ; ret = -EDESTADDRREQ;
continue; continue;
...@@ -130,6 +130,8 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net, ...@@ -130,6 +130,8 @@ static struct afs_cell *afs_alloc_cell(struct afs_net *net,
_leave(" = -ENAMETOOLONG"); _leave(" = -ENAMETOOLONG");
return ERR_PTR(-ENAMETOOLONG); return ERR_PTR(-ENAMETOOLONG);
} }
if (namelen == 5 && memcmp(name, "@cell", 5) == 0)
return ERR_PTR(-EINVAL);
_enter("%*.*s,%s", namelen, namelen, name, vllist); _enter("%*.*s,%s", namelen, namelen, name, vllist);
...@@ -334,8 +336,8 @@ int afs_cell_init(struct afs_net *net, const char *rootcell) ...@@ -334,8 +336,8 @@ int afs_cell_init(struct afs_net *net, const char *rootcell)
return PTR_ERR(new_root); return PTR_ERR(new_root);
} }
set_bit(AFS_CELL_FL_NO_GC, &new_root->flags); if (!test_and_set_bit(AFS_CELL_FL_NO_GC, &new_root->flags))
afs_get_cell(new_root); afs_get_cell(new_root);
/* install the new cell */ /* install the new cell */
write_seqlock(&net->cells_lock); write_seqlock(&net->cells_lock);
...@@ -411,7 +413,7 @@ static void afs_cell_destroy(struct rcu_head *rcu) ...@@ -411,7 +413,7 @@ static void afs_cell_destroy(struct rcu_head *rcu)
ASSERTCMP(atomic_read(&cell->usage), ==, 0); 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); key_put(cell->anonymous_key);
kfree(cell); kfree(cell);
......
...@@ -178,8 +178,8 @@ static void SRXAFSCB_CallBack(struct work_struct *work) ...@@ -178,8 +178,8 @@ static void SRXAFSCB_CallBack(struct work_struct *work)
*/ */
static int afs_deliver_cb_callback(struct afs_call *call) static int afs_deliver_cb_callback(struct afs_call *call)
{ {
struct afs_callback_break *cb;
struct sockaddr_rxrpc srx; struct sockaddr_rxrpc srx;
struct afs_callback *cb;
struct afs_server *server; struct afs_server *server;
__be32 *bp; __be32 *bp;
int ret, loop; int ret, loop;
...@@ -201,7 +201,7 @@ static int afs_deliver_cb_callback(struct afs_call *call) ...@@ -201,7 +201,7 @@ static int afs_deliver_cb_callback(struct afs_call *call)
call->count = ntohl(call->tmp); call->count = ntohl(call->tmp);
_debug("FID count: %u", call->count); _debug("FID count: %u", call->count);
if (call->count > AFSCBMAX) if (call->count > AFSCBMAX)
return -EBADMSG; return afs_protocol_error(call, -EBADMSG);
call->buffer = kmalloc(call->count * 3 * 4, GFP_KERNEL); call->buffer = kmalloc(call->count * 3 * 4, GFP_KERNEL);
if (!call->buffer) if (!call->buffer)
...@@ -218,7 +218,7 @@ static int afs_deliver_cb_callback(struct afs_call *call) ...@@ -218,7 +218,7 @@ static int afs_deliver_cb_callback(struct afs_call *call)
_debug("unmarshall FID array"); _debug("unmarshall FID array");
call->request = kcalloc(call->count, call->request = kcalloc(call->count,
sizeof(struct afs_callback), sizeof(struct afs_callback_break),
GFP_KERNEL); GFP_KERNEL);
if (!call->request) if (!call->request)
return -ENOMEM; return -ENOMEM;
...@@ -229,7 +229,7 @@ static int afs_deliver_cb_callback(struct afs_call *call) ...@@ -229,7 +229,7 @@ static int afs_deliver_cb_callback(struct afs_call *call)
cb->fid.vid = ntohl(*bp++); cb->fid.vid = ntohl(*bp++);
cb->fid.vnode = ntohl(*bp++); cb->fid.vnode = ntohl(*bp++);
cb->fid.unique = ntohl(*bp++); cb->fid.unique = ntohl(*bp++);
cb->type = AFSCM_CB_UNTYPED; cb->cb.type = AFSCM_CB_UNTYPED;
} }
call->offset = 0; call->offset = 0;
...@@ -245,7 +245,7 @@ static int afs_deliver_cb_callback(struct afs_call *call) ...@@ -245,7 +245,7 @@ static int afs_deliver_cb_callback(struct afs_call *call)
call->count2 = ntohl(call->tmp); call->count2 = ntohl(call->tmp);
_debug("CB count: %u", call->count2); _debug("CB count: %u", call->count2);
if (call->count2 != call->count && call->count2 != 0) if (call->count2 != call->count && call->count2 != 0)
return -EBADMSG; return afs_protocol_error(call, -EBADMSG);
call->offset = 0; call->offset = 0;
call->unmarshall++; call->unmarshall++;
...@@ -260,9 +260,9 @@ static int afs_deliver_cb_callback(struct afs_call *call) ...@@ -260,9 +260,9 @@ static int afs_deliver_cb_callback(struct afs_call *call)
cb = call->request; cb = call->request;
bp = call->buffer; bp = call->buffer;
for (loop = call->count2; loop > 0; loop--, cb++) { for (loop = call->count2; loop > 0; loop--, cb++) {
cb->version = ntohl(*bp++); cb->cb.version = ntohl(*bp++);
cb->expiry = ntohl(*bp++); cb->cb.expiry = ntohl(*bp++);
cb->type = ntohl(*bp++); cb->cb.type = ntohl(*bp++);
} }
call->offset = 0; call->offset = 0;
...@@ -500,9 +500,9 @@ static int afs_deliver_cb_probe_uuid(struct afs_call *call) ...@@ -500,9 +500,9 @@ static int afs_deliver_cb_probe_uuid(struct afs_call *call)
b = call->buffer; b = call->buffer;
r = call->request; r = call->request;
r->time_low = ntohl(b[0]); r->time_low = b[0];
r->time_mid = ntohl(b[1]); r->time_mid = htons(ntohl(b[1]));
r->time_hi_and_version = ntohl(b[2]); r->time_hi_and_version = htons(ntohl(b[2]));
r->clock_seq_hi_and_reserved = ntohl(b[3]); r->clock_seq_hi_and_reserved = ntohl(b[3]);
r->clock_seq_low = ntohl(b[4]); r->clock_seq_low = ntohl(b[4]);
......
/* dir.c: AFS filesystem directory handling /* dir.c: AFS filesystem directory handling
* *
* Copyright (C) 2002 Red Hat, Inc. All Rights Reserved. * Copyright (C) 2002, 2018 Red Hat, Inc. All Rights Reserved.
* Written by David Howells (dhowells@redhat.com) * Written by David Howells (dhowells@redhat.com)
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
...@@ -10,27 +10,26 @@ ...@@ -10,27 +10,26 @@
*/ */
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/namei.h> #include <linux/namei.h>
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/swap.h>
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/dns_resolver.h> #include <linux/task_io_accounting_ops.h>
#include "internal.h" #include "internal.h"
#include "xdr_fs.h"
static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags); unsigned int flags);
static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags);
static int afs_dir_open(struct inode *inode, struct file *file); static int afs_dir_open(struct inode *inode, struct file *file);
static int afs_readdir(struct file *file, struct dir_context *ctx); static int afs_readdir(struct file *file, struct dir_context *ctx);
static int afs_d_revalidate(struct dentry *dentry, unsigned int flags); static int afs_d_revalidate(struct dentry *dentry, unsigned int flags);
static int afs_d_delete(const struct dentry *dentry); static int afs_d_delete(const struct dentry *dentry);
static void afs_d_release(struct dentry *dentry); static int afs_lookup_one_filldir(struct dir_context *ctx, const char *name, int nlen,
static int afs_lookup_filldir(struct dir_context *ctx, const char *name, int nlen,
loff_t fpos, u64 ino, unsigned dtype); loff_t fpos, u64 ino, unsigned dtype);
static int afs_lookup_filldir(struct dir_context *ctx, const char *name, int nlen,
loff_t fpos, u64 ino, unsigned dtype);
static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
bool excl); bool excl);
static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode); static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode);
...@@ -43,6 +42,14 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, ...@@ -43,6 +42,14 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry, struct inode *new_dir, struct dentry *new_dentry,
unsigned int flags); unsigned int flags);
static int afs_dir_releasepage(struct page *page, gfp_t gfp_flags);
static void afs_dir_invalidatepage(struct page *page, unsigned int offset,
unsigned int length);
static int afs_dir_set_page_dirty(struct page *page)
{
BUG(); /* This should never happen. */
}
const struct file_operations afs_dir_file_operations = { const struct file_operations afs_dir_file_operations = {
.open = afs_dir_open, .open = afs_dir_open,
...@@ -67,15 +74,10 @@ const struct inode_operations afs_dir_inode_operations = { ...@@ -67,15 +74,10 @@ const struct inode_operations afs_dir_inode_operations = {
.listxattr = afs_listxattr, .listxattr = afs_listxattr,
}; };
const struct file_operations afs_dynroot_file_operations = { const struct address_space_operations afs_dir_aops = {
.open = dcache_dir_open, .set_page_dirty = afs_dir_set_page_dirty,
.release = dcache_dir_close, .releasepage = afs_dir_releasepage,
.iterate_shared = dcache_readdir, .invalidatepage = afs_dir_invalidatepage,
.llseek = dcache_dir_lseek,
};
const struct inode_operations afs_dynroot_inode_operations = {
.lookup = afs_dynroot_lookup,
}; };
const struct dentry_operations afs_fs_dentry_operations = { const struct dentry_operations afs_fs_dentry_operations = {
...@@ -85,91 +87,38 @@ const struct dentry_operations afs_fs_dentry_operations = { ...@@ -85,91 +87,38 @@ const struct dentry_operations afs_fs_dentry_operations = {
.d_automount = afs_d_automount, .d_automount = afs_d_automount,
}; };
#define AFS_DIR_HASHTBL_SIZE 128 struct afs_lookup_one_cookie {
#define AFS_DIR_DIRENT_SIZE 32 struct dir_context ctx;
#define AFS_DIRENT_PER_BLOCK 64 struct qstr name;
bool found;
union afs_dirent { struct afs_fid fid;
struct {
uint8_t valid;
uint8_t unused[1];
__be16 hash_next;
__be32 vnode;
__be32 unique;
uint8_t name[16];
uint8_t overflow[4]; /* if any char of the name (inc
* NUL) reaches here, consume
* the next dirent too */
} u;
uint8_t extended_name[32];
};
/* AFS directory page header (one at the beginning of every 2048-byte chunk) */
struct afs_dir_pagehdr {
__be16 npages;
__be16 magic;
#define AFS_DIR_MAGIC htons(1234)
uint8_t nentries;
uint8_t bitmap[8];
uint8_t pad[19];
};
/* directory block layout */
union afs_dir_block {
struct afs_dir_pagehdr pagehdr;
struct {
struct afs_dir_pagehdr pagehdr;
uint8_t alloc_ctrs[128];
/* dir hash table */
uint16_t hashtable[AFS_DIR_HASHTBL_SIZE];
} hdr;
union afs_dirent dirents[AFS_DIRENT_PER_BLOCK];
};
/* layout on a linux VM page */
struct afs_dir_page {
union afs_dir_block blocks[PAGE_SIZE / sizeof(union afs_dir_block)];
}; };
struct afs_lookup_cookie { struct afs_lookup_cookie {
struct dir_context ctx; struct dir_context ctx;
struct afs_fid fid; struct qstr name;
struct qstr name; bool found;
int found; bool one_only;
unsigned short nr_fids;
struct afs_file_status *statuses;
struct afs_callback *callbacks;
struct afs_fid fids[50];
}; };
/* /*
* check that a directory page is valid * check that a directory page is valid
*/ */
bool afs_dir_check_page(struct inode *dir, struct page *page) static bool afs_dir_check_page(struct afs_vnode *dvnode, struct page *page,
loff_t i_size)
{ {
struct afs_dir_page *dbuf; struct afs_xdr_dir_page *dbuf;
struct afs_vnode *vnode = AFS_FS_I(dir); loff_t latter, off;
loff_t latter, i_size, off;
int tmp, qty; int tmp, qty;
#if 0
/* check the page count */
qty = desc.size / sizeof(dbuf->blocks[0]);
if (qty == 0)
goto error;
if (page->index == 0 && qty != ntohs(dbuf->blocks[0].pagehdr.npages)) {
printk("kAFS: %s(%lu): wrong number of dir blocks %d!=%hu\n",
__func__, dir->i_ino, qty,
ntohs(dbuf->blocks[0].pagehdr.npages));
goto error;
}
#endif
/* Determine how many magic numbers there should be in this page, but /* Determine how many magic numbers there should be in this page, but
* we must take care because the directory may change size under us. * we must take care because the directory may change size under us.
*/ */
off = page_offset(page); off = page_offset(page);
i_size = i_size_read(dir);
if (i_size <= off) if (i_size <= off)
goto checked; goto checked;
...@@ -178,112 +127,225 @@ bool afs_dir_check_page(struct inode *dir, struct page *page) ...@@ -178,112 +127,225 @@ bool afs_dir_check_page(struct inode *dir, struct page *page)
qty = PAGE_SIZE; qty = PAGE_SIZE;
else else
qty = latter; qty = latter;
qty /= sizeof(union afs_dir_block); qty /= sizeof(union afs_xdr_dir_block);
/* check them */ /* check them */
dbuf = page_address(page); dbuf = kmap(page);
for (tmp = 0; tmp < qty; tmp++) { for (tmp = 0; tmp < qty; tmp++) {
if (dbuf->blocks[tmp].pagehdr.magic != AFS_DIR_MAGIC) { if (dbuf->blocks[tmp].hdr.magic != AFS_DIR_MAGIC) {
printk("kAFS: %s(%lx): bad magic %d/%d is %04hx\n", printk("kAFS: %s(%lx): bad magic %d/%d is %04hx\n",
__func__, dir->i_ino, tmp, qty, __func__, dvnode->vfs_inode.i_ino, tmp, qty,
ntohs(dbuf->blocks[tmp].pagehdr.magic)); ntohs(dbuf->blocks[tmp].hdr.magic));
trace_afs_dir_check_failed(vnode, off, i_size); trace_afs_dir_check_failed(dvnode, off, i_size);
kunmap(page);
goto error; goto error;
} }
/* Make sure each block is NUL terminated so we can reasonably
* use string functions on it. The filenames in the page
* *should* be NUL-terminated anyway.
*/
((u8 *)&dbuf->blocks[tmp])[AFS_DIR_BLOCK_SIZE - 1] = 0;
} }
kunmap(page);
checked: checked:
SetPageChecked(page); afs_stat_v(dvnode, n_read_dir);
return true; return true;
error: error:
SetPageError(page);
return false; return false;
} }
/* /*
* discard a page cached in the pagecache * open an AFS directory file
*/ */
static inline void afs_dir_put_page(struct page *page) static int afs_dir_open(struct inode *inode, struct file *file)
{ {
kunmap(page); _enter("{%lu}", inode->i_ino);
unlock_page(page);
put_page(page); BUILD_BUG_ON(sizeof(union afs_xdr_dir_block) != 2048);
BUILD_BUG_ON(sizeof(union afs_xdr_dirent) != 32);
if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(inode)->flags))
return -ENOENT;
return afs_open(inode, file);
} }
/* /*
* get a page into the pagecache * Read the directory into the pagecache in one go, scrubbing the previous
* contents. The list of pages is returned, pinning them so that they don't
* get reclaimed during the iteration.
*/ */
static struct page *afs_dir_get_page(struct inode *dir, unsigned long index, static struct afs_read *afs_read_dir(struct afs_vnode *dvnode, struct key *key)
struct key *key)
{ {
struct page *page; struct afs_read *req;
_enter("{%lu},%lu", dir->i_ino, index); loff_t i_size;
int nr_pages, nr_inline, i, n;
page = read_cache_page(dir->i_mapping, index, afs_page_filler, key); int ret = -ENOMEM;
if (!IS_ERR(page)) {
lock_page(page); retry:
kmap(page); i_size = i_size_read(&dvnode->vfs_inode);
if (unlikely(!PageChecked(page))) { if (i_size < 2048)
if (PageError(page)) return ERR_PTR(-EIO);
goto fail; if (i_size > 2048 * 1024)
} return ERR_PTR(-EFBIG);
_enter("%llu", i_size);
/* Get a request record to hold the page list. We want to hold it
* inline if we can, but we don't want to make an order 1 allocation.
*/
nr_pages = (i_size + PAGE_SIZE - 1) / PAGE_SIZE;
nr_inline = nr_pages;
if (nr_inline > (PAGE_SIZE - sizeof(*req)) / sizeof(struct page *))
nr_inline = 0;
req = kzalloc(sizeof(*req) + sizeof(struct page *) * nr_inline,
GFP_KERNEL);
if (!req)
return ERR_PTR(-ENOMEM);
refcount_set(&req->usage, 1);
req->nr_pages = nr_pages;
req->actual_len = i_size; /* May change */
req->len = nr_pages * PAGE_SIZE; /* We can ask for more than there is */
req->data_version = dvnode->status.data_version; /* May change */
if (nr_inline > 0) {
req->pages = req->array;
} else {
req->pages = kcalloc(nr_pages, sizeof(struct page *),
GFP_KERNEL);
if (!req->pages)
goto error;
} }
return page;
fail: /* Get a list of all the pages that hold or will hold the directory
afs_dir_put_page(page); * content. We need to fill in any gaps that we might find where the
_leave(" = -EIO"); * memory reclaimer has been at work. If there are any gaps, we will
return ERR_PTR(-EIO); * need to reread the entire directory contents.
} */
i = 0;
do {
n = find_get_pages_contig(dvnode->vfs_inode.i_mapping, i,
req->nr_pages - i,
req->pages + i);
_debug("find %u at %u/%u", n, i, req->nr_pages);
if (n == 0) {
gfp_t gfp = dvnode->vfs_inode.i_mapping->gfp_mask;
if (test_and_clear_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_stat_v(dvnode, n_inval);
ret = -ENOMEM;
req->pages[i] = __page_cache_alloc(gfp);
if (!req->pages[i])
goto error;
ret = add_to_page_cache_lru(req->pages[i],
dvnode->vfs_inode.i_mapping,
i, gfp);
if (ret < 0)
goto error;
set_page_private(req->pages[i], 1);
SetPagePrivate(req->pages[i]);
unlock_page(req->pages[i]);
i++;
} else {
i += n;
}
} while (i < req->nr_pages);
/* /* If we're going to reload, we need to lock all the pages to prevent
* open an AFS directory file * races.
*/ */
static int afs_dir_open(struct inode *inode, struct file *file) if (!test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) {
{ ret = -ERESTARTSYS;
_enter("{%lu}", inode->i_ino); for (i = 0; i < req->nr_pages; i++)
if (lock_page_killable(req->pages[i]) < 0)
goto error_unlock;
BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048); if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
BUILD_BUG_ON(sizeof(union afs_dirent) != 32); goto success;
if (test_bit(AFS_VNODE_DELETED, &AFS_FS_I(inode)->flags)) ret = afs_fetch_data(dvnode, key, req);
return -ENOENT; if (ret < 0)
goto error_unlock_all;
return afs_open(inode, file); task_io_account_read(PAGE_SIZE * req->nr_pages);
if (req->len < req->file_size)
goto content_has_grown;
/* Validate the data we just read. */
ret = -EIO;
for (i = 0; i < req->nr_pages; i++)
if (!afs_dir_check_page(dvnode, req->pages[i],
req->actual_len))
goto error_unlock_all;
// TODO: Trim excess pages
set_bit(AFS_VNODE_DIR_VALID, &dvnode->flags);
}
success:
i = req->nr_pages;
while (i > 0)
unlock_page(req->pages[--i]);
return req;
error_unlock_all:
i = req->nr_pages;
error_unlock:
while (i > 0)
unlock_page(req->pages[--i]);
error:
afs_put_read(req);
_leave(" = %d", ret);
return ERR_PTR(ret);
content_has_grown:
i = req->nr_pages;
while (i > 0)
unlock_page(req->pages[--i]);
afs_put_read(req);
goto retry;
} }
/* /*
* deal with one block in an AFS directory * deal with one block in an AFS directory
*/ */
static int afs_dir_iterate_block(struct dir_context *ctx, static int afs_dir_iterate_block(struct dir_context *ctx,
union afs_dir_block *block, union afs_xdr_dir_block *block,
unsigned blkoff) unsigned blkoff)
{ {
union afs_dirent *dire; union afs_xdr_dirent *dire;
unsigned offset, next, curr; unsigned offset, next, curr;
size_t nlen; size_t nlen;
int tmp; int tmp;
_enter("%u,%x,%p,,",(unsigned)ctx->pos,blkoff,block); _enter("%u,%x,%p,,",(unsigned)ctx->pos,blkoff,block);
curr = (ctx->pos - blkoff) / sizeof(union afs_dirent); curr = (ctx->pos - blkoff) / sizeof(union afs_xdr_dirent);
/* walk through the block, an entry at a time */ /* walk through the block, an entry at a time */
for (offset = AFS_DIRENT_PER_BLOCK - block->pagehdr.nentries; for (offset = (blkoff == 0 ? AFS_DIR_RESV_BLOCKS0 : AFS_DIR_RESV_BLOCKS);
offset < AFS_DIRENT_PER_BLOCK; offset < AFS_DIR_SLOTS_PER_BLOCK;
offset = next offset = next
) { ) {
next = offset + 1; next = offset + 1;
/* skip entries marked unused in the bitmap */ /* skip entries marked unused in the bitmap */
if (!(block->pagehdr.bitmap[offset / 8] & if (!(block->hdr.bitmap[offset / 8] &
(1 << (offset % 8)))) { (1 << (offset % 8)))) {
_debug("ENT[%zu.%u]: unused", _debug("ENT[%zu.%u]: unused",
blkoff / sizeof(union afs_dir_block), offset); blkoff / sizeof(union afs_xdr_dir_block), offset);
if (offset >= curr) if (offset >= curr)
ctx->pos = blkoff + ctx->pos = blkoff +
next * sizeof(union afs_dirent); next * sizeof(union afs_xdr_dirent);
continue; continue;
} }
...@@ -291,34 +353,34 @@ static int afs_dir_iterate_block(struct dir_context *ctx, ...@@ -291,34 +353,34 @@ static int afs_dir_iterate_block(struct dir_context *ctx,
dire = &block->dirents[offset]; dire = &block->dirents[offset];
nlen = strnlen(dire->u.name, nlen = strnlen(dire->u.name,
sizeof(*block) - sizeof(*block) -
offset * sizeof(union afs_dirent)); offset * sizeof(union afs_xdr_dirent));
_debug("ENT[%zu.%u]: %s %zu \"%s\"", _debug("ENT[%zu.%u]: %s %zu \"%s\"",
blkoff / sizeof(union afs_dir_block), offset, blkoff / sizeof(union afs_xdr_dir_block), offset,
(offset < curr ? "skip" : "fill"), (offset < curr ? "skip" : "fill"),
nlen, dire->u.name); nlen, dire->u.name);
/* work out where the next possible entry is */ /* work out where the next possible entry is */
for (tmp = nlen; tmp > 15; tmp -= sizeof(union afs_dirent)) { for (tmp = nlen; tmp > 15; tmp -= sizeof(union afs_xdr_dirent)) {
if (next >= AFS_DIRENT_PER_BLOCK) { if (next >= AFS_DIR_SLOTS_PER_BLOCK) {
_debug("ENT[%zu.%u]:" _debug("ENT[%zu.%u]:"
" %u travelled beyond end dir block" " %u travelled beyond end dir block"
" (len %u/%zu)", " (len %u/%zu)",
blkoff / sizeof(union afs_dir_block), blkoff / sizeof(union afs_xdr_dir_block),
offset, next, tmp, nlen); offset, next, tmp, nlen);
return -EIO; return -EIO;
} }
if (!(block->pagehdr.bitmap[next / 8] & if (!(block->hdr.bitmap[next / 8] &
(1 << (next % 8)))) { (1 << (next % 8)))) {
_debug("ENT[%zu.%u]:" _debug("ENT[%zu.%u]:"
" %u unmarked extension (len %u/%zu)", " %u unmarked extension (len %u/%zu)",
blkoff / sizeof(union afs_dir_block), blkoff / sizeof(union afs_xdr_dir_block),
offset, next, tmp, nlen); offset, next, tmp, nlen);
return -EIO; return -EIO;
} }
_debug("ENT[%zu.%u]: ext %u/%zu", _debug("ENT[%zu.%u]: ext %u/%zu",
blkoff / sizeof(union afs_dir_block), blkoff / sizeof(union afs_xdr_dir_block),
next, tmp, nlen); next, tmp, nlen);
next++; next++;
} }
...@@ -330,13 +392,14 @@ static int afs_dir_iterate_block(struct dir_context *ctx, ...@@ -330,13 +392,14 @@ static int afs_dir_iterate_block(struct dir_context *ctx,
/* found the next entry */ /* found the next entry */
if (!dir_emit(ctx, dire->u.name, nlen, if (!dir_emit(ctx, dire->u.name, nlen,
ntohl(dire->u.vnode), ntohl(dire->u.vnode),
ctx->actor == afs_lookup_filldir ? (ctx->actor == afs_lookup_filldir ||
ctx->actor == afs_lookup_one_filldir)?
ntohl(dire->u.unique) : DT_UNKNOWN)) { ntohl(dire->u.unique) : DT_UNKNOWN)) {
_leave(" = 0 [full]"); _leave(" = 0 [full]");
return 0; return 0;
} }
ctx->pos = blkoff + next * sizeof(union afs_dirent); ctx->pos = blkoff + next * sizeof(union afs_xdr_dirent);
} }
_leave(" = 1 [more]"); _leave(" = 1 [more]");
...@@ -349,8 +412,10 @@ static int afs_dir_iterate_block(struct dir_context *ctx, ...@@ -349,8 +412,10 @@ static int afs_dir_iterate_block(struct dir_context *ctx,
static int afs_dir_iterate(struct inode *dir, struct dir_context *ctx, static int afs_dir_iterate(struct inode *dir, struct dir_context *ctx,
struct key *key) struct key *key)
{ {
union afs_dir_block *dblock; struct afs_vnode *dvnode = AFS_FS_I(dir);
struct afs_dir_page *dbuf; struct afs_xdr_dir_page *dbuf;
union afs_xdr_dir_block *dblock;
struct afs_read *req;
struct page *page; struct page *page;
unsigned blkoff, limit; unsigned blkoff, limit;
int ret; int ret;
...@@ -362,45 +427,53 @@ static int afs_dir_iterate(struct inode *dir, struct dir_context *ctx, ...@@ -362,45 +427,53 @@ static int afs_dir_iterate(struct inode *dir, struct dir_context *ctx,
return -ESTALE; return -ESTALE;
} }
req = afs_read_dir(dvnode, key);
if (IS_ERR(req))
return PTR_ERR(req);
/* round the file position up to the next entry boundary */ /* round the file position up to the next entry boundary */
ctx->pos += sizeof(union afs_dirent) - 1; ctx->pos += sizeof(union afs_xdr_dirent) - 1;
ctx->pos &= ~(sizeof(union afs_dirent) - 1); ctx->pos &= ~(sizeof(union afs_xdr_dirent) - 1);
/* walk through the blocks in sequence */ /* walk through the blocks in sequence */
ret = 0; ret = 0;
while (ctx->pos < dir->i_size) { while (ctx->pos < req->actual_len) {
blkoff = ctx->pos & ~(sizeof(union afs_dir_block) - 1); blkoff = ctx->pos & ~(sizeof(union afs_xdr_dir_block) - 1);
/* fetch the appropriate page from the directory */ /* Fetch the appropriate page from the directory and re-add it
page = afs_dir_get_page(dir, blkoff / PAGE_SIZE, key); * to the LRU.
if (IS_ERR(page)) { */
ret = PTR_ERR(page); page = req->pages[blkoff / PAGE_SIZE];
if (!page) {
ret = -EIO;
break; break;
} }
mark_page_accessed(page);
limit = blkoff & ~(PAGE_SIZE - 1); limit = blkoff & ~(PAGE_SIZE - 1);
dbuf = page_address(page); dbuf = kmap(page);
/* deal with the individual blocks stashed on this page */ /* deal with the individual blocks stashed on this page */
do { do {
dblock = &dbuf->blocks[(blkoff % PAGE_SIZE) / dblock = &dbuf->blocks[(blkoff % PAGE_SIZE) /
sizeof(union afs_dir_block)]; sizeof(union afs_xdr_dir_block)];
ret = afs_dir_iterate_block(ctx, dblock, blkoff); ret = afs_dir_iterate_block(ctx, dblock, blkoff);
if (ret != 1) { if (ret != 1) {
afs_dir_put_page(page); kunmap(page);
goto out; goto out;
} }
blkoff += sizeof(union afs_dir_block); blkoff += sizeof(union afs_xdr_dir_block);
} while (ctx->pos < dir->i_size && blkoff < limit); } while (ctx->pos < dir->i_size && blkoff < limit);
afs_dir_put_page(page); kunmap(page);
ret = 0; ret = 0;
} }
out: out:
afs_put_read(req);
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
} }
...@@ -414,23 +487,23 @@ static int afs_readdir(struct file *file, struct dir_context *ctx) ...@@ -414,23 +487,23 @@ static int afs_readdir(struct file *file, struct dir_context *ctx)
} }
/* /*
* search the directory for a name * Search the directory for a single name
* - if afs_dir_iterate_block() spots this function, it'll pass the FID * - if afs_dir_iterate_block() spots this function, it'll pass the FID
* uniquifier through dtype * uniquifier through dtype
*/ */
static int afs_lookup_filldir(struct dir_context *ctx, const char *name, static int afs_lookup_one_filldir(struct dir_context *ctx, const char *name,
int nlen, loff_t fpos, u64 ino, unsigned dtype) int nlen, loff_t fpos, u64 ino, unsigned dtype)
{ {
struct afs_lookup_cookie *cookie = struct afs_lookup_one_cookie *cookie =
container_of(ctx, struct afs_lookup_cookie, ctx); container_of(ctx, struct afs_lookup_one_cookie, ctx);
_enter("{%s,%u},%s,%u,,%llu,%u", _enter("{%s,%u},%s,%u,,%llu,%u",
cookie->name.name, cookie->name.len, name, nlen, cookie->name.name, cookie->name.len, name, nlen,
(unsigned long long) ino, dtype); (unsigned long long) ino, dtype);
/* insanity checks first */ /* insanity checks first */
BUILD_BUG_ON(sizeof(union afs_dir_block) != 2048); BUILD_BUG_ON(sizeof(union afs_xdr_dir_block) != 2048);
BUILD_BUG_ON(sizeof(union afs_dirent) != 32); BUILD_BUG_ON(sizeof(union afs_xdr_dirent) != 32);
if (cookie->name.len != nlen || if (cookie->name.len != nlen ||
memcmp(cookie->name.name, name, nlen) != 0) { memcmp(cookie->name.name, name, nlen) != 0) {
...@@ -447,15 +520,15 @@ static int afs_lookup_filldir(struct dir_context *ctx, const char *name, ...@@ -447,15 +520,15 @@ static int afs_lookup_filldir(struct dir_context *ctx, const char *name,
} }
/* /*
* do a lookup in a directory * Do a lookup of a single name in a directory
* - just returns the FID the dentry name maps to if found * - just returns the FID the dentry name maps to if found
*/ */
static int afs_do_lookup(struct inode *dir, struct dentry *dentry, static int afs_do_lookup_one(struct inode *dir, struct dentry *dentry,
struct afs_fid *fid, struct key *key) struct afs_fid *fid, struct key *key)
{ {
struct afs_super_info *as = dir->i_sb->s_fs_info; struct afs_super_info *as = dir->i_sb->s_fs_info;
struct afs_lookup_cookie cookie = { struct afs_lookup_one_cookie cookie = {
.ctx.actor = afs_lookup_filldir, .ctx.actor = afs_lookup_one_filldir,
.name = dentry->d_name, .name = dentry->d_name,
.fid.vid = as->volume->vid .fid.vid = as->volume->vid
}; };
...@@ -482,70 +555,265 @@ static int afs_do_lookup(struct inode *dir, struct dentry *dentry, ...@@ -482,70 +555,265 @@ static int afs_do_lookup(struct inode *dir, struct dentry *dentry,
} }
/* /*
* Probe to see if a cell may exist. This prevents positive dentries from * search the directory for a name
* being created unnecessarily. * - if afs_dir_iterate_block() spots this function, it'll pass the FID
* uniquifier through dtype
*/ */
static int afs_probe_cell_name(struct dentry *dentry) static int afs_lookup_filldir(struct dir_context *ctx, const char *name,
int nlen, loff_t fpos, u64 ino, unsigned dtype)
{ {
struct afs_cell *cell; struct afs_lookup_cookie *cookie =
const char *name = dentry->d_name.name; container_of(ctx, struct afs_lookup_cookie, ctx);
size_t len = dentry->d_name.len;
int ret; int ret;
/* Names prefixed with a dot are R/W mounts. */ _enter("{%s,%u},%s,%u,,%llu,%u",
if (name[0] == '.') { cookie->name.name, cookie->name.len, name, nlen,
if (len == 1) (unsigned long long) ino, dtype);
return -EINVAL;
name++;
len--;
}
cell = afs_lookup_cell_rcu(afs_d2net(dentry), name, len); /* insanity checks first */
if (!IS_ERR(cell)) { BUILD_BUG_ON(sizeof(union afs_xdr_dir_block) != 2048);
afs_put_cell(afs_d2net(dentry), cell); BUILD_BUG_ON(sizeof(union afs_xdr_dirent) != 32);
return 0;
if (cookie->found) {
if (cookie->nr_fids < 50) {
cookie->fids[cookie->nr_fids].vnode = ino;
cookie->fids[cookie->nr_fids].unique = dtype;
cookie->nr_fids++;
}
} else if (cookie->name.len == nlen &&
memcmp(cookie->name.name, name, nlen) == 0) {
cookie->fids[0].vnode = ino;
cookie->fids[0].unique = dtype;
cookie->found = 1;
if (cookie->one_only)
return -1;
} }
ret = dns_query("afsdb", name, len, "ipv4", NULL, NULL); ret = cookie->nr_fids >= 50 ? -1 : 0;
if (ret == -ENODATA) _leave(" = %d", ret);
ret = -EDESTADDRREQ;
return ret; return ret;
} }
/* /*
* Try to auto mount the mountpoint with pseudo directory, if the autocell * Do a lookup in a directory. We make use of bulk lookup to query a slew of
* operation is setted. * files in one go and create inodes for them. The inode of the file we were
* asked for is returned.
*/ */
static struct inode *afs_try_auto_mntpt(struct dentry *dentry, static struct inode *afs_do_lookup(struct inode *dir, struct dentry *dentry,
struct inode *dir, struct afs_fid *fid) struct key *key)
{ {
struct afs_vnode *vnode = AFS_FS_I(dir); struct afs_lookup_cookie *cookie;
struct inode *inode; struct afs_cb_interest *cbi = NULL;
int ret = -ENOENT; struct afs_super_info *as = dir->i_sb->s_fs_info;
struct afs_iget_data data;
struct afs_fs_cursor fc;
struct afs_vnode *dvnode = AFS_FS_I(dir);
struct inode *inode = NULL;
int ret, i;
_enter("%p{%pd}, {%x:%u}", _enter("{%lu},%p{%pd},", dir->i_ino, dentry, dentry);
dentry, dentry, vnode->fid.vid, vnode->fid.vnode);
cookie = kzalloc(sizeof(struct afs_lookup_cookie), GFP_KERNEL);
if (!cookie)
return ERR_PTR(-ENOMEM);
cookie->ctx.actor = afs_lookup_filldir;
cookie->name = dentry->d_name;
cookie->nr_fids = 1; /* slot 0 is saved for the fid we actually want */
read_seqlock_excl(&dvnode->cb_lock);
if (dvnode->cb_interest &&
dvnode->cb_interest->server &&
test_bit(AFS_SERVER_FL_NO_IBULK, &dvnode->cb_interest->server->flags))
cookie->one_only = true;
read_sequnlock_excl(&dvnode->cb_lock);
for (i = 0; i < 50; i++)
cookie->fids[i].vid = as->volume->vid;
/* search the directory */
ret = afs_dir_iterate(dir, &cookie->ctx, key);
if (ret < 0) {
inode = ERR_PTR(ret);
goto out;
}
if (!test_bit(AFS_VNODE_AUTOCELL, &vnode->flags)) inode = ERR_PTR(-ENOENT);
if (!cookie->found)
goto out; goto out;
ret = afs_probe_cell_name(dentry); /* Check to see if we already have an inode for the primary fid. */
if (ret < 0) data.volume = dvnode->volume;
data.fid = cookie->fids[0];
inode = ilookup5(dir->i_sb, cookie->fids[0].vnode, afs_iget5_test, &data);
if (inode)
goto out; goto out;
inode = afs_iget_pseudo_dir(dir->i_sb, false); /* Need space for examining all the selected files */
if (IS_ERR(inode)) { inode = ERR_PTR(-ENOMEM);
ret = PTR_ERR(inode); cookie->statuses = kcalloc(cookie->nr_fids, sizeof(struct afs_file_status),
GFP_KERNEL);
if (!cookie->statuses)
goto out; goto out;
cookie->callbacks = kcalloc(cookie->nr_fids, sizeof(struct afs_callback),
GFP_KERNEL);
if (!cookie->callbacks)
goto out_s;
/* Try FS.InlineBulkStatus first. Abort codes for the individual
* lookups contained therein are stored in the reply without aborting
* the whole operation.
*/
if (cookie->one_only)
goto no_inline_bulk_status;
inode = ERR_PTR(-ERESTARTSYS);
if (afs_begin_vnode_operation(&fc, dvnode, key)) {
while (afs_select_fileserver(&fc)) {
if (test_bit(AFS_SERVER_FL_NO_IBULK,
&fc.cbi->server->flags)) {
fc.ac.abort_code = RX_INVALID_OPERATION;
fc.ac.error = -ECONNABORTED;
break;
}
afs_fs_inline_bulk_status(&fc,
afs_v2net(dvnode),
cookie->fids,
cookie->statuses,
cookie->callbacks,
cookie->nr_fids, NULL);
}
if (fc.ac.error == 0)
cbi = afs_get_cb_interest(fc.cbi);
if (fc.ac.abort_code == RX_INVALID_OPERATION)
set_bit(AFS_SERVER_FL_NO_IBULK, &fc.cbi->server->flags);
inode = ERR_PTR(afs_end_vnode_operation(&fc));
} }
*fid = AFS_FS_I(inode)->fid; if (!IS_ERR(inode))
_leave("= %p", inode); goto success;
return inode; if (fc.ac.abort_code != RX_INVALID_OPERATION)
goto out_c;
no_inline_bulk_status:
/* We could try FS.BulkStatus next, but this aborts the entire op if
* any of the lookups fails - so, for the moment, revert to
* FS.FetchStatus for just the primary fid.
*/
cookie->nr_fids = 1;
inode = ERR_PTR(-ERESTARTSYS);
if (afs_begin_vnode_operation(&fc, dvnode, key)) {
while (afs_select_fileserver(&fc)) {
afs_fs_fetch_status(&fc,
afs_v2net(dvnode),
cookie->fids,
cookie->statuses,
cookie->callbacks,
NULL);
}
if (fc.ac.error == 0)
cbi = afs_get_cb_interest(fc.cbi);
inode = ERR_PTR(afs_end_vnode_operation(&fc));
}
if (IS_ERR(inode))
goto out_c;
for (i = 0; i < cookie->nr_fids; i++)
cookie->statuses[i].abort_code = 0;
success:
/* Turn all the files into inodes and save the first one - which is the
* one we actually want.
*/
if (cookie->statuses[0].abort_code != 0)
inode = ERR_PTR(afs_abort_to_error(cookie->statuses[0].abort_code));
for (i = 0; i < cookie->nr_fids; i++) {
struct inode *ti;
if (cookie->statuses[i].abort_code != 0)
continue;
ti = afs_iget(dir->i_sb, key, &cookie->fids[i],
&cookie->statuses[i],
&cookie->callbacks[i],
cbi);
if (i == 0) {
inode = ti;
} else {
if (!IS_ERR(ti))
iput(ti);
}
}
out_c:
afs_put_cb_interest(afs_v2net(dvnode), cbi);
kfree(cookie->callbacks);
out_s:
kfree(cookie->statuses);
out: out:
_leave("= %d", ret); kfree(cookie);
return ERR_PTR(ret); return inode;
}
/*
* Look up an entry in a directory with @sys substitution.
*/
static struct dentry *afs_lookup_atsys(struct inode *dir, struct dentry *dentry,
struct key *key)
{
struct afs_sysnames *subs;
struct afs_net *net = afs_i2net(dir);
struct dentry *ret;
char *buf, *p, *name;
int len, i;
_enter("");
ret = ERR_PTR(-ENOMEM);
p = buf = kmalloc(AFSNAMEMAX, GFP_KERNEL);
if (!buf)
goto out_p;
if (dentry->d_name.len > 4) {
memcpy(p, dentry->d_name.name, dentry->d_name.len - 4);
p += dentry->d_name.len - 4;
}
/* There is an ordered list of substitutes that we have to try. */
read_lock(&net->sysnames_lock);
subs = net->sysnames;
refcount_inc(&subs->usage);
read_unlock(&net->sysnames_lock);
for (i = 0; i < subs->nr; i++) {
name = subs->subs[i];
len = dentry->d_name.len - 4 + strlen(name);
if (len >= AFSNAMEMAX) {
ret = ERR_PTR(-ENAMETOOLONG);
goto out_s;
}
strcpy(p, name);
ret = lookup_one_len(buf, dentry->d_parent, len);
if (IS_ERR(ret) || d_is_positive(ret))
goto out_s;
dput(ret);
}
/* We don't want to d_add() the @sys dentry here as we don't want to
* the cached dentry to hide changes to the sysnames list.
*/
ret = NULL;
out_s:
afs_put_sysnames(subs);
kfree(buf);
out_p:
key_put(key);
return ret;
} }
/* /*
...@@ -554,16 +822,13 @@ static struct inode *afs_try_auto_mntpt(struct dentry *dentry, ...@@ -554,16 +822,13 @@ static struct inode *afs_try_auto_mntpt(struct dentry *dentry,
static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags) unsigned int flags)
{ {
struct afs_vnode *vnode; struct afs_vnode *dvnode = AFS_FS_I(dir);
struct afs_fid fid;
struct inode *inode; struct inode *inode;
struct key *key; struct key *key;
int ret; int ret;
vnode = AFS_FS_I(dir);
_enter("{%x:%u},%p{%pd},", _enter("{%x:%u},%p{%pd},",
vnode->fid.vid, vnode->fid.vnode, dentry, dentry); dvnode->fid.vid, dvnode->fid.vnode, dentry, dentry);
ASSERTCMP(d_inode(dentry), ==, NULL); ASSERTCMP(d_inode(dentry), ==, NULL);
...@@ -572,28 +837,37 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, ...@@ -572,28 +837,37 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
return ERR_PTR(-ENAMETOOLONG); return ERR_PTR(-ENAMETOOLONG);
} }
if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { if (test_bit(AFS_VNODE_DELETED, &dvnode->flags)) {
_leave(" = -ESTALE"); _leave(" = -ESTALE");
return ERR_PTR(-ESTALE); return ERR_PTR(-ESTALE);
} }
key = afs_request_key(vnode->volume->cell); key = afs_request_key(dvnode->volume->cell);
if (IS_ERR(key)) { if (IS_ERR(key)) {
_leave(" = %ld [key]", PTR_ERR(key)); _leave(" = %ld [key]", PTR_ERR(key));
return ERR_CAST(key); return ERR_CAST(key);
} }
ret = afs_validate(vnode, key); ret = afs_validate(dvnode, key);
if (ret < 0) { if (ret < 0) {
key_put(key); key_put(key);
_leave(" = %d [val]", ret); _leave(" = %d [val]", ret);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
ret = afs_do_lookup(dir, dentry, &fid, key); if (dentry->d_name.len >= 4 &&
if (ret < 0) { dentry->d_name.name[dentry->d_name.len - 4] == '@' &&
dentry->d_name.name[dentry->d_name.len - 3] == 's' &&
dentry->d_name.name[dentry->d_name.len - 2] == 'y' &&
dentry->d_name.name[dentry->d_name.len - 1] == 's')
return afs_lookup_atsys(dir, dentry, key);
afs_stat_v(dvnode, n_lookup);
inode = afs_do_lookup(dir, dentry, key);
if (IS_ERR(inode)) {
ret = PTR_ERR(inode);
if (ret == -ENOENT) { if (ret == -ENOENT) {
inode = afs_try_auto_mntpt(dentry, dir, &fid); inode = afs_try_auto_mntpt(dentry, dir);
if (!IS_ERR(inode)) { if (!IS_ERR(inode)) {
key_put(key); key_put(key);
goto success; goto success;
...@@ -611,10 +885,9 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, ...@@ -611,10 +885,9 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
_leave(" = %d [do]", ret); _leave(" = %d [do]", ret);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
dentry->d_fsdata = (void *)(unsigned long) vnode->status.data_version; dentry->d_fsdata = (void *)(unsigned long)dvnode->status.data_version;
/* instantiate the dentry */ /* instantiate the dentry */
inode = afs_iget(dir->i_sb, key, &fid, NULL, NULL, NULL);
key_put(key); key_put(key);
if (IS_ERR(inode)) { if (IS_ERR(inode)) {
_leave(" = %ld", PTR_ERR(inode)); _leave(" = %ld", PTR_ERR(inode));
...@@ -623,55 +896,13 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry, ...@@ -623,55 +896,13 @@ static struct dentry *afs_lookup(struct inode *dir, struct dentry *dentry,
success: success:
d_add(dentry, inode); d_add(dentry, inode);
_leave(" = 0 { vn=%u u=%u } -> { ino=%lu v=%u }", _leave(" = 0 { ino=%lu v=%u }",
fid.vnode,
fid.unique,
d_inode(dentry)->i_ino, d_inode(dentry)->i_ino,
d_inode(dentry)->i_generation); d_inode(dentry)->i_generation);
return NULL; return NULL;
} }
/*
* Look up an entry in a dynroot directory.
*/
static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentry,
unsigned int flags)
{
struct afs_vnode *vnode;
struct afs_fid fid;
struct inode *inode;
int ret;
vnode = AFS_FS_I(dir);
_enter("%pd", dentry);
ASSERTCMP(d_inode(dentry), ==, NULL);
if (dentry->d_name.len >= AFSNAMEMAX) {
_leave(" = -ENAMETOOLONG");
return ERR_PTR(-ENAMETOOLONG);
}
inode = afs_try_auto_mntpt(dentry, dir, &fid);
if (IS_ERR(inode)) {
ret = PTR_ERR(inode);
if (ret == -ENOENT) {
d_add(dentry, NULL);
_leave(" = NULL [negative]");
return NULL;
}
_leave(" = %d [do]", ret);
return ERR_PTR(ret);
}
d_add(dentry, inode);
_leave(" = 0 { ino=%lu v=%u }",
d_inode(dentry)->i_ino, d_inode(dentry)->i_generation);
return NULL;
}
/* /*
* check that a dentry lookup hit has found a valid entry * check that a dentry lookup hit has found a valid entry
* - NOTE! the hit can be a negative hit too, so we can't assume we have an * - NOTE! the hit can be a negative hit too, so we can't assume we have an
...@@ -679,21 +910,17 @@ static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentr ...@@ -679,21 +910,17 @@ static struct dentry *afs_dynroot_lookup(struct inode *dir, struct dentry *dentr
*/ */
static int afs_d_revalidate(struct dentry *dentry, unsigned int flags) static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
{ {
struct afs_super_info *as = dentry->d_sb->s_fs_info;
struct afs_vnode *vnode, *dir; struct afs_vnode *vnode, *dir;
struct afs_fid uninitialized_var(fid); struct afs_fid uninitialized_var(fid);
struct dentry *parent; struct dentry *parent;
struct inode *inode; struct inode *inode;
struct key *key; struct key *key;
void *dir_version; long dir_version, de_version;
int ret; int ret;
if (flags & LOOKUP_RCU) if (flags & LOOKUP_RCU)
return -ECHILD; return -ECHILD;
if (as->dyn_root)
return 1;
if (d_really_is_positive(dentry)) { if (d_really_is_positive(dentry)) {
vnode = AFS_FS_I(d_inode(dentry)); vnode = AFS_FS_I(d_inode(dentry));
_enter("{v={%x:%u} n=%pd fl=%lx},", _enter("{v={%x:%u} n=%pd fl=%lx},",
...@@ -729,14 +956,25 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags) ...@@ -729,14 +956,25 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
goto out_bad_parent; goto out_bad_parent;
} }
dir_version = (void *) (unsigned long) dir->status.data_version; /* We only need to invalidate a dentry if the server's copy changed
if (dentry->d_fsdata == dir_version) * behind our back. If we made the change, it's no problem. Note that
goto out_valid; /* the dir contents are unchanged */ * on a 32-bit system, we only have 32 bits in the dentry to store the
* version.
*/
dir_version = (long)dir->status.data_version;
de_version = (long)dentry->d_fsdata;
if (de_version == dir_version)
goto out_valid;
dir_version = (long)dir->invalid_before;
if (de_version - dir_version >= 0)
goto out_valid;
_debug("dir modified"); _debug("dir modified");
afs_stat_v(dir, n_reval);
/* search the directory for this vnode */ /* search the directory for this vnode */
ret = afs_do_lookup(&dir->vfs_inode, dentry, &fid, key); ret = afs_do_lookup_one(&dir->vfs_inode, dentry, &fid, key);
switch (ret) { switch (ret) {
case 0: case 0:
/* the filename maps to something */ /* the filename maps to something */
...@@ -789,7 +1027,7 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags) ...@@ -789,7 +1027,7 @@ static int afs_d_revalidate(struct dentry *dentry, unsigned int flags)
} }
out_valid: out_valid:
dentry->d_fsdata = dir_version; dentry->d_fsdata = (void *)dir_version;
dput(parent); dput(parent);
key_put(key); key_put(key);
_leave(" = 1 [valid]"); _leave(" = 1 [valid]");
...@@ -840,7 +1078,7 @@ static int afs_d_delete(const struct dentry *dentry) ...@@ -840,7 +1078,7 @@ static int afs_d_delete(const struct dentry *dentry)
/* /*
* handle dentry release * handle dentry release
*/ */
static void afs_d_release(struct dentry *dentry) void afs_d_release(struct dentry *dentry)
{ {
_enter("%pd", dentry); _enter("%pd", dentry);
} }
...@@ -854,6 +1092,7 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc, ...@@ -854,6 +1092,7 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc,
struct afs_file_status *newstatus, struct afs_file_status *newstatus,
struct afs_callback *newcb) struct afs_callback *newcb)
{ {
struct afs_vnode *vnode;
struct inode *inode; struct inode *inode;
if (fc->ac.error < 0) if (fc->ac.error < 0)
...@@ -871,6 +1110,8 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc, ...@@ -871,6 +1110,8 @@ static void afs_vnode_new_inode(struct afs_fs_cursor *fc,
return; return;
} }
vnode = AFS_FS_I(inode);
set_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
d_add(new_dentry, inode); d_add(new_dentry, inode);
} }
...@@ -885,6 +1126,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ...@@ -885,6 +1126,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
struct afs_vnode *dvnode = AFS_FS_I(dir); struct afs_vnode *dvnode = AFS_FS_I(dir);
struct afs_fid newfid; struct afs_fid newfid;
struct key *key; struct key *key;
u64 data_version = dvnode->status.data_version;
int ret; int ret;
mode |= S_IFDIR; mode |= S_IFDIR;
...@@ -902,7 +1144,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ...@@ -902,7 +1144,7 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
if (afs_begin_vnode_operation(&fc, dvnode, key)) { if (afs_begin_vnode_operation(&fc, dvnode, key)) {
while (afs_select_fileserver(&fc)) { while (afs_select_fileserver(&fc)) {
fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
afs_fs_create(&fc, dentry->d_name.name, mode, afs_fs_create(&fc, dentry->d_name.name, mode, data_version,
&newfid, &newstatus, &newcb); &newfid, &newstatus, &newcb);
} }
...@@ -916,6 +1158,11 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) ...@@ -916,6 +1158,11 @@ static int afs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
goto error_key; goto error_key;
} }
if (ret == 0 &&
test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_edit_dir_add(dvnode, &dentry->d_name, &newfid,
afs_edit_dir_for_create);
key_put(key); key_put(key);
_leave(" = 0"); _leave(" = 0");
return 0; return 0;
...@@ -939,6 +1186,7 @@ static void afs_dir_remove_subdir(struct dentry *dentry) ...@@ -939,6 +1186,7 @@ static void afs_dir_remove_subdir(struct dentry *dentry)
clear_nlink(&vnode->vfs_inode); clear_nlink(&vnode->vfs_inode);
set_bit(AFS_VNODE_DELETED, &vnode->flags); set_bit(AFS_VNODE_DELETED, &vnode->flags);
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
clear_bit(AFS_VNODE_DIR_VALID, &vnode->flags);
} }
} }
...@@ -950,6 +1198,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -950,6 +1198,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
struct afs_fs_cursor fc; struct afs_fs_cursor fc;
struct afs_vnode *dvnode = AFS_FS_I(dir); struct afs_vnode *dvnode = AFS_FS_I(dir);
struct key *key; struct key *key;
u64 data_version = dvnode->status.data_version;
int ret; int ret;
_enter("{%x:%u},{%pd}", _enter("{%x:%u},{%pd}",
...@@ -965,13 +1214,18 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry) ...@@ -965,13 +1214,18 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
if (afs_begin_vnode_operation(&fc, dvnode, key)) { if (afs_begin_vnode_operation(&fc, dvnode, key)) {
while (afs_select_fileserver(&fc)) { while (afs_select_fileserver(&fc)) {
fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
afs_fs_remove(&fc, dentry->d_name.name, true); afs_fs_remove(&fc, dentry->d_name.name, true,
data_version);
} }
afs_vnode_commit_status(&fc, dvnode, fc.cb_break); afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
ret = afs_end_vnode_operation(&fc); ret = afs_end_vnode_operation(&fc);
if (ret == 0) if (ret == 0) {
afs_dir_remove_subdir(dentry); afs_dir_remove_subdir(dentry);
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_edit_dir_remove(dvnode, &dentry->d_name,
afs_edit_dir_for_rmdir);
}
} }
key_put(key); key_put(key);
...@@ -1036,6 +1290,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1036,6 +1290,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode; struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode;
struct key *key; struct key *key;
unsigned long d_version = (unsigned long)dentry->d_fsdata; unsigned long d_version = (unsigned long)dentry->d_fsdata;
u64 data_version = dvnode->status.data_version;
int ret; int ret;
_enter("{%x:%u},{%pd}", _enter("{%x:%u},{%pd}",
...@@ -1062,7 +1317,8 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1062,7 +1317,8 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
if (afs_begin_vnode_operation(&fc, dvnode, key)) { if (afs_begin_vnode_operation(&fc, dvnode, key)) {
while (afs_select_fileserver(&fc)) { while (afs_select_fileserver(&fc)) {
fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
afs_fs_remove(&fc, dentry->d_name.name, false); afs_fs_remove(&fc, dentry->d_name.name, false,
data_version);
} }
afs_vnode_commit_status(&fc, dvnode, fc.cb_break); afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
...@@ -1071,6 +1327,10 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) ...@@ -1071,6 +1327,10 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
ret = afs_dir_remove_link( ret = afs_dir_remove_link(
dentry, key, d_version, dentry, key, d_version,
(unsigned long)dvnode->status.data_version); (unsigned long)dvnode->status.data_version);
if (ret == 0 &&
test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_edit_dir_remove(dvnode, &dentry->d_name,
afs_edit_dir_for_unlink);
} }
error_key: error_key:
...@@ -1092,6 +1352,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ...@@ -1092,6 +1352,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
struct afs_vnode *dvnode = AFS_FS_I(dir); struct afs_vnode *dvnode = AFS_FS_I(dir);
struct afs_fid newfid; struct afs_fid newfid;
struct key *key; struct key *key;
u64 data_version = dvnode->status.data_version;
int ret; int ret;
mode |= S_IFREG; mode |= S_IFREG;
...@@ -1113,7 +1374,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ...@@ -1113,7 +1374,7 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
if (afs_begin_vnode_operation(&fc, dvnode, key)) { if (afs_begin_vnode_operation(&fc, dvnode, key)) {
while (afs_select_fileserver(&fc)) { while (afs_select_fileserver(&fc)) {
fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
afs_fs_create(&fc, dentry->d_name.name, mode, afs_fs_create(&fc, dentry->d_name.name, mode, data_version,
&newfid, &newstatus, &newcb); &newfid, &newstatus, &newcb);
} }
...@@ -1127,6 +1388,10 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ...@@ -1127,6 +1388,10 @@ static int afs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
goto error_key; goto error_key;
} }
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_edit_dir_add(dvnode, &dentry->d_name, &newfid,
afs_edit_dir_for_create);
key_put(key); key_put(key);
_leave(" = 0"); _leave(" = 0");
return 0; return 0;
...@@ -1148,10 +1413,12 @@ static int afs_link(struct dentry *from, struct inode *dir, ...@@ -1148,10 +1413,12 @@ static int afs_link(struct dentry *from, struct inode *dir,
struct afs_fs_cursor fc; struct afs_fs_cursor fc;
struct afs_vnode *dvnode, *vnode; struct afs_vnode *dvnode, *vnode;
struct key *key; struct key *key;
u64 data_version;
int ret; int ret;
vnode = AFS_FS_I(d_inode(from)); vnode = AFS_FS_I(d_inode(from));
dvnode = AFS_FS_I(dir); dvnode = AFS_FS_I(dir);
data_version = dvnode->status.data_version;
_enter("{%x:%u},{%x:%u},{%pd}", _enter("{%x:%u},{%x:%u},{%pd}",
vnode->fid.vid, vnode->fid.vnode, vnode->fid.vid, vnode->fid.vnode,
...@@ -1178,7 +1445,7 @@ static int afs_link(struct dentry *from, struct inode *dir, ...@@ -1178,7 +1445,7 @@ static int afs_link(struct dentry *from, struct inode *dir,
while (afs_select_fileserver(&fc)) { while (afs_select_fileserver(&fc)) {
fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
fc.cb_break_2 = vnode->cb_break + vnode->cb_s_break; fc.cb_break_2 = vnode->cb_break + vnode->cb_s_break;
afs_fs_link(&fc, vnode, dentry->d_name.name); afs_fs_link(&fc, vnode, dentry->d_name.name, data_version);
} }
afs_vnode_commit_status(&fc, dvnode, fc.cb_break); afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
...@@ -1194,6 +1461,10 @@ static int afs_link(struct dentry *from, struct inode *dir, ...@@ -1194,6 +1461,10 @@ static int afs_link(struct dentry *from, struct inode *dir,
goto error_key; goto error_key;
} }
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_edit_dir_add(dvnode, &dentry->d_name, &vnode->fid,
afs_edit_dir_for_link);
key_put(key); key_put(key);
_leave(" = 0"); _leave(" = 0");
return 0; return 0;
...@@ -1217,6 +1488,7 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, ...@@ -1217,6 +1488,7 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
struct afs_vnode *dvnode = AFS_FS_I(dir); struct afs_vnode *dvnode = AFS_FS_I(dir);
struct afs_fid newfid; struct afs_fid newfid;
struct key *key; struct key *key;
u64 data_version = dvnode->status.data_version;
int ret; int ret;
_enter("{%x:%u},{%pd},%s", _enter("{%x:%u},{%pd},%s",
...@@ -1241,7 +1513,8 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, ...@@ -1241,7 +1513,8 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
if (afs_begin_vnode_operation(&fc, dvnode, key)) { if (afs_begin_vnode_operation(&fc, dvnode, key)) {
while (afs_select_fileserver(&fc)) { while (afs_select_fileserver(&fc)) {
fc.cb_break = dvnode->cb_break + dvnode->cb_s_break; fc.cb_break = dvnode->cb_break + dvnode->cb_s_break;
afs_fs_symlink(&fc, dentry->d_name.name, content, afs_fs_symlink(&fc, dentry->d_name.name,
content, data_version,
&newfid, &newstatus); &newfid, &newstatus);
} }
...@@ -1255,6 +1528,10 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry, ...@@ -1255,6 +1528,10 @@ static int afs_symlink(struct inode *dir, struct dentry *dentry,
goto error_key; goto error_key;
} }
if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_edit_dir_add(dvnode, &dentry->d_name, &newfid,
afs_edit_dir_for_symlink);
key_put(key); key_put(key);
_leave(" = 0"); _leave(" = 0");
return 0; return 0;
...@@ -1277,6 +1554,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1277,6 +1554,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
struct afs_fs_cursor fc; struct afs_fs_cursor fc;
struct afs_vnode *orig_dvnode, *new_dvnode, *vnode; struct afs_vnode *orig_dvnode, *new_dvnode, *vnode;
struct key *key; struct key *key;
u64 orig_data_version, new_data_version;
bool new_negative = d_is_negative(new_dentry);
int ret; int ret;
if (flags) if (flags)
...@@ -1285,6 +1564,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1285,6 +1564,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
vnode = AFS_FS_I(d_inode(old_dentry)); vnode = AFS_FS_I(d_inode(old_dentry));
orig_dvnode = AFS_FS_I(old_dir); orig_dvnode = AFS_FS_I(old_dir);
new_dvnode = AFS_FS_I(new_dir); new_dvnode = AFS_FS_I(new_dir);
orig_data_version = orig_dvnode->status.data_version;
new_data_version = new_dvnode->status.data_version;
_enter("{%x:%u},{%x:%u},{%x:%u},{%pd}", _enter("{%x:%u},{%x:%u},{%x:%u},{%pd}",
orig_dvnode->fid.vid, orig_dvnode->fid.vnode, orig_dvnode->fid.vid, orig_dvnode->fid.vnode,
...@@ -1310,7 +1591,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1310,7 +1591,8 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
fc.cb_break = orig_dvnode->cb_break + orig_dvnode->cb_s_break; fc.cb_break = orig_dvnode->cb_break + orig_dvnode->cb_s_break;
fc.cb_break_2 = new_dvnode->cb_break + new_dvnode->cb_s_break; fc.cb_break_2 = new_dvnode->cb_break + new_dvnode->cb_s_break;
afs_fs_rename(&fc, old_dentry->d_name.name, afs_fs_rename(&fc, old_dentry->d_name.name,
new_dvnode, new_dentry->d_name.name); new_dvnode, new_dentry->d_name.name,
orig_data_version, new_data_version);
} }
afs_vnode_commit_status(&fc, orig_dvnode, fc.cb_break); afs_vnode_commit_status(&fc, orig_dvnode, fc.cb_break);
...@@ -1322,9 +1604,68 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -1322,9 +1604,68 @@ static int afs_rename(struct inode *old_dir, struct dentry *old_dentry,
goto error_key; goto error_key;
} }
if (ret == 0) {
if (test_bit(AFS_VNODE_DIR_VALID, &orig_dvnode->flags))
afs_edit_dir_remove(orig_dvnode, &old_dentry->d_name,
afs_edit_dir_for_rename);
if (!new_negative &&
test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags))
afs_edit_dir_remove(new_dvnode, &new_dentry->d_name,
afs_edit_dir_for_rename);
if (test_bit(AFS_VNODE_DIR_VALID, &new_dvnode->flags))
afs_edit_dir_add(new_dvnode, &new_dentry->d_name,
&vnode->fid, afs_edit_dir_for_rename);
}
error_key: error_key:
key_put(key); key_put(key);
error: error:
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
} }
/*
* Release a directory page and clean up its private state if it's not busy
* - return true if the page can now be released, false if not
*/
static int afs_dir_releasepage(struct page *page, gfp_t gfp_flags)
{
struct afs_vnode *dvnode = AFS_FS_I(page->mapping->host);
_enter("{{%x:%u}[%lu]}", dvnode->fid.vid, dvnode->fid.vnode, page->index);
set_page_private(page, 0);
ClearPagePrivate(page);
/* The directory will need reloading. */
if (test_and_clear_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_stat_v(dvnode, n_relpg);
return 1;
}
/*
* invalidate part or all of a page
* - release a page and clean up its private data if offset is 0 (indicating
* the entire page)
*/
static void afs_dir_invalidatepage(struct page *page, unsigned int offset,
unsigned int length)
{
struct afs_vnode *dvnode = AFS_FS_I(page->mapping->host);
_enter("{%lu},%u,%u", page->index, offset, length);
BUG_ON(!PageLocked(page));
/* The directory will need reloading. */
if (test_and_clear_bit(AFS_VNODE_DIR_VALID, &dvnode->flags))
afs_stat_v(dvnode, n_inval);
/* we clean up only if the entire page is being invalidated */
if (offset == 0 && length == PAGE_SIZE) {
set_page_private(page, 0);
ClearPagePrivate(page);
}
}
/* 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;
}
/* 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, ...@@ -30,7 +30,6 @@ static int afs_readpages(struct file *filp, struct address_space *mapping,
const struct file_operations afs_file_operations = { const struct file_operations afs_file_operations = {
.open = afs_open, .open = afs_open,
.flush = afs_flush,
.release = afs_release, .release = afs_release,
.llseek = generic_file_llseek, .llseek = generic_file_llseek,
.read_iter = generic_file_read_iter, .read_iter = generic_file_read_iter,
...@@ -146,6 +145,9 @@ int afs_open(struct inode *inode, struct file *file) ...@@ -146,6 +145,9 @@ int afs_open(struct inode *inode, struct file *file)
if (ret < 0) if (ret < 0)
goto error_af; goto error_af;
} }
if (file->f_flags & O_TRUNC)
set_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags);
file->private_data = af; file->private_data = af;
_leave(" = 0"); _leave(" = 0");
...@@ -170,6 +172,9 @@ int afs_release(struct inode *inode, struct file *file) ...@@ -170,6 +172,9 @@ int afs_release(struct inode *inode, struct file *file)
_enter("{%x:%u},", vnode->fid.vid, vnode->fid.vnode); _enter("{%x:%u},", vnode->fid.vid, vnode->fid.vnode);
if ((file->f_mode & FMODE_WRITE))
return vfs_fsync(file, 0);
file->private_data = NULL; file->private_data = NULL;
if (af->wb) if (af->wb)
afs_put_wb_key(af->wb); afs_put_wb_key(af->wb);
...@@ -187,10 +192,12 @@ void afs_put_read(struct afs_read *req) ...@@ -187,10 +192,12 @@ void afs_put_read(struct afs_read *req)
{ {
int i; int i;
if (atomic_dec_and_test(&req->usage)) { if (refcount_dec_and_test(&req->usage)) {
for (i = 0; i < req->nr_pages; i++) for (i = 0; i < req->nr_pages; i++)
if (req->pages[i]) if (req->pages[i])
put_page(req->pages[i]); put_page(req->pages[i]);
if (req->pages != req->array)
kfree(req->pages);
kfree(req); kfree(req);
} }
} }
...@@ -240,6 +247,12 @@ int afs_fetch_data(struct afs_vnode *vnode, struct key *key, struct afs_read *de ...@@ -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); 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); _leave(" = %d", ret);
return ret; return ret;
} }
...@@ -297,10 +310,11 @@ int afs_page_filler(void *data, struct page *page) ...@@ -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 * end of the file, the server will return a short read and the
* unmarshalling code will clear the unfilled space. * 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->pos = (loff_t)page->index << PAGE_SHIFT;
req->len = PAGE_SIZE; req->len = PAGE_SIZE;
req->nr_pages = 1; req->nr_pages = 1;
req->pages = req->array;
req->pages[0] = page; req->pages[0] = page;
get_page(page); get_page(page);
...@@ -309,10 +323,6 @@ int afs_page_filler(void *data, struct page *page) ...@@ -309,10 +323,6 @@ int afs_page_filler(void *data, struct page *page)
ret = afs_fetch_data(vnode, key, req); ret = afs_fetch_data(vnode, key, req);
afs_put_read(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 < 0) {
if (ret == -ENOENT) { if (ret == -ENOENT) {
_debug("got NOENT from server" _debug("got NOENT from server"
...@@ -447,10 +457,11 @@ static int afs_readpages_one(struct file *file, struct address_space *mapping, ...@@ -447,10 +457,11 @@ static int afs_readpages_one(struct file *file, struct address_space *mapping,
if (!req) if (!req)
return -ENOMEM; return -ENOMEM;
atomic_set(&req->usage, 1); refcount_set(&req->usage, 1);
req->page_done = afs_readpages_page_done; req->page_done = afs_readpages_page_done;
req->pos = first->index; req->pos = first->index;
req->pos <<= PAGE_SHIFT; req->pos <<= PAGE_SHIFT;
req->pages = req->array;
/* Transfer the pages to the request. We add them in until one fails /* 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 * 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) ...@@ -613,7 +613,7 @@ static int afs_do_getlk(struct file *file, struct file_lock *fl)
posix_test_lock(file, fl); posix_test_lock(file, fl);
if (fl->fl_type == F_UNLCK) { if (fl->fl_type == F_UNLCK) {
/* no local locks; consult the server */ /* no local locks; consult the server */
ret = afs_fetch_status(vnode, key); ret = afs_fetch_status(vnode, key, false);
if (ret < 0) if (ret < 0)
goto error; goto error;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/iversion.h> #include <linux/iversion.h>
#include "internal.h" #include "internal.h"
#include "afs_fs.h" #include "afs_fs.h"
#include "xdr_fs.h"
static const struct afs_fid afs_zero_fid; static const struct afs_fid afs_zero_fid;
...@@ -44,109 +45,194 @@ static void xdr_decode_AFSFid(const __be32 **_bp, struct afs_fid *fid) ...@@ -44,109 +45,194 @@ static void xdr_decode_AFSFid(const __be32 **_bp, struct afs_fid *fid)
} }
/* /*
* decode an AFSFetchStatus block * Dump a bad file status record.
*/ */
static void xdr_decode_AFSFetchStatus(const __be32 **_bp, static void xdr_dump_bad(const __be32 *bp)
struct afs_file_status *status,
struct afs_vnode *vnode,
afs_dataversion_t *store_version)
{ {
afs_dataversion_t expected_version; __be32 x[4];
const __be32 *bp = *_bp; 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; 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 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)
{
const struct afs_xdr_AFSFetchStatus *xdr = (const void *)*_bp;
u64 data_version, size; u64 data_version, size;
bool changed = false; u32 type, abort_code;
kuid_t owner; u8 flags = 0;
kgid_t group; int ret;
if (vnode) if (vnode)
write_seqlock(&vnode->cb_lock); write_seqlock(&vnode->cb_lock);
#define EXTRACT(DST) \ if (xdr->if_version != htonl(AFS_FSTATUS_VERSION)) {
do { \ pr_warn("Unknown AFSFetchStatus version %u\n", ntohl(xdr->if_version));
u32 x = ntohl(*bp++); \ goto bad;
if (DST != x) \ }
changed |= true; \
DST = 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;
if (size != status->size) { type = ntohl(xdr->type);
status->size = size; abort_code = ntohl(xdr->abort_code);
changed |= true; 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;
} }
status->mode &= S_IALLUGO;
_debug("vnode time %lx, %lx", #define EXTRACT_M(FIELD) \
status->mtime_client, status->mtime_server); do { \
u32 x = ntohl(xdr->FIELD); \
if (status->FIELD != x) { \
flags |= AFS_VNODE_META_CHANGED; \
status->FIELD = x; \
} \
} while (0)
if (vnode) { EXTRACT_M(nlink);
if (changed && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) { EXTRACT_M(author);
_debug("vnode changed"); EXTRACT_M(owner);
i_size_write(&vnode->vfs_inode, size); EXTRACT_M(caller_access); /* call ticket dependent */
vnode->vfs_inode.i_uid = status->owner; EXTRACT_M(anon_access);
vnode->vfs_inode.i_gid = status->group; EXTRACT_M(mode);
vnode->vfs_inode.i_generation = vnode->fid.unique; EXTRACT_M(group);
set_nlink(&vnode->vfs_inode, status->nlink);
status->mtime_client = ntohl(xdr->mtime_client);
mode = vnode->vfs_inode.i_mode; status->mtime_server = ntohl(xdr->mtime_server);
mode &= ~S_IALLUGO; status->lock_count = ntohl(xdr->lock_count);
mode |= status->mode;
barrier(); size = (u64)ntohl(xdr->size_lo);
vnode->vfs_inode.i_mode = mode; 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;
}
vnode->vfs_inode.i_ctime.tv_sec = status->mtime_client; if (read_req) {
vnode->vfs_inode.i_mtime = vnode->vfs_inode.i_ctime; read_req->data_version = data_version;
vnode->vfs_inode.i_atime = vnode->vfs_inode.i_ctime; read_req->file_size = size;
inode_set_iversion_raw(&vnode->vfs_inode, data_version);
} }
expected_version = status->data_version; *_bp = (const void *)*_bp + sizeof(*xdr);
if (store_version)
expected_version = *store_version;
if (expected_version != data_version) { if (vnode) {
status->data_version = data_version; if (test_bit(AFS_VNODE_UNSET, &vnode->flags))
if (vnode && !test_bit(AFS_VNODE_UNSET, &vnode->flags)) { flags |= AFS_VNODE_NOT_YET_SET;
_debug("vnode modified %llx on {%x:%u}", afs_update_inode_from_status(vnode, status, expected_version,
(unsigned long long) data_version, flags);
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) if (vnode)
write_sequnlock(&vnode->cb_lock); 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, ...@@ -274,7 +360,7 @@ static void xdr_decode_AFSFetchVolumeStatus(const __be32 **_bp,
/* /*
* deliver reply data to an FS.FetchStatus * 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]; struct afs_vnode *vnode = call->reply[0];
const __be32 *bp; const __be32 *bp;
...@@ -288,7 +374,9 @@ static int afs_deliver_fs_fetch_status(struct afs_call *call) ...@@ -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 */ /* unmarshall the reply once we've received all of it */
bp = call->buffer; 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); xdr_decode_AFSCallBack(call, vnode, &bp);
if (call->reply[1]) if (call->reply[1])
xdr_decode_AFSVolSync(&bp, 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) ...@@ -300,17 +388,18 @@ static int afs_deliver_fs_fetch_status(struct afs_call *call)
/* /*
* FS.FetchStatus operation type * FS.FetchStatus operation type
*/ */
static const struct afs_call_type afs_RXFSFetchStatus = { static const struct afs_call_type afs_RXFSFetchStatus_vnode = {
.name = "FS.FetchStatus", .name = "FS.FetchStatus(vnode)",
.op = afs_FS_FetchStatus, .op = afs_FS_FetchStatus,
.deliver = afs_deliver_fs_fetch_status, .deliver = afs_deliver_fs_fetch_status_vnode,
.destructor = afs_flat_call_destructor, .destructor = afs_flat_call_destructor,
}; };
/* /*
* fetch the status information for a file * 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_vnode *vnode = fc->vnode;
struct afs_call *call; struct afs_call *call;
...@@ -320,7 +409,8 @@ int afs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_volsync *volsy ...@@ -320,7 +409,8 @@ int afs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_volsync *volsy
_enter(",%x,{%x:%u},,", _enter(",%x,{%x:%u},,",
key_serial(fc->key), vnode->fid.vid, vnode->fid.vnode); 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) { if (!call) {
fc->ac.error = -ENOMEM; fc->ac.error = -ENOMEM;
return -ENOMEM; return -ENOMEM;
...@@ -329,6 +419,7 @@ int afs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_volsync *volsy ...@@ -329,6 +419,7 @@ int afs_fs_fetch_file_status(struct afs_fs_cursor *fc, struct afs_volsync *volsy
call->key = fc->key; call->key = fc->key;
call->reply[0] = vnode; call->reply[0] = vnode;
call->reply[1] = volsync; call->reply[1] = volsync;
call->expected_version = new_inode ? 1 : vnode->status.data_version;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
...@@ -464,7 +555,9 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call) ...@@ -464,7 +555,9 @@ static int afs_deliver_fs_fetch_data(struct afs_call *call)
return ret; return ret;
bp = call->buffer; 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); xdr_decode_AFSCallBack(call, vnode, &bp);
if (call->reply[1]) if (call->reply[1])
xdr_decode_AFSVolSync(&bp, 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) ...@@ -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[0] = vnode;
call->reply[1] = NULL; /* volsync */ call->reply[1] = NULL; /* volsync */
call->reply[2] = req; call->reply[2] = req;
call->expected_version = vnode->status.data_version;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
...@@ -546,7 +640,7 @@ static int afs_fs_fetch_data64(struct afs_fs_cursor *fc, struct afs_read *req) ...@@ -546,7 +640,7 @@ static int afs_fs_fetch_data64(struct afs_fs_cursor *fc, struct afs_read *req)
bp[6] = 0; bp[6] = 0;
bp[7] = htonl(lower_32_bits(req->len)); bp[7] = htonl(lower_32_bits(req->len));
atomic_inc(&req->usage); refcount_inc(&req->usage);
call->cb_break = fc->cb_break; call->cb_break = fc->cb_break;
afs_use_fs_server(call, fc->cbi); afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid); 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) ...@@ -578,6 +672,7 @@ int afs_fs_fetch_data(struct afs_fs_cursor *fc, struct afs_read *req)
call->reply[0] = vnode; call->reply[0] = vnode;
call->reply[1] = NULL; /* volsync */ call->reply[1] = NULL; /* volsync */
call->reply[2] = req; call->reply[2] = req;
call->expected_version = vnode->status.data_version;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
...@@ -588,7 +683,7 @@ int afs_fs_fetch_data(struct afs_fs_cursor *fc, struct afs_read *req) ...@@ -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[4] = htonl(lower_32_bits(req->pos));
bp[5] = htonl(lower_32_bits(req->len)); bp[5] = htonl(lower_32_bits(req->len));
atomic_inc(&req->usage); refcount_inc(&req->usage);
call->cb_break = fc->cb_break; call->cb_break = fc->cb_break;
afs_use_fs_server(call, fc->cbi); afs_use_fs_server(call, fc->cbi);
trace_afs_make_fs_call(call, &vnode->fid); trace_afs_make_fs_call(call, &vnode->fid);
...@@ -613,8 +708,10 @@ static int afs_deliver_fs_create_vnode(struct afs_call *call) ...@@ -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 */ /* unmarshall the reply once we've received all of it */
bp = call->buffer; bp = call->buffer;
xdr_decode_AFSFid(&bp, call->reply[1]); xdr_decode_AFSFid(&bp, call->reply[1]);
xdr_decode_AFSFetchStatus(&bp, call->reply[2], NULL, NULL); if (xdr_decode_AFSFetchStatus(call, &bp, call->reply[2], NULL, NULL, NULL) < 0 ||
xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL); 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_AFSCallBack_raw(&bp, call->reply[3]);
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */ /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
...@@ -645,6 +742,7 @@ static const struct afs_call_type afs_RXFSMakeDir = { ...@@ -645,6 +742,7 @@ static const struct afs_call_type afs_RXFSMakeDir = {
int afs_fs_create(struct afs_fs_cursor *fc, int afs_fs_create(struct afs_fs_cursor *fc,
const char *name, const char *name,
umode_t mode, umode_t mode,
u64 current_data_version,
struct afs_fid *newfid, struct afs_fid *newfid,
struct afs_file_status *newstatus, struct afs_file_status *newstatus,
struct afs_callback *newcb) struct afs_callback *newcb)
...@@ -672,6 +770,7 @@ int afs_fs_create(struct afs_fs_cursor *fc, ...@@ -672,6 +770,7 @@ int afs_fs_create(struct afs_fs_cursor *fc,
call->reply[1] = newfid; call->reply[1] = newfid;
call->reply[2] = newstatus; call->reply[2] = newstatus;
call->reply[3] = newcb; call->reply[3] = newcb;
call->expected_version = current_data_version + 1;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
...@@ -715,7 +814,9 @@ static int afs_deliver_fs_remove(struct afs_call *call) ...@@ -715,7 +814,9 @@ static int afs_deliver_fs_remove(struct afs_call *call)
/* unmarshall the reply once we've received all of it */ /* unmarshall the reply once we've received all of it */
bp = call->buffer; bp = call->buffer;
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]); */ /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
_leave(" = 0 [done]"); _leave(" = 0 [done]");
...@@ -742,7 +843,8 @@ static const struct afs_call_type afs_RXFSRemoveDir = { ...@@ -742,7 +843,8 @@ static const struct afs_call_type afs_RXFSRemoveDir = {
/* /*
* remove a file or directory * 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_vnode *vnode = fc->vnode;
struct afs_call *call; struct afs_call *call;
...@@ -764,6 +866,7 @@ int afs_fs_remove(struct afs_fs_cursor *fc, const char *name, bool isdir) ...@@ -764,6 +866,7 @@ int afs_fs_remove(struct afs_fs_cursor *fc, const char *name, bool isdir)
call->key = fc->key; call->key = fc->key;
call->reply[0] = vnode; call->reply[0] = vnode;
call->expected_version = current_data_version + 1;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
...@@ -801,8 +904,10 @@ static int afs_deliver_fs_link(struct afs_call *call) ...@@ -801,8 +904,10 @@ static int afs_deliver_fs_link(struct afs_call *call)
/* unmarshall the reply once we've received all of it */ /* unmarshall the reply once we've received all of it */
bp = call->buffer; bp = call->buffer;
xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, NULL); if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode, NULL, NULL) < 0 ||
xdr_decode_AFSFetchStatus(&bp, &dvnode->status, dvnode, NULL); 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]); */ /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
_leave(" = 0 [done]"); _leave(" = 0 [done]");
...@@ -823,7 +928,7 @@ static const struct afs_call_type afs_RXFSLink = { ...@@ -823,7 +928,7 @@ static const struct afs_call_type afs_RXFSLink = {
* make a hard link * make a hard link
*/ */
int afs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode, 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_vnode *dvnode = fc->vnode;
struct afs_call *call; struct afs_call *call;
...@@ -844,6 +949,7 @@ int afs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode, ...@@ -844,6 +949,7 @@ int afs_fs_link(struct afs_fs_cursor *fc, struct afs_vnode *vnode,
call->key = fc->key; call->key = fc->key;
call->reply[0] = dvnode; call->reply[0] = dvnode;
call->reply[1] = vnode; call->reply[1] = vnode;
call->expected_version = current_data_version + 1;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
...@@ -885,8 +991,10 @@ static int afs_deliver_fs_symlink(struct afs_call *call) ...@@ -885,8 +991,10 @@ static int afs_deliver_fs_symlink(struct afs_call *call)
/* unmarshall the reply once we've received all of it */ /* unmarshall the reply once we've received all of it */
bp = call->buffer; bp = call->buffer;
xdr_decode_AFSFid(&bp, call->reply[1]); xdr_decode_AFSFid(&bp, call->reply[1]);
xdr_decode_AFSFetchStatus(&bp, call->reply[2], NULL, NULL); if (xdr_decode_AFSFetchStatus(call, &bp, call->reply[2], NULL, NULL, NULL) ||
xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, 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]); */ /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
_leave(" = 0 [done]"); _leave(" = 0 [done]");
...@@ -909,6 +1017,7 @@ static const struct afs_call_type afs_RXFSSymlink = { ...@@ -909,6 +1017,7 @@ static const struct afs_call_type afs_RXFSSymlink = {
int afs_fs_symlink(struct afs_fs_cursor *fc, int afs_fs_symlink(struct afs_fs_cursor *fc,
const char *name, const char *name,
const char *contents, const char *contents,
u64 current_data_version,
struct afs_fid *newfid, struct afs_fid *newfid,
struct afs_file_status *newstatus) struct afs_file_status *newstatus)
{ {
...@@ -937,6 +1046,7 @@ int afs_fs_symlink(struct afs_fs_cursor *fc, ...@@ -937,6 +1046,7 @@ int afs_fs_symlink(struct afs_fs_cursor *fc,
call->reply[0] = vnode; call->reply[0] = vnode;
call->reply[1] = newfid; call->reply[1] = newfid;
call->reply[2] = newstatus; call->reply[2] = newstatus;
call->expected_version = current_data_version + 1;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
...@@ -987,10 +1097,13 @@ static int afs_deliver_fs_rename(struct afs_call *call) ...@@ -987,10 +1097,13 @@ static int afs_deliver_fs_rename(struct afs_call *call)
/* unmarshall the reply once we've received all of it */ /* unmarshall the reply once we've received all of it */
bp = call->buffer; bp = call->buffer;
xdr_decode_AFSFetchStatus(&bp, &orig_dvnode->status, orig_dvnode, NULL); if (xdr_decode_AFSFetchStatus(call, &bp, &orig_dvnode->status, orig_dvnode,
if (new_dvnode != orig_dvnode) &call->expected_version, NULL) < 0)
xdr_decode_AFSFetchStatus(&bp, &new_dvnode->status, new_dvnode, return afs_protocol_error(call, -EBADMSG);
NULL); 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]); */ /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
_leave(" = 0 [done]"); _leave(" = 0 [done]");
...@@ -1013,7 +1126,9 @@ static const struct afs_call_type afs_RXFSRename = { ...@@ -1013,7 +1126,9 @@ static const struct afs_call_type afs_RXFSRename = {
int afs_fs_rename(struct afs_fs_cursor *fc, int afs_fs_rename(struct afs_fs_cursor *fc,
const char *orig_name, const char *orig_name,
struct afs_vnode *new_dvnode, 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_vnode *orig_dvnode = fc->vnode;
struct afs_call *call; struct afs_call *call;
...@@ -1041,6 +1156,8 @@ int afs_fs_rename(struct afs_fs_cursor *fc, ...@@ -1041,6 +1156,8 @@ int afs_fs_rename(struct afs_fs_cursor *fc,
call->key = fc->key; call->key = fc->key;
call->reply[0] = orig_dvnode; call->reply[0] = orig_dvnode;
call->reply[1] = new_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 */ /* marshall the parameters */
bp = call->request; bp = call->request;
...@@ -1089,8 +1206,9 @@ static int afs_deliver_fs_store_data(struct afs_call *call) ...@@ -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 */ /* unmarshall the reply once we've received all of it */
bp = call->buffer; bp = call->buffer;
xdr_decode_AFSFetchStatus(&bp, &vnode->status, vnode, if (xdr_decode_AFSFetchStatus(call, &bp, &vnode->status, vnode,
&call->store_version); &call->expected_version, NULL) < 0)
return afs_protocol_error(call, -EBADMSG);
/* xdr_decode_AFSVolSync(&bp, call->reply[X]); */ /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
afs_pages_written_back(vnode, call); afs_pages_written_back(vnode, call);
...@@ -1147,7 +1265,7 @@ static int afs_fs_store_data64(struct afs_fs_cursor *fc, ...@@ -1147,7 +1265,7 @@ static int afs_fs_store_data64(struct afs_fs_cursor *fc,
call->first_offset = offset; call->first_offset = offset;
call->last_to = to; call->last_to = to;
call->send_pages = true; call->send_pages = true;
call->store_version = vnode->status.data_version + 1; call->expected_version = vnode->status.data_version + 1;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
...@@ -1222,7 +1340,7 @@ int afs_fs_store_data(struct afs_fs_cursor *fc, struct address_space *mapping, ...@@ -1222,7 +1340,7 @@ int afs_fs_store_data(struct afs_fs_cursor *fc, struct address_space *mapping,
call->first_offset = offset; call->first_offset = offset;
call->last_to = to; call->last_to = to;
call->send_pages = true; call->send_pages = true;
call->store_version = vnode->status.data_version + 1; call->expected_version = vnode->status.data_version + 1;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
...@@ -1252,7 +1370,6 @@ int afs_fs_store_data(struct afs_fs_cursor *fc, struct address_space *mapping, ...@@ -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) static int afs_deliver_fs_store_status(struct afs_call *call)
{ {
afs_dataversion_t *store_version;
struct afs_vnode *vnode = call->reply[0]; struct afs_vnode *vnode = call->reply[0];
const __be32 *bp; const __be32 *bp;
int ret; int ret;
...@@ -1264,12 +1381,10 @@ static int afs_deliver_fs_store_status(struct afs_call *call) ...@@ -1264,12 +1381,10 @@ static int afs_deliver_fs_store_status(struct afs_call *call)
return ret; return ret;
/* unmarshall the reply once we've received all of it */ /* 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; 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]); */ /* xdr_decode_AFSVolSync(&bp, call->reply[X]); */
_leave(" = 0 [done]"); _leave(" = 0 [done]");
...@@ -1324,7 +1439,7 @@ static int afs_fs_setattr_size64(struct afs_fs_cursor *fc, struct iattr *attr) ...@@ -1324,7 +1439,7 @@ static int afs_fs_setattr_size64(struct afs_fs_cursor *fc, struct iattr *attr)
call->key = fc->key; call->key = fc->key;
call->reply[0] = vnode; call->reply[0] = vnode;
call->store_version = vnode->status.data_version + 1; call->expected_version = vnode->status.data_version + 1;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
...@@ -1373,7 +1488,7 @@ static int afs_fs_setattr_size(struct afs_fs_cursor *fc, struct iattr *attr) ...@@ -1373,7 +1488,7 @@ static int afs_fs_setattr_size(struct afs_fs_cursor *fc, struct iattr *attr)
call->key = fc->key; call->key = fc->key;
call->reply[0] = vnode; call->reply[0] = vnode;
call->store_version = vnode->status.data_version + 1; call->expected_version = vnode->status.data_version + 1;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
...@@ -1418,6 +1533,7 @@ int afs_fs_setattr(struct afs_fs_cursor *fc, struct iattr *attr) ...@@ -1418,6 +1533,7 @@ int afs_fs_setattr(struct afs_fs_cursor *fc, struct iattr *attr)
call->key = fc->key; call->key = fc->key;
call->reply[0] = vnode; call->reply[0] = vnode;
call->expected_version = vnode->status.data_version;
/* marshall the parameters */ /* marshall the parameters */
bp = call->request; bp = call->request;
...@@ -1471,7 +1587,7 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call) ...@@ -1471,7 +1587,7 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call)
call->count = ntohl(call->tmp); call->count = ntohl(call->tmp);
_debug("volname length: %u", call->count); _debug("volname length: %u", call->count);
if (call->count >= AFSNAMEMAX) if (call->count >= AFSNAMEMAX)
return -EBADMSG; return afs_protocol_error(call, -EBADMSG);
call->offset = 0; call->offset = 0;
call->unmarshall++; call->unmarshall++;
...@@ -1518,7 +1634,7 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call) ...@@ -1518,7 +1634,7 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call)
call->count = ntohl(call->tmp); call->count = ntohl(call->tmp);
_debug("offline msg length: %u", call->count); _debug("offline msg length: %u", call->count);
if (call->count >= AFSNAMEMAX) if (call->count >= AFSNAMEMAX)
return -EBADMSG; return afs_protocol_error(call, -EBADMSG);
call->offset = 0; call->offset = 0;
call->unmarshall++; call->unmarshall++;
...@@ -1565,7 +1681,7 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call) ...@@ -1565,7 +1681,7 @@ static int afs_deliver_fs_get_volume_status(struct afs_call *call)
call->count = ntohl(call->tmp); call->count = ntohl(call->tmp);
_debug("motd length: %u", call->count); _debug("motd length: %u", call->count);
if (call->count >= AFSNAMEMAX) if (call->count >= AFSNAMEMAX)
return -EBADMSG; return afs_protocol_error(call, -EBADMSG);
call->offset = 0; call->offset = 0;
call->unmarshall++; call->unmarshall++;
...@@ -1947,3 +2063,265 @@ int afs_fs_get_capabilities(struct afs_net *net, ...@@ -1947,3 +2063,265 @@ int afs_fs_get_capabilities(struct afs_net *net,
trace_afs_make_fs_call(call, NULL); trace_afs_make_fs_call(call, NULL);
return afs_make_call(ac, call, GFP_NOFS, false); 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 = { ...@@ -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); struct inode *inode = AFS_VNODE_TO_I(vnode);
bool changed;
_debug("FS: ft=%d lk=%d sz=%llu ver=%Lu mod=%hu", _debug("FS: ft=%d lk=%d sz=%llu ver=%Lu mod=%hu",
vnode->status.type, vnode->status.type,
...@@ -46,16 +45,21 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key) ...@@ -46,16 +45,21 @@ static int afs_inode_map_status(struct afs_vnode *vnode, struct key *key)
read_seqlock_excl(&vnode->cb_lock); read_seqlock_excl(&vnode->cb_lock);
afs_update_inode_from_status(vnode, &vnode->status, NULL,
AFS_VNODE_NOT_YET_SET);
switch (vnode->status.type) { switch (vnode->status.type) {
case AFS_FTYPE_FILE: case AFS_FTYPE_FILE:
inode->i_mode = S_IFREG | vnode->status.mode; inode->i_mode = S_IFREG | vnode->status.mode;
inode->i_op = &afs_file_inode_operations; inode->i_op = &afs_file_inode_operations;
inode->i_fop = &afs_file_operations; inode->i_fop = &afs_file_operations;
inode->i_mapping->a_ops = &afs_fs_aops;
break; break;
case AFS_FTYPE_DIR: case AFS_FTYPE_DIR:
inode->i_mode = S_IFDIR | vnode->status.mode; inode->i_mode = S_IFDIR | vnode->status.mode;
inode->i_op = &afs_dir_inode_operations; inode->i_op = &afs_dir_inode_operations;
inode->i_fop = &afs_dir_file_operations; inode->i_fop = &afs_dir_file_operations;
inode->i_mapping->a_ops = &afs_dir_aops;
break; break;
case AFS_FTYPE_SYMLINK: case AFS_FTYPE_SYMLINK:
/* Symlinks with a mode of 0644 are actually mountpoints. */ /* 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) ...@@ -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_mode = S_IFDIR | 0555;
inode->i_op = &afs_mntpt_inode_operations; inode->i_op = &afs_mntpt_inode_operations;
inode->i_fop = &afs_mntpt_file_operations; inode->i_fop = &afs_mntpt_file_operations;
inode->i_mapping->a_ops = &afs_fs_aops;
} else { } else {
inode->i_mode = S_IFLNK | vnode->status.mode; inode->i_mode = S_IFLNK | vnode->status.mode;
inode->i_op = &afs_symlink_inode_operations; inode->i_op = &afs_symlink_inode_operations;
inode->i_mapping->a_ops = &afs_fs_aops;
} }
inode_nohighmem(inode); inode_nohighmem(inode);
break; break;
default: default:
printk("kAFS: AFS vnode with undefined type\n"); printk("kAFS: AFS vnode with undefined type\n");
read_sequnlock_excl(&vnode->cb_lock); 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_blocks = 0;
inode->i_generation = vnode->fid.unique; vnode->invalid_before = vnode->status.data_version;
inode_set_iversion_raw(inode, vnode->status.data_version);
inode->i_mapping->a_ops = &afs_fs_aops;
read_sequnlock_excl(&vnode->cb_lock); read_sequnlock_excl(&vnode->cb_lock);
#ifdef CONFIG_AFS_FSCACHE
if (changed)
fscache_attr_changed(vnode->cache);
#endif
return 0; return 0;
} }
/* /*
* Fetch file status from the volume. * 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; struct afs_fs_cursor fc;
int ret; int ret;
...@@ -119,7 +109,7 @@ int afs_fetch_status(struct afs_vnode *vnode, struct key *key) ...@@ -119,7 +109,7 @@ int afs_fetch_status(struct afs_vnode *vnode, struct key *key)
if (afs_begin_vnode_operation(&fc, vnode, key)) { if (afs_begin_vnode_operation(&fc, vnode, key)) {
while (afs_select_fileserver(&fc)) { while (afs_select_fileserver(&fc)) {
fc.cb_break = vnode->cb_break + vnode->cb_s_break; 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); afs_check_for_remote_deletion(&fc, fc.vnode);
...@@ -255,6 +245,11 @@ static void afs_get_inode_cache(struct afs_vnode *vnode) ...@@ -255,6 +245,11 @@ static void afs_get_inode_cache(struct afs_vnode *vnode)
} __packed key; } __packed key;
struct afs_vnode_cache_aux aux; struct afs_vnode_cache_aux aux;
if (vnode->status.type == AFS_FTYPE_DIR) {
vnode->cache = NULL;
return;
}
key.vnode_id = vnode->fid.vnode; key.vnode_id = vnode->fid.vnode;
key.unique = vnode->fid.unique; key.unique = vnode->fid.unique;
key.vnode_id_ext[0] = 0; key.vnode_id_ext[0] = 0;
...@@ -307,7 +302,7 @@ struct inode *afs_iget(struct super_block *sb, struct key *key, ...@@ -307,7 +302,7 @@ struct inode *afs_iget(struct super_block *sb, struct key *key,
if (!status) { if (!status) {
/* it's a remotely extant inode */ /* it's a remotely extant inode */
ret = afs_fetch_status(vnode, key); ret = afs_fetch_status(vnode, key, true);
if (ret < 0) if (ret < 0)
goto bad_inode; goto bad_inode;
} else { } else {
...@@ -331,15 +326,12 @@ struct inode *afs_iget(struct super_block *sb, struct key *key, ...@@ -331,15 +326,12 @@ struct inode *afs_iget(struct super_block *sb, struct key *key,
vnode->cb_expires_at += ktime_get_real_seconds(); vnode->cb_expires_at += ktime_get_real_seconds();
} }
/* set up caching before mapping the status, as map-status reads the ret = afs_inode_init_from_status(vnode, key);
* 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);
if (ret < 0) if (ret < 0)
goto bad_inode; goto bad_inode;
afs_get_inode_cache(vnode);
/* success */ /* success */
clear_bit(AFS_VNODE_UNSET, &vnode->flags); clear_bit(AFS_VNODE_UNSET, &vnode->flags);
inode->i_flags |= S_NOATIME; inode->i_flags |= S_NOATIME;
...@@ -349,10 +341,6 @@ struct inode *afs_iget(struct super_block *sb, struct key *key, ...@@ -349,10 +341,6 @@ struct inode *afs_iget(struct super_block *sb, struct key *key,
/* failure */ /* failure */
bad_inode: bad_inode:
#ifdef CONFIG_AFS_FSCACHE
fscache_relinquish_cookie(vnode->cache, NULL, ret == -ENOENT);
vnode->cache = NULL;
#endif
iget_failed(inode); iget_failed(inode);
_leave(" = %d [bad]", ret); _leave(" = %d [bad]", ret);
return ERR_PTR(ret); return ERR_PTR(ret);
...@@ -407,8 +395,11 @@ int afs_validate(struct afs_vnode *vnode, struct key *key) ...@@ -407,8 +395,11 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { if (test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break) { if (vnode->cb_s_break != vnode->cb_interest->server->cb_s_break) {
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) && } else if (vnode->status.type == AFS_FTYPE_DIR &&
!test_bit(AFS_VNODE_ZAP_DATA, &vnode->flags) && 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) { vnode->cb_expires_at - 10 > now) {
valid = true; valid = true;
} }
...@@ -432,7 +423,7 @@ int afs_validate(struct afs_vnode *vnode, struct key *key) ...@@ -432,7 +423,7 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
* access */ * access */
if (!test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) { if (!test_bit(AFS_VNODE_CB_PROMISED, &vnode->flags)) {
_debug("not promised"); _debug("not promised");
ret = afs_fetch_status(vnode, key); ret = afs_fetch_status(vnode, key, false);
if (ret < 0) { if (ret < 0) {
if (ret == -ENOENT) { if (ret == -ENOENT) {
set_bit(AFS_VNODE_DELETED, &vnode->flags); set_bit(AFS_VNODE_DELETED, &vnode->flags);
...@@ -453,8 +444,6 @@ int afs_validate(struct afs_vnode *vnode, struct key *key) ...@@ -453,8 +444,6 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
* different */ * different */
if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags)) if (test_and_clear_bit(AFS_VNODE_ZAP_DATA, &vnode->flags))
afs_zap_data(vnode); afs_zap_data(vnode);
clear_bit(AFS_VNODE_DIR_MODIFIED, &vnode->flags);
mutex_unlock(&vnode->validate_lock); mutex_unlock(&vnode->validate_lock);
valid: valid:
_leave(" = 0"); _leave(" = 0");
...@@ -544,7 +533,7 @@ void afs_evict_inode(struct inode *inode) ...@@ -544,7 +533,7 @@ void afs_evict_inode(struct inode *inode)
} }
#endif #endif
afs_put_permits(vnode->permit_cache); afs_put_permits(rcu_access_pointer(vnode->permit_cache));
_leave(""); _leave("");
} }
......
...@@ -122,7 +122,8 @@ struct afs_call { ...@@ -122,7 +122,8 @@ struct afs_call {
u32 operation_ID; /* operation ID for an incoming call */ u32 operation_ID; /* operation ID for an incoming call */
u32 count; /* count for use in unmarshalling */ u32 count; /* count for use in unmarshalling */
__be32 tmp; /* place to extract temporary data */ __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 { struct afs_call_type {
...@@ -173,11 +174,14 @@ struct afs_read { ...@@ -173,11 +174,14 @@ struct afs_read {
loff_t len; /* How much we're asking for */ loff_t len; /* How much we're asking for */
loff_t actual_len; /* How much we're actually getting */ loff_t actual_len; /* How much we're actually getting */
loff_t remain; /* Amount remaining */ 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 index; /* Which page we're reading into */
unsigned int nr_pages; unsigned int nr_pages;
void (*page_done)(struct afs_call *, struct afs_read *); 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) ...@@ -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; 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. * AFS network namespace record.
*/ */
...@@ -245,9 +261,25 @@ struct afs_net { ...@@ -245,9 +261,25 @@ struct afs_net {
struct mutex lock_manager_mutex; struct mutex lock_manager_mutex;
/* Misc */ /* 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 extern struct afs_net __afs_net;// Dummy AFS network namespace; TODO: replace with real netns
enum afs_cell_state { enum afs_cell_state {
...@@ -363,6 +395,7 @@ struct afs_server { ...@@ -363,6 +395,7 @@ struct afs_server {
#define AFS_SERVER_FL_UPDATING 4 #define AFS_SERVER_FL_UPDATING 4
#define AFS_SERVER_FL_PROBED 5 /* The fileserver has been probed */ #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_PROBING 6 /* Fileserver is being probed */
#define AFS_SERVER_FL_NO_IBULK 7 /* Fileserver doesn't support FS.InlineBulkStatus */
atomic_t usage; atomic_t usage;
u32 addr_version; /* Address list version */ u32 addr_version; /* Address list version */
...@@ -455,10 +488,11 @@ struct afs_vnode { ...@@ -455,10 +488,11 @@ struct afs_vnode {
struct afs_volume *volume; /* volume on which vnode resides */ struct afs_volume *volume; /* volume on which vnode resides */
struct afs_fid fid; /* the file identifier for this inode */ struct afs_fid fid; /* the file identifier for this inode */
struct afs_file_status status; /* AFS status info for this file */ 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 #ifdef CONFIG_AFS_FSCACHE
struct fscache_cookie *cache; /* caching cookie */ struct fscache_cookie *cache; /* caching cookie */
#endif #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 io_lock; /* Lock for serialising I/O on this mutex */
struct mutex validate_lock; /* lock for validating this vnode */ struct mutex validate_lock; /* lock for validating this vnode */
spinlock_t wb_lock; /* lock for wb_keys */ spinlock_t wb_lock; /* lock for wb_keys */
...@@ -466,12 +500,13 @@ struct afs_vnode { ...@@ -466,12 +500,13 @@ struct afs_vnode {
unsigned long flags; unsigned long flags;
#define AFS_VNODE_CB_PROMISED 0 /* Set if vnode has a callback promise */ #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_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_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_DELETED 4 /* set if vnode deleted on server */
#define AFS_VNODE_MOUNTPOINT 5 /* set if vnode is a mountpoint symlink */ #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_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_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 wb_keys; /* List of keys available for writeback */
struct list_head pending_locks; /* locks waiting to be granted */ struct list_head pending_locks; /* locks waiting to be granted */
...@@ -611,7 +646,7 @@ extern struct fscache_cookie_def afs_vnode_cache_index_def; ...@@ -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_init_callback_state(struct afs_server *);
extern void afs_break_callback(struct afs_vnode *); 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 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 *); 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 *); ...@@ -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 file_operations afs_dir_file_operations;
extern const struct inode_operations afs_dir_inode_operations; extern const struct inode_operations afs_dir_inode_operations;
extern const struct address_space_operations afs_dir_aops;
extern const struct dentry_operations afs_fs_dentry_operations;
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 file_operations afs_dynroot_file_operations;
extern const struct inode_operations afs_dynroot_inode_operations; extern const struct inode_operations afs_dynroot_inode_operations;
extern const struct dentry_operations afs_fs_dentry_operations; extern const struct dentry_operations afs_dynroot_dentry_operations;
extern bool afs_dir_check_page(struct inode *, struct page *); extern struct inode *afs_try_auto_mntpt(struct dentry *, struct inode *);
/* /*
* file.c * file.c
...@@ -680,17 +730,23 @@ extern int afs_flock(struct file *, int, struct file_lock *); ...@@ -680,17 +730,23 @@ extern int afs_flock(struct file *, int, struct file_lock *);
/* /*
* fsclient.c * 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_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_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 *); 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_remove(struct afs_fs_cursor *, const char *, bool, u64);
extern int afs_fs_link(struct afs_fs_cursor *, struct afs_vnode *, const char *); 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 *, extern int afs_fs_symlink(struct afs_fs_cursor *, const char *, const char *, u64,
struct afs_fid *, struct afs_file_status *); struct afs_fid *, struct afs_file_status *);
extern int afs_fs_rename(struct afs_fs_cursor *, const char *, 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 *, extern int afs_fs_store_data(struct afs_fs_cursor *, struct address_space *,
pgoff_t, pgoff_t, unsigned, unsigned); pgoff_t, pgoff_t, unsigned, unsigned);
extern int afs_fs_setattr(struct afs_fs_cursor *, struct iattr *); 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 *, ...@@ -702,11 +758,18 @@ extern int afs_fs_give_up_all_callbacks(struct afs_net *, struct afs_server *,
struct afs_addr_cursor *, struct key *); struct afs_addr_cursor *, struct key *);
extern int afs_fs_get_capabilities(struct afs_net *, struct afs_server *, extern int afs_fs_get_capabilities(struct afs_net *, struct afs_server *,
struct afs_addr_cursor *, struct key *); 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 * 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 int afs_iget5_test(struct inode *, void *);
extern struct inode *afs_iget_pseudo_dir(struct super_block *, bool); extern struct inode *afs_iget_pseudo_dir(struct super_block *, bool);
extern struct inode *afs_iget(struct super_block *, struct key *, extern struct inode *afs_iget(struct super_block *, struct key *,
...@@ -754,6 +817,13 @@ static inline void afs_put_net(struct afs_net *net) ...@@ -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 * misc.c
*/ */
...@@ -781,6 +851,7 @@ extern int __net_init afs_proc_init(struct afs_net *); ...@@ -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 void __net_exit afs_proc_cleanup(struct afs_net *);
extern int afs_proc_cell_setup(struct afs_net *, struct afs_cell *); extern int afs_proc_cell_setup(struct afs_net *, struct afs_cell *);
extern void afs_proc_cell_remove(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 * rotate.c
...@@ -809,6 +880,7 @@ extern void afs_flat_call_destructor(struct afs_call *); ...@@ -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_empty_reply(struct afs_call *);
extern void afs_send_simple_reply(struct afs_call *, const void *, size_t); 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_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) static inline int afs_transfer_reply(struct afs_call *call)
{ {
...@@ -955,7 +1027,6 @@ extern int afs_writepage(struct page *, struct writeback_control *); ...@@ -955,7 +1027,6 @@ extern int afs_writepage(struct page *, struct writeback_control *);
extern int afs_writepages(struct address_space *, 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 void afs_pages_written_back(struct afs_vnode *, struct afs_call *);
extern ssize_t afs_file_write(struct kiocb *, struct iov_iter *); 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_fsync(struct file *, loff_t, loff_t, int);
extern int afs_page_mkwrite(struct vm_fault *); extern int afs_page_mkwrite(struct vm_fault *);
extern void afs_prune_wb_keys(struct afs_vnode *); 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"); ...@@ -34,11 +34,42 @@ MODULE_PARM_DESC(rootcell, "root AFS cell name and VL server IP addr list");
struct workqueue_struct *afs_wq; struct workqueue_struct *afs_wq;
struct afs_net __afs_net; 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. * Initialise an AFS network namespace record.
*/ */
static int __net_init afs_net_init(struct afs_net *net) static int __net_init afs_net_init(struct afs_net *net)
{ {
struct afs_sysnames *sysnames;
int ret; int ret;
net->live = true; net->live = true;
...@@ -67,6 +98,16 @@ static int __net_init afs_net_init(struct afs_net *net) ...@@ -67,6 +98,16 @@ static int __net_init afs_net_init(struct afs_net *net)
INIT_WORK(&net->fs_manager, afs_manage_servers); INIT_WORK(&net->fs_manager, afs_manage_servers);
timer_setup(&net->fs_timer, afs_servers_timer, 0); 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 */ /* Register the /proc stuff */
ret = afs_proc_init(net); ret = afs_proc_init(net);
if (ret < 0) if (ret < 0)
...@@ -92,6 +133,8 @@ static int __net_init afs_net_init(struct afs_net *net) ...@@ -92,6 +133,8 @@ static int __net_init afs_net_init(struct afs_net *net)
net->live = false; net->live = false;
afs_proc_cleanup(net); afs_proc_cleanup(net);
error_proc: error_proc:
afs_put_sysnames(net->sysnames);
error_sysnames:
net->live = false; net->live = false;
return ret; return ret;
} }
...@@ -106,6 +149,7 @@ static void __net_exit afs_net_exit(struct afs_net *net) ...@@ -106,6 +149,7 @@ static void __net_exit afs_net_exit(struct afs_net *net)
afs_purge_servers(net); afs_purge_servers(net);
afs_close_socket(net); afs_close_socket(net);
afs_proc_cleanup(net); afs_proc_cleanup(net);
afs_put_sysnames(net->sysnames);
} }
/* /*
......
...@@ -126,6 +126,34 @@ static const struct file_operations afs_proc_servers_fops = { ...@@ -126,6 +126,34 @@ static const struct file_operations afs_proc_servers_fops = {
.release = seq_release, .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 * initialise the /proc/fs/afs/ directory
*/ */
...@@ -139,7 +167,9 @@ int afs_proc_init(struct afs_net *net) ...@@ -139,7 +167,9 @@ int afs_proc_init(struct afs_net *net)
if (!proc_create("cells", 0644, net->proc_afs, &afs_proc_cells_fops) || 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("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; goto error_tree;
_leave(" = 0"); _leave(" = 0");
...@@ -183,6 +213,7 @@ static int afs_proc_cells_open(struct inode *inode, struct file *file) ...@@ -183,6 +213,7 @@ static int afs_proc_cells_open(struct inode *inode, struct file *file)
* first item * first item
*/ */
static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos) static void *afs_proc_cells_start(struct seq_file *m, loff_t *_pos)
__acquires(rcu)
{ {
struct afs_net *net = afs_seq2net(m); 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) ...@@ -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 * clean up after reading from the cells list
*/ */
static void afs_proc_cells_stop(struct seq_file *m, void *v) static void afs_proc_cells_stop(struct seq_file *m, void *v)
__releases(rcu)
{ {
rcu_read_unlock(); rcu_read_unlock();
} }
...@@ -282,7 +314,8 @@ static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf, ...@@ -282,7 +314,8 @@ static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
goto done; 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); printk("kAFS: Added new cell '%s'\n", name);
} else { } else {
goto inval; goto inval;
...@@ -304,7 +337,40 @@ static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf, ...@@ -304,7 +337,40 @@ static ssize_t afs_proc_cells_write(struct file *file, const char __user *buf,
static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf, static ssize_t afs_proc_rootcell_read(struct file *file, char __user *buf,
size_t size, loff_t *_pos) 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, ...@@ -327,6 +393,12 @@ static ssize_t afs_proc_rootcell_write(struct file *file,
if (IS_ERR(kbuf)) if (IS_ERR(kbuf))
return PTR_ERR(kbuf); return PTR_ERR(kbuf);
ret = -EINVAL;
if (kbuf[0] == '.')
goto out;
if (memchr(kbuf, '/', size))
goto out;
/* trim to first NL */ /* trim to first NL */
s = memchr(kbuf, '\n', size); s = memchr(kbuf, '\n', size);
if (s) if (s)
...@@ -339,6 +411,7 @@ static ssize_t afs_proc_rootcell_write(struct file *file, ...@@ -339,6 +411,7 @@ static ssize_t afs_proc_rootcell_write(struct file *file,
if (ret >= 0) if (ret >= 0)
ret = size; /* consume everything, always */ ret = size; /* consume everything, always */
out:
kfree(kbuf); kfree(kbuf);
_leave(" = %d", ret); _leave(" = %d", ret);
return ret; return ret;
...@@ -413,6 +486,7 @@ static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file) ...@@ -413,6 +486,7 @@ static int afs_proc_cell_volumes_open(struct inode *inode, struct file *file)
* first item * first item
*/ */
static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos) static void *afs_proc_cell_volumes_start(struct seq_file *m, loff_t *_pos)
__acquires(cell->proc_lock)
{ {
struct afs_cell *cell = m->private; struct afs_cell *cell = m->private;
...@@ -438,6 +512,7 @@ static void *afs_proc_cell_volumes_next(struct seq_file *p, void *v, ...@@ -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 * clean up after reading from the cells list
*/ */
static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v) static void afs_proc_cell_volumes_stop(struct seq_file *p, void *v)
__releases(cell->proc_lock)
{ {
struct afs_cell *cell = p->private; struct afs_cell *cell = p->private;
...@@ -500,6 +575,7 @@ static int afs_proc_cell_vlservers_open(struct inode *inode, struct file *file) ...@@ -500,6 +575,7 @@ static int afs_proc_cell_vlservers_open(struct inode *inode, struct file *file)
* first item * first item
*/ */
static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos) static void *afs_proc_cell_vlservers_start(struct seq_file *m, loff_t *_pos)
__acquires(rcu)
{ {
struct afs_addr_list *alist; struct afs_addr_list *alist;
struct afs_cell *cell = m->private; struct afs_cell *cell = m->private;
...@@ -544,6 +620,7 @@ static void *afs_proc_cell_vlservers_next(struct seq_file *p, void *v, ...@@ -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 * clean up after reading from the cells list
*/ */
static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v) static void afs_proc_cell_vlservers_stop(struct seq_file *p, void *v)
__releases(rcu)
{ {
rcu_read_unlock(); rcu_read_unlock();
} }
...@@ -580,6 +657,7 @@ static int afs_proc_servers_open(struct inode *inode, struct file *file) ...@@ -580,6 +657,7 @@ static int afs_proc_servers_open(struct inode *inode, struct file *file)
* first item. * first item.
*/ */
static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos) static void *afs_proc_servers_start(struct seq_file *m, loff_t *_pos)
__acquires(rcu)
{ {
struct afs_net *net = afs_seq2net(m); 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) ...@@ -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 * clean up after reading from the cells list
*/ */
static void afs_proc_servers_stop(struct seq_file *p, void *v) static void afs_proc_servers_stop(struct seq_file *p, void *v)
__releases(rcu)
{ {
rcu_read_unlock(); rcu_read_unlock();
} }
...@@ -626,3 +705,244 @@ static int afs_proc_servers_show(struct seq_file *m, void *v) ...@@ -626,3 +705,244 @@ static int afs_proc_servers_show(struct seq_file *m, void *v)
&alist->addrs[alist->index].transport); &alist->addrs[alist->index].transport);
return 0; 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 @@ ...@@ -21,7 +21,7 @@
/* /*
* Initialise a filesystem server cursor for iterating over FS servers. * 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)); memset(fc, 0, sizeof(*fc));
} }
......
...@@ -926,3 +926,12 @@ int afs_extract_data(struct afs_call *call, void *buf, size_t count, ...@@ -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); afs_set_call_complete(call, ret, remote_abort);
return ret; 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, ...@@ -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)) { if (cb_break != (vnode->cb_break + vnode->cb_interest->server->cb_s_break))
rcu_read_unlock();
goto someone_else_changed_it; goto someone_else_changed_it;
}
/* We need a ref on any permits list we want to copy as we'll have to /* We need a ref on any permits list we want to copy as we'll have to
* drop the lock to do memory allocation. * drop the lock to do memory allocation.
*/ */
if (permits && !refcount_inc_not_zero(&permits->usage)) { if (permits && !refcount_inc_not_zero(&permits->usage))
rcu_read_unlock();
goto someone_else_changed_it; goto someone_else_changed_it;
}
rcu_read_unlock(); rcu_read_unlock();
...@@ -278,6 +274,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key, ...@@ -278,6 +274,7 @@ void afs_cache_permit(struct afs_vnode *vnode, struct key *key,
/* Someone else changed the cache under us - don't recheck at this /* Someone else changed the cache under us - don't recheck at this
* time. * time.
*/ */
rcu_read_unlock();
return; return;
} }
...@@ -296,8 +293,6 @@ int afs_check_permit(struct afs_vnode *vnode, struct key *key, ...@@ -296,8 +293,6 @@ int afs_check_permit(struct afs_vnode *vnode, struct key *key,
_enter("{%x:%u},%x", _enter("{%x:%u},%x",
vnode->fid.vid, vnode->fid.vnode, key_serial(key)); vnode->fid.vid, vnode->fid.vnode, key_serial(key));
permits = vnode->permit_cache;
/* check the permits to see if we've got one yet */ /* check the permits to see if we've got one yet */
if (key == vnode->volume->cell->anonymous_key) { if (key == vnode->volume->cell->anonymous_key) {
_debug("anon"); _debug("anon");
...@@ -327,7 +322,7 @@ int afs_check_permit(struct afs_vnode *vnode, struct key *key, ...@@ -327,7 +322,7 @@ int afs_check_permit(struct afs_vnode *vnode, struct key *key,
*/ */
_debug("no valid permit"); _debug("no valid permit");
ret = afs_fetch_status(vnode, key); ret = afs_fetch_status(vnode, key, false);
if (ret < 0) { if (ret < 0) {
*_access = 0; *_access = 0;
_leave(" = %d", ret); _leave(" = %d", ret);
......
...@@ -59,7 +59,8 @@ struct afs_server *afs_find_server(struct afs_net *net, ...@@ -59,7 +59,8 @@ struct afs_server *afs_find_server(struct afs_net *net,
alist = rcu_dereference(server->addresses); alist = rcu_dereference(server->addresses);
for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) { for (i = alist->nr_ipv4; i < alist->nr_addrs; i++) {
b = &alist->addrs[i].transport.sin6; 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) if (diff == 0)
diff = memcmp(&a->sin6_addr, diff = memcmp(&a->sin6_addr,
&b->sin6_addr, &b->sin6_addr,
...@@ -79,10 +80,11 @@ struct afs_server *afs_find_server(struct afs_net *net, ...@@ -79,10 +80,11 @@ struct afs_server *afs_find_server(struct afs_net *net,
alist = rcu_dereference(server->addresses); alist = rcu_dereference(server->addresses);
for (i = 0; i < alist->nr_ipv4; i++) { for (i = 0; i < alist->nr_ipv4; i++) {
b = &alist->addrs[i].transport.sin6; 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) if (diff == 0)
diff = ((u32)a->sin6_addr.s6_addr32[3] - diff = ((u32 __force)a->sin6_addr.s6_addr32[3] -
(u32)b->sin6_addr.s6_addr32[3]); (u32 __force)b->sin6_addr.s6_addr32[3]);
if (diff == 0) if (diff == 0)
goto found; goto found;
if (diff < 0) { if (diff < 0) {
...@@ -381,7 +383,7 @@ static void afs_server_rcu(struct rcu_head *rcu) ...@@ -381,7 +383,7 @@ static void afs_server_rcu(struct rcu_head *rcu)
{ {
struct afs_server *server = container_of(rcu, struct afs_server, 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); kfree(server);
} }
...@@ -390,7 +392,7 @@ static void afs_server_rcu(struct rcu_head *rcu) ...@@ -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) 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 = { struct afs_addr_cursor ac = {
.alist = alist, .alist = alist,
.addr = &alist->addrs[0], .addr = &alist->addrs[0],
......
...@@ -154,7 +154,7 @@ static int afs_show_devname(struct seq_file *m, struct dentry *root) ...@@ -154,7 +154,7 @@ static int afs_show_devname(struct seq_file *m, struct dentry *root)
seq_puts(m, "none"); seq_puts(m, "none");
return 0; return 0;
} }
switch (volume->type) { switch (volume->type) {
case AFSVL_RWVOL: case AFSVL_RWVOL:
break; break;
...@@ -269,7 +269,7 @@ static int afs_parse_device_name(struct afs_mount_params *params, ...@@ -269,7 +269,7 @@ static int afs_parse_device_name(struct afs_mount_params *params,
int cellnamesz; int cellnamesz;
_enter(",%s", name); _enter(",%s", name);
if (!name) { if (!name) {
printk(KERN_ERR "kAFS: no volume name specified\n"); printk(KERN_ERR "kAFS: no volume name specified\n");
return -EINVAL; return -EINVAL;
...@@ -418,7 +418,10 @@ static int afs_fill_super(struct super_block *sb, ...@@ -418,7 +418,10 @@ static int afs_fill_super(struct super_block *sb,
if (!sb->s_root) if (!sb->s_root)
goto error; 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"); _leave(" = 0");
return 0; return 0;
...@@ -676,7 +679,7 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf) ...@@ -676,7 +679,7 @@ static int afs_statfs(struct dentry *dentry, struct kstatfs *buf)
buf->f_bfree = 0; buf->f_bfree = 0;
return 0; return 0;
} }
key = afs_request_key(vnode->volume->cell); key = afs_request_key(vnode->volume->cell);
if (IS_ERR(key)) if (IS_ERR(key))
return PTR_ERR(key); return PTR_ERR(key);
......
...@@ -303,7 +303,7 @@ struct afs_addr_list *afs_vl_get_addrs_u(struct afs_net *net, ...@@ -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_hi_and_reserved = htonl(u->clock_seq_hi_and_reserved);
r->uuid.clock_seq_low = htonl(u->clock_seq_low); r->uuid.clock_seq_low = htonl(u->clock_seq_low);
for (i = 0; i < 6; i++) 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); trace_afs_make_vl_call(call);
return (struct afs_addr_list *)afs_make_call(ac, call, GFP_KERNEL, false); return (struct afs_addr_list *)afs_make_call(ac, call, GFP_KERNEL, false);
...@@ -450,7 +450,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call) ...@@ -450,7 +450,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
call->count2 = ntohl(*bp); /* Type or next count */ call->count2 = ntohl(*bp); /* Type or next count */
if (call->count > YFS_MAXENDPOINTS) if (call->count > YFS_MAXENDPOINTS)
return -EBADMSG; return afs_protocol_error(call, -EBADMSG);
alist = afs_alloc_addrlist(call->count, FS_SERVICE, AFS_FS_PORT); alist = afs_alloc_addrlist(call->count, FS_SERVICE, AFS_FS_PORT);
if (!alist) if (!alist)
...@@ -474,7 +474,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call) ...@@ -474,7 +474,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
size = sizeof(__be32) * (1 + 4 + 1); size = sizeof(__be32) * (1 + 4 + 1);
break; break;
default: default:
return -EBADMSG; return afs_protocol_error(call, -EBADMSG);
} }
size += sizeof(__be32); size += sizeof(__be32);
...@@ -487,24 +487,24 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call) ...@@ -487,24 +487,24 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
switch (call->count2) { switch (call->count2) {
case YFS_ENDPOINT_IPV4: case YFS_ENDPOINT_IPV4:
if (ntohl(bp[0]) != sizeof(__be32) * 2) 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])); afs_merge_fs_addr4(alist, bp[1], ntohl(bp[2]));
bp += 3; bp += 3;
break; break;
case YFS_ENDPOINT_IPV6: case YFS_ENDPOINT_IPV6:
if (ntohl(bp[0]) != sizeof(__be32) * 5) 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])); afs_merge_fs_addr6(alist, bp + 1, ntohl(bp[5]));
bp += 6; bp += 6;
break; break;
default: default:
return -EBADMSG; return afs_protocol_error(call, -EBADMSG);
} }
/* Got either the type of the next entry or the count of /* Got either the type of the next entry or the count of
* volEndpoints if no more fsEndpoints. * volEndpoints if no more fsEndpoints.
*/ */
call->count2 = htonl(*bp++); call->count2 = ntohl(*bp++);
call->offset = 0; call->offset = 0;
call->count--; call->count--;
...@@ -517,7 +517,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call) ...@@ -517,7 +517,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
if (!call->count) if (!call->count)
goto end; goto end;
if (call->count > YFS_MAXENDPOINTS) if (call->count > YFS_MAXENDPOINTS)
return -EBADMSG; return afs_protocol_error(call, -EBADMSG);
call->unmarshall = 3; call->unmarshall = 3;
...@@ -531,7 +531,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call) ...@@ -531,7 +531,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
return ret; return ret;
bp = call->buffer; bp = call->buffer;
call->count2 = htonl(*bp++); call->count2 = ntohl(*bp++);
call->offset = 0; call->offset = 0;
call->unmarshall = 4; call->unmarshall = 4;
...@@ -545,7 +545,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call) ...@@ -545,7 +545,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
size = sizeof(__be32) * (1 + 4 + 1); size = sizeof(__be32) * (1 + 4 + 1);
break; break;
default: default:
return -EBADMSG; return afs_protocol_error(call, -EBADMSG);
} }
if (call->count > 1) if (call->count > 1)
...@@ -558,16 +558,16 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call) ...@@ -558,16 +558,16 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
switch (call->count2) { switch (call->count2) {
case YFS_ENDPOINT_IPV4: case YFS_ENDPOINT_IPV4:
if (ntohl(bp[0]) != sizeof(__be32) * 2) if (ntohl(bp[0]) != sizeof(__be32) * 2)
return -EBADMSG; return afs_protocol_error(call, -EBADMSG);
bp += 3; bp += 3;
break; break;
case YFS_ENDPOINT_IPV6: case YFS_ENDPOINT_IPV6:
if (ntohl(bp[0]) != sizeof(__be32) * 5) if (ntohl(bp[0]) != sizeof(__be32) * 5)
return -EBADMSG; return afs_protocol_error(call, -EBADMSG);
bp += 6; bp += 6;
break; break;
default: default:
return -EBADMSG; return afs_protocol_error(call, -EBADMSG);
} }
/* Got either the type of the next entry or the count of /* Got either the type of the next entry or the count of
...@@ -576,7 +576,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call) ...@@ -576,7 +576,7 @@ static int afs_deliver_yfsvl_get_endpoints(struct afs_call *call)
call->offset = 0; call->offset = 0;
call->count--; call->count--;
if (call->count > 0) { if (call->count > 0) {
call->count2 = htonl(*bp++); call->count2 = ntohl(*bp++);
goto again; goto again;
} }
......
...@@ -42,10 +42,11 @@ static int afs_fill_page(struct afs_vnode *vnode, struct key *key, ...@@ -42,10 +42,11 @@ static int afs_fill_page(struct afs_vnode *vnode, struct key *key,
if (!req) if (!req)
return -ENOMEM; return -ENOMEM;
atomic_set(&req->usage, 1); refcount_set(&req->usage, 1);
req->pos = pos; req->pos = pos;
req->len = len; req->len = len;
req->nr_pages = 1; req->nr_pages = 1;
req->pages = req->array;
req->pages[0] = page; req->pages[0] = page;
get_page(page); get_page(page);
...@@ -124,7 +125,12 @@ int afs_write_begin(struct file *file, struct address_space *mapping, ...@@ -124,7 +125,12 @@ int afs_write_begin(struct file *file, struct address_space *mapping,
page->index, priv); page->index, priv);
goto flush_conflicting_write; 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; goto flush_conflicting_write;
if (from < f) if (from < f)
f = from; f = from;
...@@ -355,6 +361,12 @@ static int afs_store_data(struct address_space *mapping, ...@@ -355,6 +361,12 @@ static int afs_store_data(struct address_space *mapping,
} }
switch (ret) { 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 -EACCES:
case -EPERM: case -EPERM:
case -ENOKEY: case -ENOKEY:
...@@ -412,7 +424,8 @@ static int afs_write_back_from_locked_page(struct address_space *mapping, ...@@ -412,7 +424,8 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
trace_afs_page_dirty(vnode, tracepoint_string("WARN"), trace_afs_page_dirty(vnode, tracepoint_string("WARN"),
primary_page->index, priv); 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; goto no_more;
start++; start++;
...@@ -433,9 +446,10 @@ static int afs_write_back_from_locked_page(struct address_space *mapping, ...@@ -433,9 +446,10 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
} }
for (loop = 0; loop < n; loop++) { for (loop = 0; loop < n; loop++) {
if (to != PAGE_SIZE)
break;
page = pages[loop]; page = pages[loop];
if (to != PAGE_SIZE &&
!test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags))
break;
if (page->index > final_page) if (page->index > final_page)
break; break;
if (!trylock_page(page)) if (!trylock_page(page))
...@@ -448,7 +462,8 @@ static int afs_write_back_from_locked_page(struct address_space *mapping, ...@@ -448,7 +462,8 @@ static int afs_write_back_from_locked_page(struct address_space *mapping,
priv = page_private(page); priv = page_private(page);
f = priv & AFS_PRIV_MAX; f = priv & AFS_PRIV_MAX;
t = priv >> AFS_PRIV_SHIFT; t = priv >> AFS_PRIV_SHIFT;
if (f != 0) { if (f != 0 &&
!test_bit(AFS_VNODE_NEW_CONTENT, &vnode->flags)) {
unlock_page(page); unlock_page(page);
break; break;
} }
...@@ -734,20 +749,6 @@ int afs_fsync(struct file *file, loff_t start, loff_t end, int datasync) ...@@ -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); 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 * 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 * - if it returns an error, the caller will deliver a bus error signal
......
/* 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, ...@@ -1667,7 +1667,7 @@ typedef int (*filldir_t)(struct dir_context *, const char *, int, loff_t, u64,
unsigned); unsigned);
struct dir_context { struct dir_context {
const filldir_t actor; filldir_t actor;
loff_t pos; loff_t pos;
}; };
......
...@@ -49,6 +49,7 @@ enum afs_fs_operation { ...@@ -49,6 +49,7 @@ enum afs_fs_operation {
afs_FS_ExtendLock = 157, /* AFS Extend a file lock */ afs_FS_ExtendLock = 157, /* AFS Extend a file lock */
afs_FS_ReleaseLock = 158, /* AFS Release a file lock */ afs_FS_ReleaseLock = 158, /* AFS Release a file lock */
afs_FS_Lookup = 161, /* AFS lookup file in directory */ 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_FetchData64 = 65537, /* AFS Fetch file data */
afs_FS_StoreData64 = 65538, /* AFS Store file data */ afs_FS_StoreData64 = 65538, /* AFS Store file data */
afs_FS_GiveUpAllCallBacks = 65539, /* AFS Give up all our callbacks on a server */ afs_FS_GiveUpAllCallBacks = 65539, /* AFS Give up all our callbacks on a server */
...@@ -62,6 +63,27 @@ enum afs_vl_operation { ...@@ -62,6 +63,27 @@ enum afs_vl_operation {
afs_VL_GetCapabilities = 65537, /* AFS Get VL server capabilities */ 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 */ #endif /* end __AFS_DECLARE_TRACE_ENUMS_ONCE_ONLY */
/* /*
...@@ -93,6 +115,7 @@ enum afs_vl_operation { ...@@ -93,6 +115,7 @@ enum afs_vl_operation {
EM(afs_FS_ExtendLock, "FS.ExtendLock") \ EM(afs_FS_ExtendLock, "FS.ExtendLock") \
EM(afs_FS_ReleaseLock, "FS.ReleaseLock") \ EM(afs_FS_ReleaseLock, "FS.ReleaseLock") \
EM(afs_FS_Lookup, "FS.Lookup") \ EM(afs_FS_Lookup, "FS.Lookup") \
EM(afs_FS_InlineBulkStatus, "FS.InlineBulkStatus") \
EM(afs_FS_FetchData64, "FS.FetchData64") \ EM(afs_FS_FetchData64, "FS.FetchData64") \
EM(afs_FS_StoreData64, "FS.StoreData64") \ EM(afs_FS_StoreData64, "FS.StoreData64") \
EM(afs_FS_GiveUpAllCallBacks, "FS.GiveUpAllCallBacks") \ EM(afs_FS_GiveUpAllCallBacks, "FS.GiveUpAllCallBacks") \
...@@ -104,6 +127,25 @@ enum afs_vl_operation { ...@@ -104,6 +127,25 @@ enum afs_vl_operation {
EM(afs_YFSVL_GetEndpoints, "YFSVL.GetEndpoints") \ EM(afs_YFSVL_GetEndpoints, "YFSVL.GetEndpoints") \
E_(afs_VL_GetCapabilities, "VL.GetCapabilities") 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. * Export enum symbols via userspace.
...@@ -116,6 +158,8 @@ enum afs_vl_operation { ...@@ -116,6 +158,8 @@ enum afs_vl_operation {
afs_call_traces; afs_call_traces;
afs_fs_operations; afs_fs_operations;
afs_vl_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 * Now redefine the EM() and E_() macros to map the enums to the strings that
...@@ -462,6 +506,75 @@ TRACE_EVENT(afs_call_state, ...@@ -462,6 +506,75 @@ TRACE_EVENT(afs_call_state,
__entry->ret, __entry->abort) __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 */ #endif /* _TRACE_AFS_H */
/* This part must be outside protection */ /* This part must be outside protection */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment